mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 06:11:37 +00:00
Merge mozilla-central to fx-team
This commit is contained in:
commit
8fb4a8baee
@ -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
|
||||
|
@ -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;
|
||||
|
@ -2848,7 +2848,6 @@ let E10SUINotification = {
|
||||
|
||||
if (!Services.appinfo.inSafeMode &&
|
||||
!Services.appinfo.accessibilityEnabled &&
|
||||
!Services.appinfo.keyboardMayHaveIME &&
|
||||
isHardwareAccelerated &&
|
||||
e10sPromptShownCount < 5) {
|
||||
Services.tm.mainThread.dispatch(() => {
|
||||
|
67
dom/media/MediaDeviceInfo.cpp
Normal file
67
dom/media/MediaDeviceInfo.cpp
Normal 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
|
60
dom/media/MediaDeviceInfo.h
Normal file
60
dom/media/MediaDeviceInfo.h
Normal 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
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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',
|
||||
|
@ -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);
|
||||
};
|
||||
|
174
dom/media/systemservices/MediaChild.cpp
Normal file
174
dom/media/systemservices/MediaChild.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
74
dom/media/systemservices/MediaChild.h
Normal file
74
dom/media/systemservices/MediaChild.h
Normal 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
|
456
dom/media/systemservices/MediaParent.cpp
Normal file
456
dom/media/systemservices/MediaParent.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
46
dom/media/systemservices/MediaParent.h
Normal file
46
dom/media/systemservices/MediaParent.h
Normal 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
|
13
dom/media/systemservices/MediaUtils.cpp
Normal file
13
dom/media/systemservices/MediaUtils.cpp
Normal 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 {
|
||||
|
||||
}
|
||||
}
|
197
dom/media/systemservices/MediaUtils.h
Normal file
197
dom/media/systemservices/MediaUtils.h
Normal 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
|
35
dom/media/systemservices/PMedia.ipdl
Normal file
35
dom/media/systemservices/PMedia.ipdl
Normal 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
|
@ -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'
|
||||
|
@ -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!
|
||||
|
22
dom/webidl/MediaDeviceInfo.webidl
Normal file
22
dom/webidl/MediaDeviceInfo.webidl
Normal 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;
|
||||
};
|
@ -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);
|
||||
};
|
||||
|
@ -262,6 +262,7 @@ WEBIDL_FILES = [
|
||||
'ListBoxObject.webidl',
|
||||
'LocalMediaStream.webidl',
|
||||
'Location.webidl',
|
||||
'MediaDeviceInfo.webidl',
|
||||
'MediaDevices.webidl',
|
||||
'MediaElementAudioSourceNode.webidl',
|
||||
'MediaError.webidl',
|
||||
|
@ -40,7 +40,6 @@ protected:
|
||||
|
||||
nsCOMPtr<nsIURI> mUrl;
|
||||
nsCOMPtr<nsIURI> mOriginalURI;
|
||||
int64_t mContentLength;
|
||||
nsCOMPtr<nsILoadGroup> mLoadGroup;
|
||||
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -43,7 +43,6 @@ public:
|
||||
protected:
|
||||
nsCOMPtr<nsIURI> mUrl;
|
||||
nsCOMPtr<nsIURI> mOriginalURI;
|
||||
int64_t mContentLength;
|
||||
nsCOMPtr<nsILoadGroup> mLoadGroup;
|
||||
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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; }
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
|
@ -46,9 +46,9 @@ associated with the frame, painting the frame, and handling events that
|
||||
are passed in from the widget hierarchy. The nsIHTMLReflow interface
|
||||
inherits from the nsIReflow interface and adds methods related to word
|
||||
breaking and whitespace querying. 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. 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.
|
||||
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.
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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");
|
||||
|
@ -1014,6 +1014,7 @@ nsColumnSetFrame::Reflow(nsPresContext* aPresContext,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
// Don't support interruption in columns
|
||||
nsPresContext::InterruptPreventer noInterrupts(aPresContext);
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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?
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -141,6 +141,7 @@ nsPlaceholderFrame::Reflow(nsPresContext* aPresContext,
|
||||
}
|
||||
#endif
|
||||
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsPlaceholderFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
|
||||
aDesiredSize.ClearSize();
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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");
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -308,6 +308,7 @@ nsMathMLmpaddedFrame::Reflow(nsPresContext* aPresContext,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
mPresentationData.flags &= ~NS_MATHML_ERROR;
|
||||
ProcessAttributes();
|
||||
|
||||
///////////////
|
||||
|
@ -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);
|
||||
|
||||
|
@ -98,6 +98,8 @@ nsMathMLmspaceFrame::Reflow(nsPresContext* aPresContext,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
mPresentationData.flags &= ~NS_MATHML_ERROR;
|
||||
ProcessAttributes(aPresContext);
|
||||
|
||||
mBoundingMetrics = nsBoundingMetrics();
|
||||
|
BIN
layout/reftests/fonts/markA.woff
Normal file
BIN
layout/reftests/fonts/markA.woff
Normal file
Binary file not shown.
BIN
layout/reftests/fonts/markB.woff
Normal file
BIN
layout/reftests/fonts/markB.woff
Normal file
Binary file not shown.
BIN
layout/reftests/fonts/markC.woff
Normal file
BIN
layout/reftests/fonts/markC.woff
Normal file
Binary file not shown.
BIN
layout/reftests/fonts/markD.woff
Normal file
BIN
layout/reftests/fonts/markD.woff
Normal file
Binary file not shown.
@ -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]
|
||||
|
366
layout/style/test/test_unicode_range_loading.html
Normal file
366
layout/style/test/test_unicode_range_loading.html
Normal 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>
|
@ -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));
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user