mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 11:25:00 +00:00
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
commit
22ae53697c
@ -12,6 +12,7 @@
|
|||||||
#include "nsAccUtils.h"
|
#include "nsAccUtils.h"
|
||||||
#include "nsIAccessibleRelation.h"
|
#include "nsIAccessibleRelation.h"
|
||||||
#include "nsIAccessibleTable.h"
|
#include "nsIAccessibleTable.h"
|
||||||
|
#include "ProxyAccessible.h"
|
||||||
#include "RootAccessible.h"
|
#include "RootAccessible.h"
|
||||||
#include "nsIAccessibleValue.h"
|
#include "nsIAccessibleValue.h"
|
||||||
#include "nsMai.h"
|
#include "nsMai.h"
|
||||||
@ -20,6 +21,7 @@
|
|||||||
#include "nsAutoPtr.h"
|
#include "nsAutoPtr.h"
|
||||||
#include "prprf.h"
|
#include "prprf.h"
|
||||||
#include "nsStateMap.h"
|
#include "nsStateMap.h"
|
||||||
|
#include "mozilla/a11y/Platform.h"
|
||||||
#include "Relation.h"
|
#include "Relation.h"
|
||||||
#include "RootAccessible.h"
|
#include "RootAccessible.h"
|
||||||
#include "States.h"
|
#include "States.h"
|
||||||
@ -133,9 +135,13 @@ struct MaiAtkObject
|
|||||||
* The AccessibleWrap whose properties and features are exported
|
* The AccessibleWrap whose properties and features are exported
|
||||||
* via this object instance.
|
* via this object instance.
|
||||||
*/
|
*/
|
||||||
AccessibleWrap* accWrap;
|
uintptr_t accWrap;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This is or'd with the pointer in MaiAtkObject::accWrap if the wrap-ee is a
|
||||||
|
// proxy.
|
||||||
|
static const uintptr_t IS_PROXY = 1;
|
||||||
|
|
||||||
struct MaiAtkObjectClass
|
struct MaiAtkObjectClass
|
||||||
{
|
{
|
||||||
AtkObjectClass parent_class;
|
AtkObjectClass parent_class;
|
||||||
@ -248,7 +254,7 @@ AccessibleWrap::ShutdownAtkObject()
|
|||||||
{
|
{
|
||||||
if (mAtkObject) {
|
if (mAtkObject) {
|
||||||
if (IS_MAI_OBJECT(mAtkObject)) {
|
if (IS_MAI_OBJECT(mAtkObject)) {
|
||||||
MAI_ATK_OBJECT(mAtkObject)->accWrap = nullptr;
|
MAI_ATK_OBJECT(mAtkObject)->accWrap = 0;
|
||||||
}
|
}
|
||||||
SetMaiHyperlink(nullptr);
|
SetMaiHyperlink(nullptr);
|
||||||
g_object_unref(mAtkObject);
|
g_object_unref(mAtkObject);
|
||||||
@ -582,8 +588,7 @@ initializeCB(AtkObject *aAtkObj, gpointer aData)
|
|||||||
ATK_OBJECT_CLASS(parent_class)->initialize(aAtkObj, aData);
|
ATK_OBJECT_CLASS(parent_class)->initialize(aAtkObj, aData);
|
||||||
|
|
||||||
/* initialize object */
|
/* initialize object */
|
||||||
MAI_ATK_OBJECT(aAtkObj)->accWrap =
|
MAI_ATK_OBJECT(aAtkObj)->accWrap = reinterpret_cast<uintptr_t>(aData);
|
||||||
static_cast<AccessibleWrap*>(aData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -591,7 +596,7 @@ finalizeCB(GObject *aObj)
|
|||||||
{
|
{
|
||||||
if (!IS_MAI_OBJECT(aObj))
|
if (!IS_MAI_OBJECT(aObj))
|
||||||
return;
|
return;
|
||||||
NS_ASSERTION(MAI_ATK_OBJECT(aObj)->accWrap == nullptr, "AccWrap NOT null");
|
NS_ASSERTION(MAI_ATK_OBJECT(aObj)->accWrap == 0, "AccWrap NOT null");
|
||||||
|
|
||||||
// call parent finalize function
|
// call parent finalize function
|
||||||
// finalize of GObjectClass will unref the accessible parent if has
|
// finalize of GObjectClass will unref the accessible parent if has
|
||||||
@ -663,25 +668,33 @@ getDescriptionCB(AtkObject *aAtkObj)
|
|||||||
AtkRole
|
AtkRole
|
||||||
getRoleCB(AtkObject *aAtkObj)
|
getRoleCB(AtkObject *aAtkObj)
|
||||||
{
|
{
|
||||||
AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
|
|
||||||
if (!accWrap)
|
|
||||||
return ATK_ROLE_INVALID;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(accWrap),
|
|
||||||
"Does not support nsIAccessibleText when it should");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (aAtkObj->role != ATK_ROLE_INVALID)
|
if (aAtkObj->role != ATK_ROLE_INVALID)
|
||||||
return aAtkObj->role;
|
return aAtkObj->role;
|
||||||
|
|
||||||
|
AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
|
||||||
|
a11y::role role;
|
||||||
|
if (!accWrap) {
|
||||||
|
ProxyAccessible* proxy = GetProxy(aAtkObj);
|
||||||
|
if (!proxy)
|
||||||
|
return ATK_ROLE_INVALID;
|
||||||
|
|
||||||
|
role = proxy->Role();
|
||||||
|
} else {
|
||||||
|
#ifdef DEBUG
|
||||||
|
NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(accWrap),
|
||||||
|
"Does not support nsIAccessibleText when it should");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
role = accWrap->Role();
|
||||||
|
}
|
||||||
|
|
||||||
#define ROLE(geckoRole, stringRole, atkRole, macRole, \
|
#define ROLE(geckoRole, stringRole, atkRole, macRole, \
|
||||||
msaaRole, ia2Role, nameRule) \
|
msaaRole, ia2Role, nameRule) \
|
||||||
case roles::geckoRole: \
|
case roles::geckoRole: \
|
||||||
aAtkObj->role = atkRole; \
|
aAtkObj->role = atkRole; \
|
||||||
break;
|
break;
|
||||||
|
|
||||||
switch (accWrap->Role()) {
|
switch (role) {
|
||||||
#include "RoleMap.h"
|
#include "RoleMap.h"
|
||||||
default:
|
default:
|
||||||
MOZ_CRASH("Unknown role.");
|
MOZ_CRASH("Unknown role.");
|
||||||
@ -946,7 +959,13 @@ AccessibleWrap*
|
|||||||
GetAccessibleWrap(AtkObject* aAtkObj)
|
GetAccessibleWrap(AtkObject* aAtkObj)
|
||||||
{
|
{
|
||||||
NS_ENSURE_TRUE(IS_MAI_OBJECT(aAtkObj), nullptr);
|
NS_ENSURE_TRUE(IS_MAI_OBJECT(aAtkObj), nullptr);
|
||||||
AccessibleWrap* accWrap = MAI_ATK_OBJECT(aAtkObj)->accWrap;
|
|
||||||
|
// Make sure its native is an AccessibleWrap not a proxy.
|
||||||
|
if (MAI_ATK_OBJECT(aAtkObj)->accWrap & IS_PROXY)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
AccessibleWrap* accWrap =
|
||||||
|
reinterpret_cast<AccessibleWrap*>(MAI_ATK_OBJECT(aAtkObj)->accWrap);
|
||||||
|
|
||||||
// Check if the accessible was deconstructed.
|
// Check if the accessible was deconstructed.
|
||||||
if (!accWrap)
|
if (!accWrap)
|
||||||
@ -961,6 +980,49 @@ GetAccessibleWrap(AtkObject* aAtkObj)
|
|||||||
return accWrap;
|
return accWrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProxyAccessible*
|
||||||
|
GetProxy(AtkObject* aObj)
|
||||||
|
{
|
||||||
|
if (!aObj || !(MAI_ATK_OBJECT(aObj)->accWrap & IS_PROXY))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return reinterpret_cast<ProxyAccessible*>(MAI_ATK_OBJECT(aObj)->accWrap
|
||||||
|
& ~IS_PROXY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t
|
||||||
|
GetInterfacesForProxy(ProxyAccessible* aProxy)
|
||||||
|
{
|
||||||
|
return MAI_INTERFACE_COMPONENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
a11y::ProxyCreated(ProxyAccessible* aProxy)
|
||||||
|
{
|
||||||
|
GType type = GetMaiAtkType(GetInterfacesForProxy(aProxy));
|
||||||
|
NS_ASSERTION(type, "why don't we have a type!");
|
||||||
|
|
||||||
|
AtkObject* obj =
|
||||||
|
reinterpret_cast<AtkObject *>
|
||||||
|
(g_object_new(type, nullptr));
|
||||||
|
if (!obj)
|
||||||
|
return;
|
||||||
|
|
||||||
|
atk_object_initialize(obj, aProxy);
|
||||||
|
obj->role = ATK_ROLE_INVALID;
|
||||||
|
obj->layer = ATK_LAYER_INVALID;
|
||||||
|
aProxy->SetWrapper(reinterpret_cast<uintptr_t>(obj) | IS_PROXY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
a11y::ProxyDestroyed(ProxyAccessible* aProxy)
|
||||||
|
{
|
||||||
|
auto obj = reinterpret_cast<MaiAtkObject*>(aProxy->GetWrapper() & ~IS_PROXY);
|
||||||
|
obj->accWrap = 0;
|
||||||
|
g_object_unref(obj);
|
||||||
|
aProxy->SetWrapper(0);
|
||||||
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
AccessibleWrap::HandleAccEvent(AccEvent* aEvent)
|
AccessibleWrap::HandleAccEvent(AccEvent* aEvent)
|
||||||
{
|
{
|
||||||
|
@ -97,7 +97,7 @@ private:
|
|||||||
|
|
||||||
static EAvailableAtkSignals gAvailableAtkSignals;
|
static EAvailableAtkSignals gAvailableAtkSignals;
|
||||||
|
|
||||||
uint16_t CreateMaiInterfaces(void);
|
uint16_t CreateMaiInterfaces();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace a11y
|
} // namespace a11y
|
||||||
|
@ -35,6 +35,7 @@ LOCAL_INCLUDES += [
|
|||||||
'/accessible/base',
|
'/accessible/base',
|
||||||
'/accessible/generic',
|
'/accessible/generic',
|
||||||
'/accessible/html',
|
'/accessible/html',
|
||||||
|
'/accessible/ipc',
|
||||||
'/accessible/xpcom',
|
'/accessible/xpcom',
|
||||||
'/accessible/xul',
|
'/accessible/xul',
|
||||||
'/other-licenses/atk-1.0',
|
'/other-licenses/atk-1.0',
|
||||||
|
@ -13,6 +13,12 @@
|
|||||||
|
|
||||||
#include "AccessibleWrap.h"
|
#include "AccessibleWrap.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace a11y {
|
||||||
|
class ProxyAccessible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#define MAI_TYPE_ATK_OBJECT (mai_atk_object_get_type ())
|
#define MAI_TYPE_ATK_OBJECT (mai_atk_object_get_type ())
|
||||||
#define MAI_ATK_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
|
#define MAI_ATK_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
|
||||||
MAI_TYPE_ATK_OBJECT, MaiAtkObject))
|
MAI_TYPE_ATK_OBJECT, MaiAtkObject))
|
||||||
@ -29,6 +35,7 @@
|
|||||||
GType mai_atk_object_get_type(void);
|
GType mai_atk_object_get_type(void);
|
||||||
GType mai_util_get_type();
|
GType mai_util_get_type();
|
||||||
mozilla::a11y::AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj);
|
mozilla::a11y::AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj);
|
||||||
|
mozilla::a11y::ProxyAccessible* GetProxy(AtkObject* aAtkObj);
|
||||||
|
|
||||||
extern int atkMajorVersion, atkMinorVersion;
|
extern int atkMajorVersion, atkMinorVersion;
|
||||||
|
|
||||||
|
@ -232,6 +232,8 @@ public:
|
|||||||
bool IsShow() const { return mEventType == nsIAccessibleEvent::EVENT_SHOW; }
|
bool IsShow() const { return mEventType == nsIAccessibleEvent::EVENT_SHOW; }
|
||||||
bool IsHide() const { return mEventType == nsIAccessibleEvent::EVENT_HIDE; }
|
bool IsHide() const { return mEventType == nsIAccessibleEvent::EVENT_HIDE; }
|
||||||
|
|
||||||
|
Accessible* Parent() const { return mParent; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
nsCOMPtr<nsINode> mNode;
|
nsCOMPtr<nsINode> mNode;
|
||||||
nsRefPtr<Accessible> mParent;
|
nsRefPtr<Accessible> mParent;
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include "ApplicationAccessible.h"
|
#include "ApplicationAccessible.h"
|
||||||
#include "ARIAMap.h"
|
#include "ARIAMap.h"
|
||||||
#include "DocAccessible-inl.h"
|
#include "DocAccessible-inl.h"
|
||||||
|
#include "DocAccessibleChild.h"
|
||||||
#include "nsAccessibilityService.h"
|
#include "nsAccessibilityService.h"
|
||||||
#include "RootAccessibleWrap.h"
|
#include "RootAccessibleWrap.h"
|
||||||
|
|
||||||
@ -27,6 +28,8 @@
|
|||||||
#include "nsServiceManagerUtils.h"
|
#include "nsServiceManagerUtils.h"
|
||||||
#include "nsIWebProgress.h"
|
#include "nsIWebProgress.h"
|
||||||
#include "nsCoreUtils.h"
|
#include "nsCoreUtils.h"
|
||||||
|
#include "nsXULAppAPI.h"
|
||||||
|
#include "mozilla/dom/ContentChild.h"
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
using namespace mozilla::a11y;
|
using namespace mozilla::a11y;
|
||||||
@ -418,6 +421,12 @@ DocManager::CreateDocOrRootAccessible(nsIDocument* aDocument)
|
|||||||
docAcc->FireDelayedEvent(nsIAccessibleEvent::EVENT_REORDER,
|
docAcc->FireDelayedEvent(nsIAccessibleEvent::EVENT_REORDER,
|
||||||
ApplicationAcc());
|
ApplicationAcc());
|
||||||
|
|
||||||
|
if (XRE_GetProcessType() != GeckoProcessType_Default) {
|
||||||
|
DocAccessibleChild* ipcDoc = new DocAccessibleChild(docAcc);
|
||||||
|
docAcc->SetIPCDoc(ipcDoc);
|
||||||
|
auto contentChild = dom::ContentChild::GetSingleton();
|
||||||
|
contentChild->SendPDocAccessibleConstructor(ipcDoc, nullptr, 0);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
parentDocAcc->BindChildDocument(docAcc);
|
parentDocAcc->BindChildDocument(docAcc);
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ namespace a11y {
|
|||||||
|
|
||||||
class Accessible;
|
class Accessible;
|
||||||
class DocAccessible;
|
class DocAccessible;
|
||||||
|
class DocAccessibleParent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manage the document accessible life cycle.
|
* Manage the document accessible life cycle.
|
||||||
@ -65,6 +66,25 @@ public:
|
|||||||
RemoveListeners(aDocument);
|
RemoveListeners(aDocument);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Notification that a top level document in a content process has gone away.
|
||||||
|
*/
|
||||||
|
void RemoteDocShutdown(DocAccessibleParent* aDoc)
|
||||||
|
{
|
||||||
|
DebugOnly<bool> result = mRemoteDocuments.RemoveElement(aDoc);
|
||||||
|
MOZ_ASSERT(result, "Why didn't we find the document!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Notify of a new top level document in a content process.
|
||||||
|
*/
|
||||||
|
void RemoteDocAdded(DocAccessibleParent* aDoc)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(!mRemoteDocuments.Contains(aDoc),
|
||||||
|
"How did we already have the doc!");
|
||||||
|
mRemoteDocuments.AppendElement(aDoc);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
bool IsProcessingRefreshDriverNotification() const;
|
bool IsProcessingRefreshDriverNotification() const;
|
||||||
#endif
|
#endif
|
||||||
@ -144,6 +164,11 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
DocAccessibleHashtable mDocAccessibleCache;
|
DocAccessibleHashtable mDocAccessibleCache;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The list of remote top level documents.
|
||||||
|
*/
|
||||||
|
nsTArray<DocAccessibleParent*> mRemoteDocuments;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include "Accessible-inl.h"
|
#include "Accessible-inl.h"
|
||||||
#include "nsEventShell.h"
|
#include "nsEventShell.h"
|
||||||
#include "DocAccessible.h"
|
#include "DocAccessible.h"
|
||||||
|
#include "DocAccessibleChild.h"
|
||||||
#include "nsAccessibilityService.h"
|
#include "nsAccessibilityService.h"
|
||||||
#include "nsTextEquivUtils.h"
|
#include "nsTextEquivUtils.h"
|
||||||
#ifdef A11Y_LOG
|
#ifdef A11Y_LOG
|
||||||
@ -555,5 +556,15 @@ EventQueue::ProcessEventQueue()
|
|||||||
|
|
||||||
if (!mDocument)
|
if (!mDocument)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (XRE_GetProcessType() != GeckoProcessType_Default) {
|
||||||
|
DocAccessibleChild* ipcDoc = mDocument->IPCDoc();
|
||||||
|
if (event->mEventType == nsIAccessibleEvent::EVENT_SHOW)
|
||||||
|
ipcDoc->ShowEvent(downcast_accEvent(event));
|
||||||
|
else if (event->mEventType == nsIAccessibleEvent::EVENT_HIDE)
|
||||||
|
ipcDoc->SendHideEvent(reinterpret_cast<uintptr_t>(event->GetAccessible()));
|
||||||
|
else
|
||||||
|
ipcDoc->SendEvent(event->GetEventType());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,11 @@
|
|||||||
#include "NotificationController.h"
|
#include "NotificationController.h"
|
||||||
|
|
||||||
#include "DocAccessible-inl.h"
|
#include "DocAccessible-inl.h"
|
||||||
|
#include "DocAccessibleChild.h"
|
||||||
#include "TextLeafAccessible.h"
|
#include "TextLeafAccessible.h"
|
||||||
#include "TextUpdater.h"
|
#include "TextUpdater.h"
|
||||||
|
|
||||||
|
#include "mozilla/dom/ContentChild.h"
|
||||||
#include "mozilla/dom/Element.h"
|
#include "mozilla/dom/Element.h"
|
||||||
#include "mozilla/Telemetry.h"
|
#include "mozilla/Telemetry.h"
|
||||||
|
|
||||||
@ -217,8 +219,19 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
|
|||||||
if (ownerContent) {
|
if (ownerContent) {
|
||||||
Accessible* outerDocAcc = mDocument->GetAccessible(ownerContent);
|
Accessible* outerDocAcc = mDocument->GetAccessible(ownerContent);
|
||||||
if (outerDocAcc && outerDocAcc->AppendChild(childDoc)) {
|
if (outerDocAcc && outerDocAcc->AppendChild(childDoc)) {
|
||||||
if (mDocument->AppendChildDocument(childDoc))
|
if (mDocument->AppendChildDocument(childDoc)) {
|
||||||
|
if (XRE_GetProcessType() != GeckoProcessType_Default) {
|
||||||
|
DocAccessibleChild* ipcDoc = new DocAccessibleChild(childDoc);
|
||||||
|
childDoc->SetIPCDoc(ipcDoc);
|
||||||
|
auto contentChild = dom::ContentChild::GetSingleton();
|
||||||
|
DocAccessibleChild* parentIPCDoc = mDocument->IPCDoc();
|
||||||
|
uint64_t id = reinterpret_cast<uintptr_t>(outerDocAcc->UniqueID());
|
||||||
|
contentChild->SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc,
|
||||||
|
id);
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
outerDocAcc->RemoveChild(childDoc);
|
outerDocAcc->RemoveChild(childDoc);
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace a11y {
|
namespace a11y {
|
||||||
|
|
||||||
|
class ProxyAccessible;
|
||||||
|
|
||||||
enum EPlatformDisabledState {
|
enum EPlatformDisabledState {
|
||||||
ePlatformIsForceEnabled = -1,
|
ePlatformIsForceEnabled = -1,
|
||||||
ePlatformIsEnabled = 0,
|
ePlatformIsEnabled = 0,
|
||||||
@ -47,6 +49,17 @@ void PlatformInit();
|
|||||||
*/
|
*/
|
||||||
void PlatformShutdown();
|
void PlatformShutdown();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called when a new ProxyAccessible is created, so the platform may setup a
|
||||||
|
* wrapper for it, or take other action.
|
||||||
|
*/
|
||||||
|
void ProxyCreated(ProxyAccessible*);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called just before a ProxyAccessible is destroyed so its wrapper can be
|
||||||
|
* disposed of and other action taken.
|
||||||
|
*/
|
||||||
|
void ProxyDestroyed(ProxyAccessible*);
|
||||||
} // namespace a11y
|
} // namespace a11y
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
||||||
|
@ -783,7 +783,9 @@ enum Role {
|
|||||||
/**
|
/**
|
||||||
* Represent a keyboard or keypad key (ARIA role "key").
|
* Represent a keyboard or keypad key (ARIA role "key").
|
||||||
*/
|
*/
|
||||||
KEY = 129
|
KEY = 129,
|
||||||
|
|
||||||
|
LAST_ROLE = KEY
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace role
|
} // namespace role
|
||||||
|
@ -60,6 +60,7 @@ if CONFIG['A11Y_LOG']:
|
|||||||
LOCAL_INCLUDES += [
|
LOCAL_INCLUDES += [
|
||||||
'/accessible/generic',
|
'/accessible/generic',
|
||||||
'/accessible/html',
|
'/accessible/html',
|
||||||
|
'/accessible/ipc',
|
||||||
'/accessible/xpcom',
|
'/accessible/xpcom',
|
||||||
'/accessible/xul',
|
'/accessible/xul',
|
||||||
'/dom/xbl',
|
'/dom/xbl',
|
||||||
@ -93,3 +94,5 @@ FINAL_LIBRARY = 'xul'
|
|||||||
|
|
||||||
if CONFIG['MOZ_ENABLE_GTK']:
|
if CONFIG['MOZ_ENABLE_GTK']:
|
||||||
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
|
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
|
||||||
|
|
||||||
|
include('/ipc/chromium/chromium-config.mozbuild')
|
||||||
|
@ -240,7 +240,7 @@ Accessible::Description(nsString& aDescription)
|
|||||||
if (!aDescription.IsEmpty()) {
|
if (!aDescription.IsEmpty()) {
|
||||||
aDescription.CompressWhitespace();
|
aDescription.CompressWhitespace();
|
||||||
nsAutoString name;
|
nsAutoString name;
|
||||||
ENameValueFlag nameFlag = Name(name);
|
Name(name);
|
||||||
// Don't expose a description if it is the same as the name.
|
// Don't expose a description if it is the same as the name.
|
||||||
if (aDescription.Equals(name))
|
if (aDescription.Equals(name))
|
||||||
aDescription.Truncate();
|
aDescription.Truncate();
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "Accessible-inl.h"
|
#include "Accessible-inl.h"
|
||||||
#include "AccIterator.h"
|
#include "AccIterator.h"
|
||||||
#include "DocAccessible-inl.h"
|
#include "DocAccessible-inl.h"
|
||||||
|
#include "DocAccessibleChild.h"
|
||||||
#include "HTMLImageMapAccessible.h"
|
#include "HTMLImageMapAccessible.h"
|
||||||
#include "nsAccCache.h"
|
#include "nsAccCache.h"
|
||||||
#include "nsAccessiblePivot.h"
|
#include "nsAccessiblePivot.h"
|
||||||
@ -83,7 +84,7 @@ DocAccessible::
|
|||||||
mScrollPositionChangedTicks(0),
|
mScrollPositionChangedTicks(0),
|
||||||
mLoadState(eTreeConstructionPending), mDocFlags(0), mLoadEventType(0),
|
mLoadState(eTreeConstructionPending), mDocFlags(0), mLoadEventType(0),
|
||||||
mVirtualCursor(nullptr),
|
mVirtualCursor(nullptr),
|
||||||
mPresShell(aPresShell)
|
mPresShell(aPresShell), mIPCDoc(nullptr)
|
||||||
{
|
{
|
||||||
mGenericTypes |= eDocument;
|
mGenericTypes |= eDocument;
|
||||||
mStateFlags |= eNotNodeMapEntry;
|
mStateFlags |= eNotNodeMapEntry;
|
||||||
@ -473,6 +474,12 @@ DocAccessible::Shutdown()
|
|||||||
|
|
||||||
mChildDocuments.Clear();
|
mChildDocuments.Clear();
|
||||||
|
|
||||||
|
// XXX thinking about ordering?
|
||||||
|
if (XRE_GetProcessType() != GeckoProcessType_Default) {
|
||||||
|
DocAccessibleChild::Send__delete__(mIPCDoc);
|
||||||
|
MOZ_ASSERT(!mIPCDoc);
|
||||||
|
}
|
||||||
|
|
||||||
if (mVirtualCursor) {
|
if (mVirtualCursor) {
|
||||||
mVirtualCursor->RemoveObserver(this);
|
mVirtualCursor->RemoveObserver(this);
|
||||||
mVirtualCursor = nullptr;
|
mVirtualCursor = nullptr;
|
||||||
@ -1436,6 +1443,13 @@ DocAccessible::DoInitialUpdate()
|
|||||||
nsRefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(Parent());
|
nsRefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(Parent());
|
||||||
ParentDocument()->FireDelayedEvent(reorderEvent);
|
ParentDocument()->FireDelayedEvent(reorderEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t childCount = ChildCount();
|
||||||
|
for (uint32_t i = 0; i < childCount; i++) {
|
||||||
|
Accessible* child = GetChildAt(i);
|
||||||
|
nsRefPtr<AccShowEvent> event = new AccShowEvent(child, child->GetContent());
|
||||||
|
FireDelayedEvent(event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -33,6 +33,7 @@ namespace a11y {
|
|||||||
|
|
||||||
class DocManager;
|
class DocManager;
|
||||||
class NotificationController;
|
class NotificationController;
|
||||||
|
class DocAccessibleChild;
|
||||||
class RelatedAccIterator;
|
class RelatedAccIterator;
|
||||||
template<class Class, class Arg>
|
template<class Class, class Arg>
|
||||||
class TNotification;
|
class TNotification;
|
||||||
@ -519,6 +520,20 @@ protected:
|
|||||||
*/
|
*/
|
||||||
bool IsLoadEventTarget() const;
|
bool IsLoadEventTarget() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this document is in a content process return the object responsible for
|
||||||
|
* communicating with the main process for it.
|
||||||
|
*/
|
||||||
|
DocAccessibleChild* IPCDoc() const { return mIPCDoc; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the object responsible for communicating with the main process on
|
||||||
|
* behalf of this document.
|
||||||
|
*/
|
||||||
|
void SetIPCDoc(DocAccessibleChild* aIPCDoc) { mIPCDoc = aIPCDoc; }
|
||||||
|
|
||||||
|
friend class DocAccessibleChild;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to fire scrolling end event after page scroll.
|
* Used to fire scrolling end event after page scroll.
|
||||||
*
|
*
|
||||||
@ -642,6 +657,9 @@ protected:
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
nsIPresShell* mPresShell;
|
nsIPresShell* mPresShell;
|
||||||
|
|
||||||
|
// Exclusively owned by IPDL so don't manually delete it!
|
||||||
|
DocAccessibleChild* mIPCDoc;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline DocAccessible*
|
inline DocAccessible*
|
||||||
|
@ -28,6 +28,7 @@ UNIFIED_SOURCES += [
|
|||||||
LOCAL_INCLUDES += [
|
LOCAL_INCLUDES += [
|
||||||
'/accessible/base',
|
'/accessible/base',
|
||||||
'/accessible/html',
|
'/accessible/html',
|
||||||
|
'/accessible/ipc',
|
||||||
'/accessible/xpcom',
|
'/accessible/xpcom',
|
||||||
'/accessible/xul',
|
'/accessible/xul',
|
||||||
'/content/base/src',
|
'/content/base/src',
|
||||||
@ -54,3 +55,5 @@ else:
|
|||||||
]
|
]
|
||||||
|
|
||||||
FINAL_LIBRARY = 'xul'
|
FINAL_LIBRARY = 'xul'
|
||||||
|
|
||||||
|
include('/ipc/chromium/chromium-config.mozbuild')
|
||||||
|
40
accessible/ipc/DocAccessibleChild.cpp
Normal file
40
accessible/ipc/DocAccessibleChild.cpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* 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 "DocAccessibleChild.h"
|
||||||
|
|
||||||
|
#include "Accessible-inl.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace a11y {
|
||||||
|
|
||||||
|
void
|
||||||
|
SerializeTree(Accessible* aRoot, nsTArray<AccessibleData>& aTree)
|
||||||
|
{
|
||||||
|
uint64_t id = reinterpret_cast<uint64_t>(aRoot->UniqueID());
|
||||||
|
uint32_t role = aRoot->Role();
|
||||||
|
uint32_t childCount = aRoot->ChildCount();
|
||||||
|
|
||||||
|
nsString name;
|
||||||
|
aRoot->Name(name);
|
||||||
|
aTree.AppendElement(AccessibleData(id, role, childCount, name));
|
||||||
|
for (uint32_t i = 0; i < childCount; i++)
|
||||||
|
SerializeTree(aRoot->GetChildAt(i), aTree);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DocAccessibleChild::ShowEvent(AccShowEvent* aShowEvent)
|
||||||
|
{
|
||||||
|
Accessible* parent = aShowEvent->Parent();
|
||||||
|
uint64_t parentID = parent->IsDoc() ? 0 : reinterpret_cast<uint64_t>(parent->UniqueID());
|
||||||
|
uint32_t idxInParent = aShowEvent->GetAccessible()->IndexInParent();
|
||||||
|
nsTArray<AccessibleData> shownTree;
|
||||||
|
ShowEventData data(parentID, idxInParent, shownTree);
|
||||||
|
SerializeTree(aShowEvent->GetAccessible(), data.NewTree());
|
||||||
|
SendShowEvent(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
43
accessible/ipc/DocAccessibleChild.h
Normal file
43
accessible/ipc/DocAccessibleChild.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* 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_a11y_DocAccessibleChild_h
|
||||||
|
#define mozilla_a11y_DocAccessibleChild_h
|
||||||
|
|
||||||
|
#include "mozilla/a11y/DocAccessible.h"
|
||||||
|
#include "mozilla/a11y/PDocAccessibleChild.h"
|
||||||
|
#include "nsISupportsImpl.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace a11y {
|
||||||
|
class AccShowEvent;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These objects handle content side communication for an accessible document,
|
||||||
|
* and their lifetime is the same as the document they represent.
|
||||||
|
*/
|
||||||
|
class DocAccessibleChild : public PDocAccessibleChild
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DocAccessibleChild(DocAccessible* aDoc) :
|
||||||
|
mDoc(aDoc)
|
||||||
|
{ MOZ_COUNT_CTOR(DocAccessibleChild); }
|
||||||
|
~DocAccessibleChild()
|
||||||
|
{
|
||||||
|
mDoc->SetIPCDoc(nullptr);
|
||||||
|
MOZ_COUNT_DTOR(DocAccessibleChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShowEvent(AccShowEvent* aShowEvent);
|
||||||
|
|
||||||
|
private:
|
||||||
|
DocAccessible* mDoc;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
112
accessible/ipc/DocAccessibleParent.cpp
Normal file
112
accessible/ipc/DocAccessibleParent.cpp
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* 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 "DocAccessibleParent.h"
|
||||||
|
#include "nsAutoPtr.h"
|
||||||
|
#include "mozilla/a11y/Platform.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace a11y {
|
||||||
|
|
||||||
|
bool
|
||||||
|
DocAccessibleParent::RecvShowEvent(const ShowEventData& aData)
|
||||||
|
{
|
||||||
|
if (aData.NewTree().IsEmpty()) {
|
||||||
|
NS_ERROR("no children being added");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProxyAccessible* parent = nullptr;
|
||||||
|
if (aData.ID()) {
|
||||||
|
ProxyEntry* e = mAccessibles.GetEntry(aData.ID());
|
||||||
|
if (e)
|
||||||
|
parent = e->mProxy;
|
||||||
|
} else {
|
||||||
|
parent = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX This should really never happen, but sometimes we fail to fire the
|
||||||
|
// required show events.
|
||||||
|
if (!parent) {
|
||||||
|
NS_ERROR("adding child to unknown accessible");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t newChildIdx = aData.Idx();
|
||||||
|
if (newChildIdx > parent->ChildrenCount()) {
|
||||||
|
NS_ERROR("invalid index to add child at");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t consumed = AddSubtree(parent, aData.NewTree(), 0, newChildIdx);
|
||||||
|
MOZ_ASSERT(consumed == aData.NewTree().Length());
|
||||||
|
for (uint32_t i = 0; i < consumed; i++) {
|
||||||
|
uint64_t id = aData.NewTree()[i].ID();
|
||||||
|
MOZ_ASSERT(mAccessibles.GetEntry(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
DocAccessibleParent::AddSubtree(ProxyAccessible* aParent,
|
||||||
|
const nsTArray<a11y::AccessibleData>& aNewTree,
|
||||||
|
uint32_t aIdx, uint32_t aIdxInParent)
|
||||||
|
{
|
||||||
|
if (aNewTree.Length() <= aIdx) {
|
||||||
|
NS_ERROR("bad index in serialized tree!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AccessibleData& newChild = aNewTree[aIdx];
|
||||||
|
if (newChild.Role() > roles::LAST_ROLE) {
|
||||||
|
NS_ERROR("invalid role");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto role = static_cast<a11y::role>(newChild.Role());
|
||||||
|
ProxyAccessible* newProxy =
|
||||||
|
new ProxyAccessible(newChild.ID(), aParent, this, role, newChild.Name());
|
||||||
|
aParent->AddChildAt(aIdxInParent, newProxy);
|
||||||
|
mAccessibles.PutEntry(newChild.ID())->mProxy = newProxy;
|
||||||
|
ProxyCreated(newProxy);
|
||||||
|
|
||||||
|
uint32_t accessibles = 1;
|
||||||
|
uint32_t kids = newChild.ChildrenCount();
|
||||||
|
for (uint32_t i = 0; i < kids; i++) {
|
||||||
|
uint32_t consumed = AddSubtree(newProxy, aNewTree, aIdx + accessibles, i);
|
||||||
|
if (!consumed)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
accessibles += consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(newProxy->ChildrenCount() == kids);
|
||||||
|
|
||||||
|
return accessibles;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
DocAccessibleParent::RecvHideEvent(const uint64_t& aRootID)
|
||||||
|
{
|
||||||
|
ProxyEntry* rootEntry = mAccessibles.GetEntry(aRootID);
|
||||||
|
if (!rootEntry) {
|
||||||
|
NS_ERROR("invalid root being removed!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProxyAccessible* root = rootEntry->mProxy;
|
||||||
|
if (!root) {
|
||||||
|
NS_ERROR("invalid root being removed!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
root->Shutdown();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
141
accessible/ipc/DocAccessibleParent.h
Normal file
141
accessible/ipc/DocAccessibleParent.h
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* 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_a11y_DocAccessibleParent_h
|
||||||
|
#define mozilla_a11y_DocAccessibleParent_h
|
||||||
|
|
||||||
|
#include "nsAccessibilityService.h"
|
||||||
|
#include "ProxyAccessible.h"
|
||||||
|
#include "mozilla/a11y/PDocAccessibleParent.h"
|
||||||
|
#include "nsClassHashtable.h"
|
||||||
|
#include "nsHashKeys.h"
|
||||||
|
#include "nsISupportsImpl.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace a11y {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These objects live in the main process and comunicate with and represent
|
||||||
|
* an accessible document in a content process.
|
||||||
|
*/
|
||||||
|
class DocAccessibleParent : public ProxyAccessible,
|
||||||
|
public PDocAccessibleParent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DocAccessibleParent() :
|
||||||
|
mParentDoc(nullptr)
|
||||||
|
{ MOZ_COUNT_CTOR_INHERITED(DocAccessibleParent, ProxyAccessible); }
|
||||||
|
~DocAccessibleParent()
|
||||||
|
{
|
||||||
|
MOZ_COUNT_DTOR_INHERITED(DocAccessibleParent, ProxyAccessible);
|
||||||
|
MOZ_ASSERT(mChildDocs.Length() == 0);
|
||||||
|
MOZ_ASSERT(!mParentDoc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called when a message from a document in a child process notifies the main
|
||||||
|
* process it is firing an event.
|
||||||
|
*/
|
||||||
|
virtual bool RecvEvent(const uint32_t& aType) MOZ_OVERRIDE
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool RecvShowEvent(const ShowEventData& aData) MOZ_OVERRIDE;
|
||||||
|
virtual bool RecvHideEvent(const uint64_t& aRootID) MOZ_OVERRIDE;
|
||||||
|
|
||||||
|
virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mChildDocs.IsEmpty(),
|
||||||
|
"why wheren't the child docs destroyed already?");
|
||||||
|
mParentDoc ? mParentDoc->RemoveChildDoc(this)
|
||||||
|
: GetAccService()->RemoteDocShutdown(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the main processes representation of the parent document (if any)
|
||||||
|
* of the document this object represents.
|
||||||
|
*/
|
||||||
|
DocAccessibleParent* Parent() const { return mParentDoc; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called when a document in a content process notifies the main process of a
|
||||||
|
* new child document.
|
||||||
|
*/
|
||||||
|
bool AddChildDoc(DocAccessibleParent* aChildDoc, uint64_t aParentID)
|
||||||
|
{
|
||||||
|
ProxyAccessible* outerDoc = mAccessibles.GetEntry(aParentID)->mProxy;
|
||||||
|
if (!outerDoc)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
aChildDoc->mParent = outerDoc;
|
||||||
|
outerDoc->SetChildDoc(aChildDoc);
|
||||||
|
mChildDocs.AppendElement(aChildDoc);
|
||||||
|
aChildDoc->mParentDoc = this;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called when the document in the content process this object represents
|
||||||
|
* notifies the main process a child document has been removed.
|
||||||
|
*/
|
||||||
|
void RemoveChildDoc(DocAccessibleParent* aChildDoc)
|
||||||
|
{
|
||||||
|
aChildDoc->mParent->SetChildDoc(nullptr);
|
||||||
|
mChildDocs.RemoveElement(aChildDoc);
|
||||||
|
aChildDoc->mParentDoc = nullptr;
|
||||||
|
MOZ_ASSERT(aChildDoc->mChildDocs.Length() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveAccessible(ProxyAccessible* aAccessible)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mAccessibles.GetEntry(aAccessible->ID()));
|
||||||
|
mAccessibles.RemoveEntry(aAccessible->ID());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
class ProxyEntry : public PLDHashEntryHdr
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ProxyEntry(const void*) : mProxy(nullptr) {}
|
||||||
|
ProxyEntry(ProxyEntry&& aOther) :
|
||||||
|
mProxy(aOther.mProxy) { aOther.mProxy = nullptr; }
|
||||||
|
~ProxyEntry() { delete mProxy; }
|
||||||
|
|
||||||
|
typedef uint64_t KeyType;
|
||||||
|
typedef const void* KeyTypePointer;
|
||||||
|
|
||||||
|
bool KeyEquals(const void* aKey) const
|
||||||
|
{ return mProxy->ID() == (uint64_t)aKey; }
|
||||||
|
|
||||||
|
static const void* KeyToPointer(uint64_t aKey) { return (void*)aKey; }
|
||||||
|
|
||||||
|
static PLDHashNumber HashKey(const void* aKey) { return (uint64_t)aKey; }
|
||||||
|
|
||||||
|
enum { ALLOW_MEMMOVE = true };
|
||||||
|
|
||||||
|
ProxyAccessible* mProxy;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t AddSubtree(ProxyAccessible* aParent,
|
||||||
|
const nsTArray<AccessibleData>& aNewTree, uint32_t aIdx,
|
||||||
|
uint32_t aIdxInParent);
|
||||||
|
|
||||||
|
nsTArray<DocAccessibleParent*> mChildDocs;
|
||||||
|
DocAccessibleParent* mParentDoc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Conceptually this is a map from IDs to proxies, but we store the ID in the
|
||||||
|
* proxy object so we can't use a real map.
|
||||||
|
*/
|
||||||
|
nsTHashtable<ProxyEntry> mAccessibles;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
44
accessible/ipc/PDocAccessible.ipdl
Normal file
44
accessible/ipc/PDocAccessible.ipdl
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace a11y {
|
||||||
|
|
||||||
|
struct AccessibleData
|
||||||
|
{
|
||||||
|
uint64_t ID;
|
||||||
|
uint32_t Role;
|
||||||
|
uint32_t ChildrenCount;
|
||||||
|
nsString Name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ShowEventData
|
||||||
|
{
|
||||||
|
uint64_t ID;
|
||||||
|
uint32_t Idx;
|
||||||
|
AccessibleData[] NewTree;
|
||||||
|
};
|
||||||
|
|
||||||
|
protocol PDocAccessible
|
||||||
|
{
|
||||||
|
manager PContent;
|
||||||
|
|
||||||
|
parent:
|
||||||
|
__delete__();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Notify the parent process the document in the child process is firing an
|
||||||
|
* event.
|
||||||
|
*/
|
||||||
|
Event(uint32_t type);
|
||||||
|
ShowEvent(ShowEventData data);
|
||||||
|
HideEvent(uint64_t aRootID);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
42
accessible/ipc/ProxyAccessible.cpp
Normal file
42
accessible/ipc/ProxyAccessible.cpp
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* 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 "ProxyAccessible.h"
|
||||||
|
#include "DocAccessibleParent.h"
|
||||||
|
#include "mozilla/a11y/Platform.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace a11y {
|
||||||
|
|
||||||
|
void
|
||||||
|
ProxyAccessible::Shutdown()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(!mOuterDoc);
|
||||||
|
|
||||||
|
uint32_t childCount = mChildren.Length();
|
||||||
|
for (uint32_t idx = 0; idx < childCount; idx++)
|
||||||
|
mChildren[idx]->Shutdown();
|
||||||
|
|
||||||
|
mChildren.Clear();
|
||||||
|
ProxyDestroyed(this);
|
||||||
|
mDoc->RemoveAccessible(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ProxyAccessible::SetChildDoc(DocAccessibleParent* aParent)
|
||||||
|
{
|
||||||
|
if (aParent) {
|
||||||
|
MOZ_ASSERT(mChildren.IsEmpty());
|
||||||
|
mChildren.AppendElement(aParent);
|
||||||
|
mOuterDoc = true;
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(mChildren.Length() == 1);
|
||||||
|
mChildren.Clear();
|
||||||
|
mOuterDoc = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
77
accessible/ipc/ProxyAccessible.h
Normal file
77
accessible/ipc/ProxyAccessible.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* 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_a11y_ProxyAccessible_h
|
||||||
|
#define mozilla_a11y_ProxyAccessible_h
|
||||||
|
|
||||||
|
#include "mozilla/a11y/Role.h"
|
||||||
|
#include "nsString.h"
|
||||||
|
#include "nsTArray.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace a11y {
|
||||||
|
|
||||||
|
class DocAccessibleParent;
|
||||||
|
|
||||||
|
class ProxyAccessible
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
ProxyAccessible(uint64_t aID, ProxyAccessible* aParent,
|
||||||
|
DocAccessibleParent* aDoc, role aRole,
|
||||||
|
const nsString& aName) :
|
||||||
|
mParent(aParent), mDoc(aDoc), mID(aID), mRole(aRole), mOuterDoc(false), mName(aName)
|
||||||
|
{
|
||||||
|
MOZ_COUNT_CTOR(ProxyAccessible);
|
||||||
|
}
|
||||||
|
~ProxyAccessible() { MOZ_COUNT_DTOR(ProxyAccessible); }
|
||||||
|
|
||||||
|
void AddChildAt(uint32_t aIdx, ProxyAccessible* aChild)
|
||||||
|
{ mChildren.InsertElementAt(aIdx, aChild); }
|
||||||
|
|
||||||
|
uint32_t ChildrenCount() const { return mChildren.Length(); }
|
||||||
|
|
||||||
|
void Shutdown();
|
||||||
|
|
||||||
|
void SetChildDoc(DocAccessibleParent*);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the role of the accessible we're proxying.
|
||||||
|
*/
|
||||||
|
role Role() const { return mRole; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow the platform to store a pointers worth of data on us.
|
||||||
|
*/
|
||||||
|
uintptr_t GetWrapper() const { return mWrapper; }
|
||||||
|
void SetWrapper(uintptr_t aWrapper) { mWrapper = aWrapper; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the ID of the accessible being proxied.
|
||||||
|
*/
|
||||||
|
uint64_t ID() const { return mID; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ProxyAccessible() :
|
||||||
|
mParent(nullptr), mDoc(nullptr) { MOZ_COUNT_CTOR(ProxyAccessible); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ProxyAccessible* mParent;
|
||||||
|
|
||||||
|
private:
|
||||||
|
nsTArray<ProxyAccessible*> mChildren;
|
||||||
|
DocAccessibleParent* mDoc;
|
||||||
|
uintptr_t mWrapper;
|
||||||
|
uint64_t mID;
|
||||||
|
role mRole : 31;
|
||||||
|
bool mOuterDoc : 1;
|
||||||
|
nsString mName;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
28
accessible/ipc/moz.build
Normal file
28
accessible/ipc/moz.build
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||||
|
# vim: set filetype=python:
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
IPDL_SOURCES += ['PDocAccessible.ipdl']
|
||||||
|
|
||||||
|
EXPORTS.mozilla.a11y += [
|
||||||
|
'DocAccessibleChild.h',
|
||||||
|
'DocAccessibleParent.h',
|
||||||
|
'ProxyAccessible.h'
|
||||||
|
]
|
||||||
|
|
||||||
|
SOURCES += [
|
||||||
|
'DocAccessibleChild.cpp',
|
||||||
|
'DocAccessibleParent.cpp',
|
||||||
|
'ProxyAccessible.cpp'
|
||||||
|
]
|
||||||
|
|
||||||
|
LOCAL_INCLUDES += [
|
||||||
|
'../base',
|
||||||
|
'../generic',
|
||||||
|
]
|
||||||
|
|
||||||
|
FINAL_LIBRARY = 'xul'
|
||||||
|
|
||||||
|
include('/ipc/chromium/chromium-config.mozbuild')
|
@ -20,6 +20,8 @@ XPCOMUtils.defineLazyModuleGetter(this, 'ContentControl',
|
|||||||
'resource://gre/modules/accessibility/ContentControl.jsm');
|
'resource://gre/modules/accessibility/ContentControl.jsm');
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, 'Roles',
|
XPCOMUtils.defineLazyModuleGetter(this, 'Roles',
|
||||||
'resource://gre/modules/accessibility/Constants.jsm');
|
'resource://gre/modules/accessibility/Constants.jsm');
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, 'States',
|
||||||
|
'resource://gre/modules/accessibility/Constants.jsm');
|
||||||
|
|
||||||
Logger.debug('content-script.js');
|
Logger.debug('content-script.js');
|
||||||
|
|
||||||
@ -142,7 +144,20 @@ addMessageListener(
|
|||||||
eventManager.inTest = m.json.inTest;
|
eventManager.inTest = m.json.inTest;
|
||||||
eventManager.start();
|
eventManager.start();
|
||||||
|
|
||||||
sendAsyncMessage('AccessFu:ContentStarted');
|
function contentStarted() {
|
||||||
|
let accDoc = Utils.AccRetrieval.getAccessibleFor(content.document);
|
||||||
|
if (accDoc && !Utils.getState(accDoc).contains(States.BUSY)) {
|
||||||
|
sendAsyncMessage('AccessFu:ContentStarted');
|
||||||
|
} else {
|
||||||
|
content.setTimeout(contentStarted, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m.json.inTest) {
|
||||||
|
// During a test we want to wait for the document to finish loading for
|
||||||
|
// consistency.
|
||||||
|
contentStarted();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
addMessageListener(
|
addMessageListener(
|
||||||
|
@ -33,6 +33,15 @@ PlatformShutdown()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ProxyCreated(ProxyAccessible*)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ProxyDestroyed(ProxyAccessible*)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ elif toolkit == 'cocoa':
|
|||||||
else:
|
else:
|
||||||
DIRS += ['other']
|
DIRS += ['other']
|
||||||
|
|
||||||
DIRS += ['base', 'generic', 'html', 'interfaces', 'jsat', 'xpcom']
|
DIRS += ['base', 'generic', 'html', 'interfaces', 'ipc', 'jsat', 'xpcom']
|
||||||
|
|
||||||
if CONFIG['MOZ_XUL']:
|
if CONFIG['MOZ_XUL']:
|
||||||
DIRS += ['xul']
|
DIRS += ['xul']
|
||||||
|
@ -18,3 +18,13 @@ void
|
|||||||
a11y::PlatformShutdown()
|
a11y::PlatformShutdown()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
a11y::ProxyCreated(ProxyAccessible*)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
a11y::ProxyDestroyed(ProxyAccessible*)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
@ -34,3 +34,12 @@ a11y::PlatformShutdown()
|
|||||||
nsWinUtils::ShutdownWindowEmulation();
|
nsWinUtils::ShutdownWindowEmulation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
a11y::ProxyCreated(ProxyAccessible*)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
a11y::ProxyDestroyed(ProxyAccessible*)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
|
|
||||||
#define ASSERT(x) if (!(x)) { MOZ_CRASH(); }
|
#define ASSERT(x) if (!(x)) { MOZ_CRASH(); }
|
||||||
|
|
||||||
|
|
||||||
// Functions being loaded by XPCOMGlue
|
// Functions being loaded by XPCOMGlue
|
||||||
XRE_ProcLoaderServiceRunType XRE_ProcLoaderServiceRun;
|
XRE_ProcLoaderServiceRunType XRE_ProcLoaderServiceRun;
|
||||||
XRE_ProcLoaderClientInitType XRE_ProcLoaderClientInit;
|
XRE_ProcLoaderClientInitType XRE_ProcLoaderClientInit;
|
||||||
@ -43,6 +42,10 @@ static const nsDynamicFunctionLoad kXULFuncs[] = {
|
|||||||
{ nullptr, nullptr }
|
{ nullptr, nullptr }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef mozilla::Vector<int> FdArray;
|
||||||
|
static const int kReservedFileDescriptors = 5;
|
||||||
|
static const int kBeginReserveFileDescriptor = STDERR_FILENO + 1;
|
||||||
|
|
||||||
static int
|
static int
|
||||||
GetDirnameSlash(const char *aPath, char *aOutDir, int aMaxLen)
|
GetDirnameSlash(const char *aPath, char *aOutDir, int aMaxLen)
|
||||||
{
|
{
|
||||||
@ -69,7 +72,7 @@ GetXPCOMPath(const char *aProgram, char *aOutPath, int aMaxLen)
|
|||||||
int len = GetDirnameSlash(progBuf, aOutPath, aMaxLen);
|
int len = GetDirnameSlash(progBuf, aOutPath, aMaxLen);
|
||||||
NS_ENSURE_TRUE(!!len, false);
|
NS_ENSURE_TRUE(!!len, false);
|
||||||
|
|
||||||
NS_ENSURE_TRUE((len + sizeof(XPCOM_DLL)) < aMaxLen, false);
|
NS_ENSURE_TRUE((len + sizeof(XPCOM_DLL)) < (unsigned)aMaxLen, false);
|
||||||
char *afterSlash = aOutPath + len;
|
char *afterSlash = aOutPath + len;
|
||||||
strcpy(afterSlash, XPCOM_DLL);
|
strcpy(afterSlash, XPCOM_DLL);
|
||||||
return true;
|
return true;
|
||||||
@ -181,7 +184,7 @@ LoadStaticData(int argc, const char *argv[])
|
|||||||
* The parent is the b2g process and child for Nuwa.
|
* The parent is the b2g process and child for Nuwa.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
RunProcesses(int argc, const char *argv[])
|
RunProcesses(int argc, const char *argv[], FdArray& aReservedFds)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* The original main() of the b2g process. It is renamed to
|
* The original main() of the b2g process. It is renamed to
|
||||||
@ -212,15 +215,50 @@ RunProcesses(int argc, const char *argv[])
|
|||||||
* The b2g process would send a IPC message of loading Nuwa
|
* The b2g process would send a IPC message of loading Nuwa
|
||||||
* as the replacement of forking and executing plugin-container.
|
* as the replacement of forking and executing plugin-container.
|
||||||
*/
|
*/
|
||||||
return XRE_ProcLoaderServiceRun(getppid(), childSock, argc, argv);
|
return XRE_ProcLoaderServiceRun(getppid(), childSock, argc, argv,
|
||||||
|
aReservedFds);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The b2g process
|
// The b2g process
|
||||||
int childPid = pid;
|
int childPid = pid;
|
||||||
XRE_ProcLoaderClientInit(childPid, parentSock);
|
XRE_ProcLoaderClientInit(childPid, parentSock, aReservedFds);
|
||||||
return b2g_main(argc, argv);
|
return b2g_main(argc, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reserve the file descriptors that shouldn't be taken for other use for the
|
||||||
|
* child process.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
ReserveFileDescriptors(FdArray& aReservedFds)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < kReservedFileDescriptors; i++) {
|
||||||
|
struct stat fileState;
|
||||||
|
int target = kBeginReserveFileDescriptor + i;
|
||||||
|
if (fstat(target, &fileState) == 0) {
|
||||||
|
MOZ_CRASH("ProcLoader error: a magic file descriptor is occupied.");
|
||||||
|
}
|
||||||
|
|
||||||
|
int fd = open("/dev/null", O_RDWR);
|
||||||
|
if (fd == -1) {
|
||||||
|
MOZ_CRASH("ProcLoader error: failed to reserve a magic file descriptor.");
|
||||||
|
}
|
||||||
|
|
||||||
|
aReservedFds.append(target);
|
||||||
|
|
||||||
|
if (fd == target) {
|
||||||
|
// No need to call dup2(). We already occupy the desired file descriptor.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dup2(fd, target)) {
|
||||||
|
MOZ_CRASH("ProcLoader error: failed to reserve a magic file descriptor.");
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* B2G Loader is responsible for loading the b2g process and the
|
* B2G Loader is responsible for loading the b2g process and the
|
||||||
* Nuwa process. It forks into the parent process, for the b2g
|
* Nuwa process. It forks into the parent process, for the b2g
|
||||||
@ -234,7 +272,12 @@ RunProcesses(int argc, const char *argv[])
|
|||||||
int
|
int
|
||||||
main(int argc, const char* argv[])
|
main(int argc, const char* argv[])
|
||||||
{
|
{
|
||||||
const char *program = argv[0];
|
/**
|
||||||
|
* Reserve file descriptors before loading static data.
|
||||||
|
*/
|
||||||
|
FdArray reservedFds;
|
||||||
|
ReserveFileDescriptors(reservedFds);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Before fork(), libxul and static data of Gecko are loaded for
|
* Before fork(), libxul and static data of Gecko are loaded for
|
||||||
* sharing.
|
* sharing.
|
||||||
@ -244,5 +287,5 @@ main(int argc, const char* argv[])
|
|||||||
return 255;
|
return 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
return RunProcesses(argc, argv);
|
return RunProcesses(argc, argv, reservedFds);
|
||||||
}
|
}
|
||||||
|
@ -677,6 +677,8 @@ pref("javascript.options.mem.gc_max_empty_chunk_count", 2);
|
|||||||
// Show/Hide scrollbars when active/inactive
|
// Show/Hide scrollbars when active/inactive
|
||||||
pref("ui.showHideScrollbars", 1);
|
pref("ui.showHideScrollbars", 1);
|
||||||
pref("ui.useOverlayScrollbars", 1);
|
pref("ui.useOverlayScrollbars", 1);
|
||||||
|
pref("ui.scrollbarFadeBeginDelay", 450);
|
||||||
|
pref("ui.scrollbarFadeDuration", 200);
|
||||||
|
|
||||||
// Enable the ProcessPriorityManager, and give processes with no visible
|
// Enable the ProcessPriorityManager, and give processes with no visible
|
||||||
// documents a 1s grace period before they're eligible to be marked as
|
// documents a 1s grace period before they're eligible to be marked as
|
||||||
|
@ -269,6 +269,7 @@ let consoleWatcher = {
|
|||||||
'Mixed Content Message',
|
'Mixed Content Message',
|
||||||
'CSP',
|
'CSP',
|
||||||
'Invalid HSTS Headers',
|
'Invalid HSTS Headers',
|
||||||
|
'Invalid HPKP Headers',
|
||||||
'Insecure Password Field',
|
'Insecure Password Field',
|
||||||
'SSL',
|
'SSL',
|
||||||
'CORS'
|
'CORS'
|
||||||
|
@ -1177,16 +1177,9 @@ pref("dom.ipc.plugins.enabled.x86_64", true);
|
|||||||
pref("dom.ipc.plugins.enabled", true);
|
pref("dom.ipc.plugins.enabled", true);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(NIGHTLY_BUILD)
|
// Start the browser in e10s mode
|
||||||
// browser.tabs.remote is enabled on nightly. However, users won't
|
|
||||||
// actually get remote tabs unless they enable
|
|
||||||
// browser.tabs.remote.autostart or they use the "New OOP Window" menu
|
|
||||||
// option.
|
|
||||||
pref("browser.tabs.remote", true);
|
|
||||||
#else
|
|
||||||
pref("browser.tabs.remote", false);
|
|
||||||
#endif
|
|
||||||
pref("browser.tabs.remote.autostart", false);
|
pref("browser.tabs.remote.autostart", false);
|
||||||
|
pref("browser.tabs.remote.desktopbehavior", true);
|
||||||
|
|
||||||
#if defined(MOZ_CONTENT_SANDBOX) && defined(XP_WIN)
|
#if defined(MOZ_CONTENT_SANDBOX) && defined(XP_WIN)
|
||||||
// This controls whether the content process on Windows is sandboxed.
|
// This controls whether the content process on Windows is sandboxed.
|
||||||
|
@ -6792,11 +6792,11 @@ var gIdentityHandler = {
|
|||||||
if (gURLBar.getAttribute("pageproxystate") != "valid")
|
if (gURLBar.getAttribute("pageproxystate") != "valid")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var value = content.location.href;
|
let value = gBrowser.currentURI.spec;
|
||||||
var urlString = value + "\n" + content.document.title;
|
let urlString = value + "\n" + gBrowser.contentTitle;
|
||||||
var htmlString = "<a href=\"" + value + "\">" + value + "</a>";
|
let htmlString = "<a href=\"" + value + "\">" + value + "</a>";
|
||||||
|
|
||||||
var dt = event.dataTransfer;
|
let dt = event.dataTransfer;
|
||||||
dt.setData("text/x-moz-url", urlString);
|
dt.setData("text/x-moz-url", urlString);
|
||||||
dt.setData("text/uri-list", value);
|
dt.setData("text/uri-list", value);
|
||||||
dt.setData("text/plain", value);
|
dt.setData("text/plain", value);
|
||||||
@ -6952,20 +6952,16 @@ let gRemoteTabsUI = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let remoteTabs = Services.appinfo.browserTabsRemote;
|
|
||||||
let autostart = Services.appinfo.browserTabsRemoteAutostart;
|
|
||||||
|
|
||||||
let newRemoteWindow = document.getElementById("menu_newRemoteWindow");
|
let newRemoteWindow = document.getElementById("menu_newRemoteWindow");
|
||||||
let newNonRemoteWindow = document.getElementById("menu_newNonRemoteWindow");
|
let newNonRemoteWindow = document.getElementById("menu_newNonRemoteWindow");
|
||||||
|
#ifdef E10S_TESTING_ONLY
|
||||||
if (!remoteTabs) {
|
let autostart = Services.appinfo.browserTabsRemoteAutostart;
|
||||||
newRemoteWindow.hidden = true;
|
|
||||||
newNonRemoteWindow.hidden = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
newRemoteWindow.hidden = autostart;
|
newRemoteWindow.hidden = autostart;
|
||||||
newNonRemoteWindow.hidden = !autostart;
|
newNonRemoteWindow.hidden = !autostart;
|
||||||
|
#else
|
||||||
|
newRemoteWindow.hidden = true;
|
||||||
|
newNonRemoteWindow.hidden = true;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -502,7 +502,14 @@ nsContextMenu.prototype = {
|
|||||||
let tt = devtools.TargetFactory.forTab(gBrowser.selectedTab);
|
let tt = devtools.TargetFactory.forTab(gBrowser.selectedTab);
|
||||||
return gDevTools.showToolbox(tt, "inspector").then(function(toolbox) {
|
return gDevTools.showToolbox(tt, "inspector").then(function(toolbox) {
|
||||||
let inspector = toolbox.getCurrentPanel();
|
let inspector = toolbox.getCurrentPanel();
|
||||||
inspector.selection.setNode(this.target, "browser-context-menu");
|
if (this.isRemote) {
|
||||||
|
this.browser.messageManager.sendAsyncMessage("debug:inspect", {}, {node: this.target});
|
||||||
|
inspector.walker.findInspectingNode().then(nodeFront => {
|
||||||
|
inspector.selection.setNodeFront(nodeFront, "browser-context-menu");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
inspector.selection.setNode(this.target, "browser-context-menu");
|
||||||
|
}
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -2563,7 +2563,6 @@
|
|||||||
]]>
|
]]>
|
||||||
</body>
|
</body>
|
||||||
</method>
|
</method>
|
||||||
#endif
|
|
||||||
|
|
||||||
<method name="moveTabTo">
|
<method name="moveTabTo">
|
||||||
<parameter name="aTab"/>
|
<parameter name="aTab"/>
|
||||||
|
@ -65,6 +65,7 @@ support-files =
|
|||||||
offlineQuotaNotification.html
|
offlineQuotaNotification.html
|
||||||
page_style_sample.html
|
page_style_sample.html
|
||||||
parsingTestHelpers.jsm
|
parsingTestHelpers.jsm
|
||||||
|
pinning_headers.sjs
|
||||||
popup_blocker.html
|
popup_blocker.html
|
||||||
print_postdata.sjs
|
print_postdata.sjs
|
||||||
redirect_bug623155.sjs
|
redirect_bug623155.sjs
|
||||||
@ -493,4 +494,5 @@ skip-if = e10s # Bug ?????? - test directly manipulates content (content.documen
|
|||||||
[browser_bug1045809.js]
|
[browser_bug1045809.js]
|
||||||
skip-if = e10s # Bug 1068360 - [e10s] Mixed content blocker doorhanger doesn't work
|
skip-if = e10s # Bug 1068360 - [e10s] Mixed content blocker doorhanger doesn't work
|
||||||
[browser_e10s_switchbrowser.js]
|
[browser_e10s_switchbrowser.js]
|
||||||
|
[browser_blockHPKP.js]
|
||||||
|
skip-if = e10s # bug ?????? - test directly manipulates content (content.document.getElementById)
|
||||||
|
115
browser/base/content/test/general/browser_blockHPKP.js
Normal file
115
browser/base/content/test/general/browser_blockHPKP.js
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
// Test that visiting a site pinned with HPKP headers does not succeed when it
|
||||||
|
// uses a certificate with a key not in the pinset. This should result in an
|
||||||
|
// about:neterror page
|
||||||
|
// Also verify that removal of the HPKP headers succeeds (via HPKP headers)
|
||||||
|
// and that after removal the visit to the site with the previously
|
||||||
|
// unauthorized pins succeeds.
|
||||||
|
//
|
||||||
|
// This test required three certs to be created in build/pgo/certs:
|
||||||
|
// 1. A new trusted root:
|
||||||
|
// a. certutil -S -s "Alternate trusted authority" -s "CN=Alternate Trusted Authority" -t "C,," -x -m 1 -v 120 -n "alternateTrustedAuthority" -Z SHA256 -g 2048 -2 -d .
|
||||||
|
// b. (export) certutil -L -d . -n "alternateTrustedAuthority" -a -o alternateroot.ca
|
||||||
|
// (files ended in .ca are added as trusted roots by the mochitest harness)
|
||||||
|
// 2. A good pinning server cert (signed by the pgo root):
|
||||||
|
// certutil -S -n "dynamicPinningGood" -s "CN=dynamic-pinning.example.com" -c "pgo temporary ca" -t "P,," -k rsa -g 2048 -Z SHA256 -m 8939454 -v 120 -8 "*.include-subdomains.pinning-dynamic.example.com,*.pinning-dynamic.example.com" -d .
|
||||||
|
// 3. A certificate with a different issuer, so as to cause a key pinning violation."
|
||||||
|
// certutil -S -n "dynamicPinningBad" -s "CN=bad.include-subdomains.pinning-dynamic.example.com" -c "alternateTrustedAuthority" -t "P,," -k rsa -g 2048 -Z SHA256 -m 893945439 -v 120 -8 "bad.include-subdomains.pinning-dynamic.example.com" -d .
|
||||||
|
|
||||||
|
const gSSService = Cc["@mozilla.org/ssservice;1"]
|
||||||
|
.getService(Ci.nsISiteSecurityService);
|
||||||
|
const gIOService = Cc["@mozilla.org/network/io-service;1"]
|
||||||
|
.getService(Ci.nsIIOService);
|
||||||
|
|
||||||
|
const kPinningDomain = "include-subdomains.pinning-dynamic.example.com";
|
||||||
|
const khpkpPinninEnablePref = "security.cert_pinning.process_headers_from_non_builtin_roots";
|
||||||
|
const kpkpEnforcementPref = "security.cert_pinning.enforcement_level";
|
||||||
|
const kBadPinningDomain = "bad.include-subdomains.pinning-dynamic.example.com";
|
||||||
|
const kURLPath = "/browser/browser/base/content/test/general/pinning_headers.sjs?";
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
// Enable enforcing strict pinning and processing headers from
|
||||||
|
// non-builtin roots.
|
||||||
|
Services.prefs.setIntPref(kpkpEnforcementPref, 2);
|
||||||
|
Services.prefs.setBoolPref(khpkpPinninEnablePref, true);
|
||||||
|
registerCleanupFunction(function () {
|
||||||
|
Services.prefs.clearUserPref(kpkpEnforcementPref);
|
||||||
|
Services.prefs.clearUserPref(khpkpPinninEnablePref);
|
||||||
|
let uri = gIOService.newURI("https://" + kPinningDomain, null, null);
|
||||||
|
gSSService.removeState(Ci.nsISiteSecurityService.HEADER_HPKP, uri, 0);
|
||||||
|
});
|
||||||
|
whenNewTabLoaded(window, loadPinningPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start by making a successful connection to a domain that will pin a site
|
||||||
|
function loadPinningPage() {
|
||||||
|
gBrowser.selectedBrowser.addEventListener("load",
|
||||||
|
successfulPinningPageListener,
|
||||||
|
true);
|
||||||
|
|
||||||
|
gBrowser.selectedBrowser.loadURI("https://" + kPinningDomain + kURLPath + "valid");
|
||||||
|
}
|
||||||
|
|
||||||
|
// After the site is pinned try to load with a subdomain site that should
|
||||||
|
// fail to validate
|
||||||
|
let successfulPinningPageListener = {
|
||||||
|
handleEvent: function() {
|
||||||
|
gBrowser.selectedBrowser.removeEventListener("load", this, true);
|
||||||
|
gBrowser.addProgressListener(certErrorProgressListener);
|
||||||
|
gBrowser.selectedBrowser.loadURI("https://" + kBadPinningDomain);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The browser should load about:neterror, when this happens, proceed
|
||||||
|
// to load the pinning domain again, this time removing the pinning information
|
||||||
|
let certErrorProgressListener = {
|
||||||
|
buttonClicked: false,
|
||||||
|
onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
|
||||||
|
if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
|
||||||
|
let self = this;
|
||||||
|
// Can't directly call button.click() in onStateChange
|
||||||
|
executeSoon(function() {
|
||||||
|
let button = content.document.getElementById("errorTryAgain");
|
||||||
|
// If about:neterror hasn't fully loaded, the button won't be present.
|
||||||
|
// It will eventually be there, however.
|
||||||
|
if (button && !self.buttonClicked) {
|
||||||
|
gBrowser.removeProgressListener(self);
|
||||||
|
gBrowser.selectedBrowser.addEventListener("load",
|
||||||
|
successfulPinningRemovalPageListener,
|
||||||
|
true);
|
||||||
|
gBrowser.selectedBrowser.loadURI("https://" + kPinningDomain + kURLPath + "zeromaxagevalid");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// After the pinning information has been removed (successful load) proceed
|
||||||
|
// to load again with the invalid pin domain.
|
||||||
|
let successfulPinningRemovalPageListener = {
|
||||||
|
handleEvent: function() {
|
||||||
|
gBrowser.selectedBrowser.removeEventListener("load", this, true);
|
||||||
|
gBrowser.selectedBrowser.addEventListener("load",
|
||||||
|
successfulLoadListener,
|
||||||
|
true);
|
||||||
|
|
||||||
|
gBrowser.selectedBrowser.loadURI("https://" + kBadPinningDomain);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Finally, we should successfully load
|
||||||
|
// https://bad.include-subdomains.pinning-dynamic.example.com.
|
||||||
|
let successfulLoadListener = {
|
||||||
|
handleEvent: function() {
|
||||||
|
gBrowser.selectedBrowser.removeEventListener("load", this, true);
|
||||||
|
gBrowser.removeTab(gBrowser.selectedTab);
|
||||||
|
ok(true, "load complete");
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
};
|
23
browser/base/content/test/general/pinning_headers.sjs
Normal file
23
browser/base/content/test/general/pinning_headers.sjs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
const INVALIDPIN1 = "pin-sha256=\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\";";
|
||||||
|
const INVALIDPIN2 = "pin-sha256=\"AAAAAAAAAAAAAAAAAAAAAAAAAj0e1Md7GkYYkVoZWmM=\";";
|
||||||
|
const VALIDPIN = "pin-sha256=\"hXweb81C3HnmM2Ai1dnUzFba40UJMhuu8qZmvN/6WWc=\";";
|
||||||
|
|
||||||
|
function handleRequest(request, response)
|
||||||
|
{
|
||||||
|
// avoid confusing cache behaviors
|
||||||
|
response.setHeader("Cache-Control", "no-cache", false);
|
||||||
|
|
||||||
|
response.setHeader("Content-Type", "text/plain; charset=utf-8", false);
|
||||||
|
switch (request.queryString) {
|
||||||
|
case "zeromaxagevalid":
|
||||||
|
response.setHeader("Public-Key-Pins", "max-age=0;" + VALIDPIN +
|
||||||
|
INVALIDPIN2 + "includeSubdomains");
|
||||||
|
break;
|
||||||
|
case "valid":
|
||||||
|
default:
|
||||||
|
response.setHeader("Public-Key-Pins", "max-age=50000;" + VALIDPIN +
|
||||||
|
INVALIDPIN2 + "includeSubdomains");
|
||||||
|
}
|
||||||
|
|
||||||
|
response.write("Hello world!" + request.host);
|
||||||
|
}
|
@ -1043,33 +1043,31 @@ if (Services.prefs.getBoolPref("privacy.panicButton.enabled")) {
|
|||||||
|
|
||||||
#ifdef E10S_TESTING_ONLY
|
#ifdef E10S_TESTING_ONLY
|
||||||
/**
|
/**
|
||||||
* The e10s button's purpose is to lower the barrier of entry
|
* The e10s button's purpose is to lower the barrier of entry
|
||||||
* for our Nightly testers to use e10s windows. We'll be removing it
|
* for our Nightly testers to use e10s windows. We'll be removing it
|
||||||
* once remote tabs are enabled. This button should never ever make it
|
* once remote tabs are enabled. This button should never ever make it
|
||||||
* to production. If it does, that'd be bad, and we should all feel bad.
|
* to production. If it does, that'd be bad, and we should all feel bad.
|
||||||
*/
|
*/
|
||||||
if (Services.prefs.getBoolPref("browser.tabs.remote")) {
|
let getCommandFunction = function(aOpenRemote) {
|
||||||
let getCommandFunction = function(aOpenRemote) {
|
return function(aEvent) {
|
||||||
return function(aEvent) {
|
let win = aEvent.view;
|
||||||
let win = aEvent.view;
|
if (win && typeof win.OpenBrowserWindow == "function") {
|
||||||
if (win && typeof win.OpenBrowserWindow == "function") {
|
win.OpenBrowserWindow({remote: aOpenRemote});
|
||||||
win.OpenBrowserWindow({remote: aOpenRemote});
|
}
|
||||||
}
|
};
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let openRemote = !Services.appinfo.browserTabsRemoteAutostart;
|
|
||||||
// Like the XUL menuitem counterparts, we hard-code these strings in because
|
|
||||||
// this button should never roll into production.
|
|
||||||
let buttonLabel = openRemote ? "New e10s Window"
|
|
||||||
: "New Non-e10s Window";
|
|
||||||
|
|
||||||
CustomizableWidgets.push({
|
|
||||||
id: "e10s-button",
|
|
||||||
label: buttonLabel,
|
|
||||||
tooltiptext: buttonLabel,
|
|
||||||
defaultArea: CustomizableUI.AREA_PANEL,
|
|
||||||
onCommand: getCommandFunction(openRemote),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let openRemote = !Services.appinfo.browserTabsRemoteAutostart;
|
||||||
|
// Like the XUL menuitem counterparts, we hard-code these strings in because
|
||||||
|
// this button should never roll into production.
|
||||||
|
let buttonLabel = openRemote ? "New e10s Window"
|
||||||
|
: "New Non-e10s Window";
|
||||||
|
|
||||||
|
CustomizableWidgets.push({
|
||||||
|
id: "e10s-button",
|
||||||
|
label: buttonLabel,
|
||||||
|
tooltiptext: buttonLabel,
|
||||||
|
defaultArea: CustomizableUI.AREA_PANEL,
|
||||||
|
onCommand: getCommandFunction(openRemote),
|
||||||
|
});
|
||||||
#endif
|
#endif
|
||||||
|
@ -16,6 +16,11 @@ Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/
|
|||||||
Services.prefs.setBoolPref("browser.uiCustomization.skipSourceNodeCheck", true);
|
Services.prefs.setBoolPref("browser.uiCustomization.skipSourceNodeCheck", true);
|
||||||
registerCleanupFunction(() => Services.prefs.clearUserPref("browser.uiCustomization.skipSourceNodeCheck"));
|
registerCleanupFunction(() => Services.prefs.clearUserPref("browser.uiCustomization.skipSourceNodeCheck"));
|
||||||
|
|
||||||
|
// Remove temporary e10s related new window options in customize ui,
|
||||||
|
// they break a lot of tests.
|
||||||
|
CustomizableUI.destroyWidget("e10s-button");
|
||||||
|
CustomizableUI.removeWidgetFromArea("e10s-button");
|
||||||
|
|
||||||
let {synthesizeDragStart, synthesizeDrop} = ChromeUtils;
|
let {synthesizeDragStart, synthesizeDrop} = ChromeUtils;
|
||||||
|
|
||||||
const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||||
|
@ -109,16 +109,7 @@ function addTab(url) {
|
|||||||
let browser = tab.linkedBrowser;
|
let browser = tab.linkedBrowser;
|
||||||
|
|
||||||
info("Loading the helper frame script " + FRAME_SCRIPT_URL);
|
info("Loading the helper frame script " + FRAME_SCRIPT_URL);
|
||||||
// Bug 687194 - Mochitest registers its chrome URLs after browser
|
browser.messageManager.loadFrameScript(FRAME_SCRIPT_URL, false);
|
||||||
// initialization, so the content processes don't pick them up. That
|
|
||||||
// means we can't load our frame script from its chrome URI, because
|
|
||||||
// the content process won't be able to find it.
|
|
||||||
// Instead, we resolve the chrome URI for the script to a file URI, which
|
|
||||||
// we can then pass to the content process, which it is able to find.
|
|
||||||
let registry = Cc['@mozilla.org/chrome/chrome-registry;1']
|
|
||||||
.getService(Ci.nsIChromeRegistry);
|
|
||||||
let fileURI = registry.convertChromeURL(Services.io.newURI(FRAME_SCRIPT_URL, null, null)).spec;
|
|
||||||
browser.messageManager.loadFrameScript(fileURI, false);
|
|
||||||
|
|
||||||
browser.addEventListener("load", function onload() {
|
browser.addEventListener("load", function onload() {
|
||||||
browser.removeEventListener("load", onload, true);
|
browser.removeEventListener("load", onload, true);
|
||||||
|
@ -4674,6 +4674,7 @@ var Utils = {
|
|||||||
case "Mixed Content Message":
|
case "Mixed Content Message":
|
||||||
case "CSP":
|
case "CSP":
|
||||||
case "Invalid HSTS Headers":
|
case "Invalid HSTS Headers":
|
||||||
|
case "Invalid HPKP Headers":
|
||||||
case "Insecure Password Field":
|
case "Insecure Password Field":
|
||||||
case "SSL":
|
case "SSL":
|
||||||
case "CORS":
|
case "CORS":
|
||||||
|
@ -1451,7 +1451,7 @@ Tab.prototype = {
|
|||||||
|
|
||||||
browser.setAttribute("type", "content-targetable");
|
browser.setAttribute("type", "content-targetable");
|
||||||
|
|
||||||
let useRemote = Services.appinfo.browserTabsRemote;
|
let useRemote = Services.appinfo.browserTabsRemoteAutostart;
|
||||||
let useLocal = Util.isLocalScheme(aURI);
|
let useLocal = Util.isLocalScheme(aURI);
|
||||||
browser.setAttribute("remote", (!useLocal && useRemote) ? "true" : "false");
|
browser.setAttribute("remote", (!useLocal && useRemote) ? "true" : "false");
|
||||||
|
|
||||||
|
@ -94,8 +94,6 @@ pref("toolkit.browser.contentViewExpire", 3000);
|
|||||||
pref("toolkit.defaultChromeURI", "chrome://browser/content/browser.xul");
|
pref("toolkit.defaultChromeURI", "chrome://browser/content/browser.xul");
|
||||||
pref("browser.chromeURL", "chrome://browser/content/");
|
pref("browser.chromeURL", "chrome://browser/content/");
|
||||||
|
|
||||||
pref("browser.tabs.remote", false);
|
|
||||||
|
|
||||||
// Telemetry
|
// Telemetry
|
||||||
#ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
|
#ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
|
||||||
pref("toolkit.telemetry.enabledPreRelease", true);
|
pref("toolkit.telemetry.enabledPreRelease", true);
|
||||||
|
18
build/pgo/certs/alternateroot.ca
Normal file
18
build/pgo/certs/alternateroot.ca
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIC2jCCAcKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDExtBbHRl
|
||||||
|
cm5hdGUgVHJ1c3RlZCBBdXRob3JpdHkwHhcNMTQwOTI1MjEyMTU0WhcNMjQwOTI1
|
||||||
|
MjEyMTU0WjAmMSQwIgYDVQQDExtBbHRlcm5hdGUgVHJ1c3RlZCBBdXRob3JpdHkw
|
||||||
|
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBT+BwAhO52IWgSIdZZifU
|
||||||
|
9LHOs3IR/+8DCC0WP5d/OuyKlZ6Rqd0tsd3i7durhQyjHSbLf2lJStcnFjcVEbEn
|
||||||
|
NI76RuvlN8xLLn5eV+2Ayr4cZYKztudwRmw+DV/iYAiMSy0hs7m3ssfX7qpoi1aN
|
||||||
|
RjUanwU0VTCPQhF1bEKAC2du+C5Z8e92zN5t87w7bYr7lt+m8197XliXEu+0s9Rg
|
||||||
|
nGwGaZ296BIRz6NOoJYTa43n06LU1I1+Z4d6lPdzUFrSR0GBaMhUSurUBtOin3yW
|
||||||
|
iMhg1VHX/KwqGc4als5GyCVXy8HGrA/0zQPOhetxrlhEVAdK/xBt7CZvByj1Rcc7
|
||||||
|
AgMBAAGjEzARMA8GA1UdEwQIMAYBAf8CAQAwDQYJKoZIhvcNAQELBQADggEBAJq/
|
||||||
|
hogSRqzPWTwX4wTn/DVSNdWwFLv53qep9YrSMJ8ZsfbfK9Es4VP4dBLRQAVMJ0Z5
|
||||||
|
mW1I6d/n0KayTanuUBvemYdxPi/qQNSs8UJcllqdhqWzmzAg6a0LxrMnEeKzPBPD
|
||||||
|
6q8PwQ7tYP+B4sBN9tnnsnyPgti9ZiNZn5FwXZliHXseQ7FE9/SqHlLw5LXW3YtK
|
||||||
|
juti6RmuV6fq3j+D4oeC5vb1mKgIyoTqGN6ze57v8RHi+pQ8Q+kmoUn/L3Z2YmFe
|
||||||
|
4SKN/4WoyXr8TdejpThGOCGCAd3565s5gOx5QfSQX11P8NZKO8hcN0tme3VzmGpH
|
||||||
|
K0Z/6MTmdpNaTwQ6odk=
|
||||||
|
-----END CERTIFICATE-----
|
Binary file not shown.
Binary file not shown.
@ -222,3 +222,8 @@ http://example.fi:80 privileged
|
|||||||
https://marketplace.firefox.com:443 privileged
|
https://marketplace.firefox.com:443 privileged
|
||||||
https://marketplace-dev.allizom.org:443 privileged
|
https://marketplace-dev.allizom.org:443 privileged
|
||||||
https://marketplace.allizom.org:443 privileged
|
https://marketplace.allizom.org:443 privileged
|
||||||
|
|
||||||
|
# Host for HPKP
|
||||||
|
https://include-subdomains.pinning-dynamic.example.com:443 privileged,cert=dynamicPinningGood
|
||||||
|
https://bad.include-subdomains.pinning-dynamic.example.com:443 privileged,cert=dynamicPinningBad
|
||||||
|
|
||||||
|
@ -21,10 +21,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
addLoadEvent(function() {
|
addLoadEvent(function() {
|
||||||
// We don't want to set browser.tabs.remote to true, but still have CPOWs enabled.
|
window.open("cpows_parent.xul", "", "chrome");
|
||||||
SpecialPowers.pushPrefEnv({"set": [["dom.ipc.cpows.force-enabled", true]]}, function() {
|
|
||||||
window.open("cpows_parent.xul", "", "chrome");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
]]></script>
|
]]></script>
|
||||||
</window>
|
</window>
|
||||||
|
@ -162,15 +162,9 @@ public:
|
|||||||
virtual void MetadataLoaded(const MediaInfo* aInfo,
|
virtual void MetadataLoaded(const MediaInfo* aInfo,
|
||||||
const MetadataTags* aTags) MOZ_FINAL MOZ_OVERRIDE;
|
const MetadataTags* aTags) MOZ_FINAL MOZ_OVERRIDE;
|
||||||
|
|
||||||
// Called by the video decoder object, on the main thread,
|
// Called by the decoder object, on the main thread,
|
||||||
// when it has read the first frame of the video
|
// when it has read the first frame of the video or audio.
|
||||||
// aResourceFullyLoaded should be true if the resource has been
|
virtual void FirstFrameLoaded() MOZ_FINAL MOZ_OVERRIDE;
|
||||||
// fully loaded and the caller will call ResourceLoaded next.
|
|
||||||
virtual void FirstFrameLoaded(bool aResourceFullyLoaded) MOZ_FINAL MOZ_OVERRIDE;
|
|
||||||
|
|
||||||
// Called by the video decoder object, on the main thread,
|
|
||||||
// when the resource has completed downloading.
|
|
||||||
virtual void ResourceLoaded() MOZ_FINAL MOZ_OVERRIDE;
|
|
||||||
|
|
||||||
// Called by the video decoder object, on the main thread,
|
// Called by the video decoder object, on the main thread,
|
||||||
// when the resource has a network error during loading.
|
// when the resource has a network error during loading.
|
||||||
@ -210,6 +204,9 @@ public:
|
|||||||
// ongoing.
|
// ongoing.
|
||||||
virtual void DownloadResumed(bool aForceNetworkLoading = false) MOZ_FINAL MOZ_OVERRIDE;
|
virtual void DownloadResumed(bool aForceNetworkLoading = false) MOZ_FINAL MOZ_OVERRIDE;
|
||||||
|
|
||||||
|
// Called to indicate the download is progressing.
|
||||||
|
virtual void DownloadProgressed() MOZ_FINAL MOZ_OVERRIDE;
|
||||||
|
|
||||||
// Called by the media decoder to indicate that the download has stalled
|
// Called by the media decoder to indicate that the download has stalled
|
||||||
// (no data has arrived for a while).
|
// (no data has arrived for a while).
|
||||||
virtual void DownloadStalled() MOZ_FINAL MOZ_OVERRIDE;
|
virtual void DownloadStalled() MOZ_FINAL MOZ_OVERRIDE;
|
||||||
@ -238,10 +235,6 @@ public:
|
|||||||
// HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA.
|
// HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA.
|
||||||
virtual void UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatus aNextFrame) MOZ_FINAL MOZ_OVERRIDE;
|
virtual void UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatus aNextFrame) MOZ_FINAL MOZ_OVERRIDE;
|
||||||
|
|
||||||
// Use this method to change the mReadyState member, so required
|
|
||||||
// events can be fired.
|
|
||||||
void ChangeReadyState(nsMediaReadyState aState);
|
|
||||||
|
|
||||||
// Return true if we can activate autoplay assuming enough data has arrived.
|
// Return true if we can activate autoplay assuming enough data has arrived.
|
||||||
bool CanActivateAutoplay();
|
bool CanActivateAutoplay();
|
||||||
|
|
||||||
@ -540,7 +533,7 @@ public:
|
|||||||
|
|
||||||
already_AddRefed<Promise> SetMediaKeys(MediaKeys* mediaKeys,
|
already_AddRefed<Promise> SetMediaKeys(MediaKeys* mediaKeys,
|
||||||
ErrorResult& aRv);
|
ErrorResult& aRv);
|
||||||
|
|
||||||
MediaWaitingFor WaitingFor() const;
|
MediaWaitingFor WaitingFor() const;
|
||||||
|
|
||||||
mozilla::dom::EventHandlerNonNull* GetOnencrypted();
|
mozilla::dom::EventHandlerNonNull* GetOnencrypted();
|
||||||
@ -648,6 +641,17 @@ protected:
|
|||||||
nsCOMPtr<nsITimer> mTimer;
|
nsCOMPtr<nsITimer> mTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Use this method to change the mReadyState member, so required
|
||||||
|
* events can be fired.
|
||||||
|
*/
|
||||||
|
void ChangeReadyState(nsMediaReadyState aState);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this method to change the mNetworkState member, so required
|
||||||
|
* actions will be taken during the transition.
|
||||||
|
*/
|
||||||
|
void ChangeNetworkState(nsMediaNetworkState aState);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* These two methods are called by the WakeLockBoolWrapper when the wakelock
|
* These two methods are called by the WakeLockBoolWrapper when the wakelock
|
||||||
* has to be created or released.
|
* has to be created or released.
|
||||||
@ -933,7 +937,7 @@ protected:
|
|||||||
// desired, and we'll seek to the sync point (keyframe and/or start of the
|
// desired, and we'll seek to the sync point (keyframe and/or start of the
|
||||||
// next block of audio samples) preceeding seek target.
|
// next block of audio samples) preceeding seek target.
|
||||||
void Seek(double aTime, SeekTarget::Type aSeekType, ErrorResult& aRv);
|
void Seek(double aTime, SeekTarget::Type aSeekType, ErrorResult& aRv);
|
||||||
|
|
||||||
// Update the audio channel playing state
|
// Update the audio channel playing state
|
||||||
void UpdateAudioChannelPlayingState();
|
void UpdateAudioChannelPlayingState();
|
||||||
|
|
||||||
@ -1104,9 +1108,8 @@ protected:
|
|||||||
// Set to false when completed, or not yet started.
|
// Set to false when completed, or not yet started.
|
||||||
bool mBegun;
|
bool mBegun;
|
||||||
|
|
||||||
// True when the decoder has loaded enough data to display the
|
// True if loadeddata has been fired.
|
||||||
// first frame of the content.
|
bool mLoadedDataFired;
|
||||||
bool mLoadedFirstFrame;
|
|
||||||
|
|
||||||
// Indicates whether current playback is a result of user action
|
// Indicates whether current playback is a result of user action
|
||||||
// (ie. calling of the Play method), or automatic playback due to
|
// (ie. calling of the Play method), or automatic playback due to
|
||||||
|
@ -618,6 +618,11 @@ void HTMLMediaElement::ShutdownDecoder()
|
|||||||
{
|
{
|
||||||
RemoveMediaElementFromURITable();
|
RemoveMediaElementFromURITable();
|
||||||
NS_ASSERTION(mDecoder, "Must have decoder to shut down");
|
NS_ASSERTION(mDecoder, "Must have decoder to shut down");
|
||||||
|
// TODO: This should be handled by ChangeNetworkState() so we have only one
|
||||||
|
// place to call StopProgress().
|
||||||
|
if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING) {
|
||||||
|
mDecoder->StopProgress();
|
||||||
|
}
|
||||||
mDecoder->Shutdown();
|
mDecoder->Shutdown();
|
||||||
mDecoder = nullptr;
|
mDecoder = nullptr;
|
||||||
}
|
}
|
||||||
@ -661,7 +666,7 @@ void HTMLMediaElement::AbortExistingLoads()
|
|||||||
}
|
}
|
||||||
|
|
||||||
mError = nullptr;
|
mError = nullptr;
|
||||||
mLoadedFirstFrame = false;
|
mLoadedDataFired = false;
|
||||||
mAutoplaying = true;
|
mAutoplaying = true;
|
||||||
mIsLoadingFromSourceChildren = false;
|
mIsLoadingFromSourceChildren = false;
|
||||||
mSuspendedAfterFirstFrame = false;
|
mSuspendedAfterFirstFrame = false;
|
||||||
@ -674,8 +679,8 @@ void HTMLMediaElement::AbortExistingLoads()
|
|||||||
mTags = nullptr;
|
mTags = nullptr;
|
||||||
|
|
||||||
if (mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
|
if (mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
|
||||||
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
|
|
||||||
NS_ASSERTION(!mDecoder && !mSrcStream, "How did someone setup a new stream/decoder already?");
|
NS_ASSERTION(!mDecoder && !mSrcStream, "How did someone setup a new stream/decoder already?");
|
||||||
|
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY);
|
||||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING);
|
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING);
|
||||||
mPaused = true;
|
mPaused = true;
|
||||||
|
|
||||||
@ -701,7 +706,7 @@ void HTMLMediaElement::NoSupportedMediaSourceError()
|
|||||||
NS_ASSERTION(mDelayingLoadEvent, "Load event not delayed during source selection?");
|
NS_ASSERTION(mDelayingLoadEvent, "Load event not delayed during source selection?");
|
||||||
|
|
||||||
mError = new MediaError(this, nsIDOMMediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
|
mError = new MediaError(this, nsIDOMMediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
|
||||||
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE;
|
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE);
|
||||||
DispatchAsyncEvent(NS_LITERAL_STRING("error"));
|
DispatchAsyncEvent(NS_LITERAL_STRING("error"));
|
||||||
// This clears mDelayingLoadEvent, so AddRemoveSelfReference will be called
|
// This clears mDelayingLoadEvent, so AddRemoveSelfReference will be called
|
||||||
ChangeDelayLoadStatus(false);
|
ChangeDelayLoadStatus(false);
|
||||||
@ -745,7 +750,7 @@ void HTMLMediaElement::RunInStableState(nsIRunnable* aRunnable)
|
|||||||
void HTMLMediaElement::QueueLoadFromSourceTask()
|
void HTMLMediaElement::QueueLoadFromSourceTask()
|
||||||
{
|
{
|
||||||
ChangeDelayLoadStatus(true);
|
ChangeDelayLoadStatus(true);
|
||||||
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
|
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_LOADING);
|
||||||
RunInStableState(
|
RunInStableState(
|
||||||
NS_NewRunnableMethod(this, &HTMLMediaElement::LoadFromSourceChildren));
|
NS_NewRunnableMethod(this, &HTMLMediaElement::LoadFromSourceChildren));
|
||||||
}
|
}
|
||||||
@ -756,7 +761,7 @@ void HTMLMediaElement::QueueSelectResourceTask()
|
|||||||
if (mHaveQueuedSelectResource)
|
if (mHaveQueuedSelectResource)
|
||||||
return;
|
return;
|
||||||
mHaveQueuedSelectResource = true;
|
mHaveQueuedSelectResource = true;
|
||||||
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE;
|
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE);
|
||||||
RunInStableState(
|
RunInStableState(
|
||||||
NS_NewRunnableMethod(this, &HTMLMediaElement::SelectResourceWrapper));
|
NS_NewRunnableMethod(this, &HTMLMediaElement::SelectResourceWrapper));
|
||||||
}
|
}
|
||||||
@ -817,7 +822,7 @@ void HTMLMediaElement::SelectResource()
|
|||||||
!HasSourceChildren(this)) {
|
!HasSourceChildren(this)) {
|
||||||
// The media element has neither a src attribute nor any source
|
// The media element has neither a src attribute nor any source
|
||||||
// element children, abort the load.
|
// element children, abort the load.
|
||||||
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
|
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY);
|
||||||
// This clears mDelayingLoadEvent, so AddRemoveSelfReference will be called
|
// This clears mDelayingLoadEvent, so AddRemoveSelfReference will be called
|
||||||
ChangeDelayLoadStatus(false);
|
ChangeDelayLoadStatus(false);
|
||||||
return;
|
return;
|
||||||
@ -825,7 +830,7 @@ void HTMLMediaElement::SelectResource()
|
|||||||
|
|
||||||
ChangeDelayLoadStatus(true);
|
ChangeDelayLoadStatus(true);
|
||||||
|
|
||||||
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
|
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_LOADING);
|
||||||
// Load event was delayed, and still is, so no need to call
|
// Load event was delayed, and still is, so no need to call
|
||||||
// AddRemoveSelfReference, since it must still be held
|
// AddRemoveSelfReference, since it must still be held
|
||||||
DispatchAsyncEvent(NS_LITERAL_STRING("loadstart"));
|
DispatchAsyncEvent(NS_LITERAL_STRING("loadstart"));
|
||||||
@ -921,7 +926,7 @@ void HTMLMediaElement::LoadFromSourceChildren()
|
|||||||
// Exhausted candidates, wait for more candidates to be appended to
|
// Exhausted candidates, wait for more candidates to be appended to
|
||||||
// the media element.
|
// the media element.
|
||||||
mLoadWaitStatus = WAITING_FOR_SOURCE;
|
mLoadWaitStatus = WAITING_FOR_SOURCE;
|
||||||
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE;
|
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE);
|
||||||
ChangeDelayLoadStatus(false);
|
ChangeDelayLoadStatus(false);
|
||||||
ReportLoadError("MediaLoadExhaustedCandidates");
|
ReportLoadError("MediaLoadExhaustedCandidates");
|
||||||
return;
|
return;
|
||||||
@ -990,8 +995,7 @@ void HTMLMediaElement::LoadFromSourceChildren()
|
|||||||
void HTMLMediaElement::SuspendLoad()
|
void HTMLMediaElement::SuspendLoad()
|
||||||
{
|
{
|
||||||
mSuspendedForPreloadNone = true;
|
mSuspendedForPreloadNone = true;
|
||||||
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
|
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
|
||||||
DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
|
|
||||||
ChangeDelayLoadStatus(false);
|
ChangeDelayLoadStatus(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1002,7 +1006,7 @@ void HTMLMediaElement::ResumeLoad(PreloadAction aAction)
|
|||||||
mSuspendedForPreloadNone = false;
|
mSuspendedForPreloadNone = false;
|
||||||
mPreloadAction = aAction;
|
mPreloadAction = aAction;
|
||||||
ChangeDelayLoadStatus(true);
|
ChangeDelayLoadStatus(true);
|
||||||
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
|
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_LOADING);
|
||||||
if (!mIsLoadingFromSourceChildren) {
|
if (!mIsLoadingFromSourceChildren) {
|
||||||
// We were loading from the element's src attribute.
|
// We were loading from the element's src attribute.
|
||||||
if (NS_FAILED(LoadResource())) {
|
if (NS_FAILED(LoadResource())) {
|
||||||
@ -2027,7 +2031,7 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
|
|||||||
mPlayed(new TimeRanges),
|
mPlayed(new TimeRanges),
|
||||||
mCurrentPlayRangeStart(-1.0),
|
mCurrentPlayRangeStart(-1.0),
|
||||||
mBegun(false),
|
mBegun(false),
|
||||||
mLoadedFirstFrame(false),
|
mLoadedDataFired(false),
|
||||||
mAutoplaying(true),
|
mAutoplaying(true),
|
||||||
mAutoplayEnabled(true),
|
mAutoplayEnabled(true),
|
||||||
mPaused(true),
|
mPaused(true),
|
||||||
@ -2154,11 +2158,10 @@ void HTMLMediaElement::SetPlayedOrSeeked(bool aValue)
|
|||||||
void
|
void
|
||||||
HTMLMediaElement::ResetConnectionState()
|
HTMLMediaElement::ResetConnectionState()
|
||||||
{
|
{
|
||||||
mBegun = false;
|
|
||||||
SetCurrentTime(0);
|
SetCurrentTime(0);
|
||||||
FireTimeUpdate(false);
|
FireTimeUpdate(false);
|
||||||
DispatchAsyncEvent(NS_LITERAL_STRING("ended"));
|
DispatchAsyncEvent(NS_LITERAL_STRING("ended"));
|
||||||
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
|
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY);
|
||||||
AddRemoveSelfReference();
|
AddRemoveSelfReference();
|
||||||
ChangeDelayLoadStatus(false);
|
ChangeDelayLoadStatus(false);
|
||||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING);
|
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING);
|
||||||
@ -2646,7 +2649,7 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder,
|
|||||||
nsIStreamListener** aListener,
|
nsIStreamListener** aListener,
|
||||||
MediaDecoder* aCloneDonor)
|
MediaDecoder* aCloneDonor)
|
||||||
{
|
{
|
||||||
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
|
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_LOADING);
|
||||||
|
|
||||||
// Force a same-origin check before allowing events for this media resource.
|
// Force a same-origin check before allowing events for this media resource.
|
||||||
mMediaSecurityVerified = false;
|
mMediaSecurityVerified = false;
|
||||||
@ -2667,6 +2670,8 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder,
|
|||||||
mDecoder->SetVolume(mMuted ? 0.0 : mVolume);
|
mDecoder->SetVolume(mMuted ? 0.0 : mVolume);
|
||||||
mDecoder->SetPreservesPitch(mPreservesPitch);
|
mDecoder->SetPreservesPitch(mPreservesPitch);
|
||||||
mDecoder->SetPlaybackRate(mPlaybackRate);
|
mDecoder->SetPlaybackRate(mPlaybackRate);
|
||||||
|
// Start progress timer for we are in NETWORK_LOADING.
|
||||||
|
mDecoder->StartProgress();
|
||||||
|
|
||||||
#ifdef MOZ_EME
|
#ifdef MOZ_EME
|
||||||
if (mMediaKeys) {
|
if (mMediaKeys) {
|
||||||
@ -2718,7 +2723,6 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder,
|
|||||||
NS_ASSERTION(NS_SUCCEEDED(rv) == (MediaElementTableCount(this, mLoadingSrc) == 1),
|
NS_ASSERTION(NS_SUCCEEDED(rv) == (MediaElementTableCount(this, mLoadingSrc) == 1),
|
||||||
"Media element should have single table entry if decode initialized");
|
"Media element should have single table entry if decode initialized");
|
||||||
|
|
||||||
mBegun = true;
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2776,7 +2780,7 @@ public:
|
|||||||
mHaveCurrentData = true;
|
mHaveCurrentData = true;
|
||||||
if (mElement) {
|
if (mElement) {
|
||||||
nsRefPtr<HTMLMediaElement> deathGrip = mElement;
|
nsRefPtr<HTMLMediaElement> deathGrip = mElement;
|
||||||
mElement->FirstFrameLoaded(false);
|
mElement->FirstFrameLoaded();
|
||||||
}
|
}
|
||||||
UpdateReadyStateForData();
|
UpdateReadyStateForData();
|
||||||
DoNotifyOutput();
|
DoNotifyOutput();
|
||||||
@ -2867,11 +2871,9 @@ void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream)
|
|||||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
|
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
|
||||||
DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
|
DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
|
||||||
DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata"));
|
DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata"));
|
||||||
DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
|
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
|
||||||
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
|
|
||||||
AddRemoveSelfReference();
|
AddRemoveSelfReference();
|
||||||
// FirstFrameLoaded(false) will be called when the stream has current data,
|
// FirstFrameLoaded() will be called when the stream has current data.
|
||||||
// to complete the setup by entering the HAVE_CURRENT_DATA state.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HTMLMediaElement::EndSrcMediaStreamPlayback()
|
void HTMLMediaElement::EndSrcMediaStreamPlayback()
|
||||||
@ -2921,6 +2923,7 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
|
|||||||
{
|
{
|
||||||
mHasAudio = aInfo->HasAudio();
|
mHasAudio = aInfo->HasAudio();
|
||||||
mTags = aTags;
|
mTags = aTags;
|
||||||
|
mLoadedDataFired = false;
|
||||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
|
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
|
||||||
DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
|
DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
|
||||||
DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata"));
|
DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata"));
|
||||||
@ -2940,58 +2943,20 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HTMLMediaElement::FirstFrameLoaded(bool aResourceFullyLoaded)
|
void HTMLMediaElement::FirstFrameLoaded()
|
||||||
{
|
{
|
||||||
ChangeReadyState(aResourceFullyLoaded ?
|
|
||||||
nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA :
|
|
||||||
nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
|
|
||||||
ChangeDelayLoadStatus(false);
|
|
||||||
|
|
||||||
NS_ASSERTION(!mSuspendedAfterFirstFrame, "Should not have already suspended");
|
NS_ASSERTION(!mSuspendedAfterFirstFrame, "Should not have already suspended");
|
||||||
|
|
||||||
|
ChangeDelayLoadStatus(false);
|
||||||
|
|
||||||
if (mDecoder && mAllowSuspendAfterFirstFrame && mPaused &&
|
if (mDecoder && mAllowSuspendAfterFirstFrame && mPaused &&
|
||||||
!aResourceFullyLoaded &&
|
|
||||||
!HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) &&
|
!HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) &&
|
||||||
mPreloadAction == HTMLMediaElement::PRELOAD_METADATA) {
|
mPreloadAction == HTMLMediaElement::PRELOAD_METADATA) {
|
||||||
mSuspendedAfterFirstFrame = true;
|
mSuspendedAfterFirstFrame = true;
|
||||||
mDecoder->Suspend();
|
mDecoder->Suspend();
|
||||||
} else if (mLoadedFirstFrame &&
|
|
||||||
mDownloadSuspendedByCache &&
|
|
||||||
mDecoder &&
|
|
||||||
!mDecoder->IsEnded()) {
|
|
||||||
// We've already loaded the first frame, and the decoder has signalled
|
|
||||||
// that the download has been suspended by the media cache. So move
|
|
||||||
// readyState into HAVE_ENOUGH_DATA, in case there's script waiting
|
|
||||||
// for a "canplaythrough" event; without this forced transition, we will
|
|
||||||
// never fire the "canplaythrough" event if the media cache is so small
|
|
||||||
// that the download was suspended before the first frame was loaded.
|
|
||||||
// Don't force this transition if the decoder is in ended state; the
|
|
||||||
// readyState should remain at HAVE_CURRENT_DATA in this case.
|
|
||||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HTMLMediaElement::ResourceLoaded()
|
|
||||||
{
|
|
||||||
NS_ASSERTION(!mSrcStream, "Don't call this for streams");
|
|
||||||
|
|
||||||
mBegun = false;
|
|
||||||
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
|
|
||||||
AddRemoveSelfReference();
|
|
||||||
if (mReadyState >= nsIDOMHTMLMediaElement::HAVE_METADATA) {
|
|
||||||
// MediaStream sources are put into HAVE_CURRENT_DATA state here on setup. If the
|
|
||||||
// stream is not blocked, we will receive a notification that will put it
|
|
||||||
// into HAVE_ENOUGH_DATA state.
|
|
||||||
ChangeReadyState(mSrcStream ? nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA
|
|
||||||
: nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
|
|
||||||
}
|
|
||||||
// Ensure a progress event is dispatched at the end of download.
|
|
||||||
DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
|
|
||||||
// The download has stopped.
|
|
||||||
DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTMLMediaElement::NetworkError()
|
void HTMLMediaElement::NetworkError()
|
||||||
{
|
{
|
||||||
Error(nsIDOMMediaError::MEDIA_ERR_NETWORK);
|
Error(nsIDOMMediaError::MEDIA_ERR_NETWORK);
|
||||||
@ -3033,13 +2998,12 @@ void HTMLMediaElement::Error(uint16_t aErrorCode)
|
|||||||
aErrorCode == nsIDOMMediaError::MEDIA_ERR_ABORTED,
|
aErrorCode == nsIDOMMediaError::MEDIA_ERR_ABORTED,
|
||||||
"Only use nsIDOMMediaError codes!");
|
"Only use nsIDOMMediaError codes!");
|
||||||
mError = new MediaError(this, aErrorCode);
|
mError = new MediaError(this, aErrorCode);
|
||||||
mBegun = false;
|
|
||||||
DispatchAsyncEvent(NS_LITERAL_STRING("error"));
|
DispatchAsyncEvent(NS_LITERAL_STRING("error"));
|
||||||
if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) {
|
if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) {
|
||||||
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
|
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY);
|
||||||
DispatchAsyncEvent(NS_LITERAL_STRING("emptied"));
|
DispatchAsyncEvent(NS_LITERAL_STRING("emptied"));
|
||||||
} else {
|
} else {
|
||||||
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
|
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
|
||||||
}
|
}
|
||||||
AddRemoveSelfReference();
|
AddRemoveSelfReference();
|
||||||
ChangeDelayLoadStatus(false);
|
ChangeDelayLoadStatus(false);
|
||||||
@ -3083,7 +3047,6 @@ void HTMLMediaElement::SeekStarted()
|
|||||||
if(mPlayingThroughTheAudioChannel) {
|
if(mPlayingThroughTheAudioChannel) {
|
||||||
mPlayingThroughTheAudioChannelBeforeSeek = true;
|
mPlayingThroughTheAudioChannelBeforeSeek = true;
|
||||||
}
|
}
|
||||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
|
|
||||||
FireTimeUpdate(false);
|
FireTimeUpdate(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3107,29 +3070,32 @@ void HTMLMediaElement::SeekCompleted()
|
|||||||
void HTMLMediaElement::NotifySuspendedByCache(bool aIsSuspended)
|
void HTMLMediaElement::NotifySuspendedByCache(bool aIsSuspended)
|
||||||
{
|
{
|
||||||
mDownloadSuspendedByCache = aIsSuspended;
|
mDownloadSuspendedByCache = aIsSuspended;
|
||||||
// If this is an autoplay element, we may need to kick off its autoplaying
|
|
||||||
// now so we consume data and hopefully free up cache space.
|
|
||||||
CheckAutoplayDataReady();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HTMLMediaElement::DownloadSuspended()
|
void HTMLMediaElement::DownloadSuspended()
|
||||||
{
|
{
|
||||||
DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
|
DownloadProgressed();
|
||||||
if (mBegun) {
|
if (mBegun) {
|
||||||
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
|
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
|
||||||
AddRemoveSelfReference();
|
AddRemoveSelfReference();
|
||||||
DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HTMLMediaElement::DownloadResumed(bool aForceNetworkLoading)
|
void HTMLMediaElement::DownloadResumed(bool aForceNetworkLoading)
|
||||||
{
|
{
|
||||||
if (mBegun || aForceNetworkLoading) {
|
if (mBegun || aForceNetworkLoading) {
|
||||||
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
|
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_LOADING);
|
||||||
AddRemoveSelfReference();
|
AddRemoveSelfReference();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HTMLMediaElement::DownloadProgressed()
|
||||||
|
{
|
||||||
|
if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING) {
|
||||||
|
DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void HTMLMediaElement::DownloadStalled()
|
void HTMLMediaElement::DownloadStalled()
|
||||||
{
|
{
|
||||||
if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING) {
|
if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING) {
|
||||||
@ -3146,8 +3112,7 @@ void HTMLMediaElement::UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatu
|
|||||||
{
|
{
|
||||||
if (mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
|
if (mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
|
||||||
// aNextFrame might have a next frame because the decoder can advance
|
// aNextFrame might have a next frame because the decoder can advance
|
||||||
// on its own thread before ResourceLoaded or MetadataLoaded gets
|
// on its own thread before MetadataLoaded gets a chance to run.
|
||||||
// a chance to run.
|
|
||||||
// The arrival of more data can't change us out of this readyState.
|
// The arrival of more data can't change us out of this readyState.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -3160,17 +3125,21 @@ void HTMLMediaElement::UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatu
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mReadyState > nsIDOMHTMLMediaElement::HAVE_METADATA &&
|
if (aNextFrame == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING) {
|
||||||
mDownloadSuspendedByCache &&
|
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
|
||||||
mDecoder &&
|
return;
|
||||||
!mDecoder->IsEnded()) {
|
}
|
||||||
// The decoder has signalled that the download has been suspended by the
|
|
||||||
|
if (mDownloadSuspendedByCache && mDecoder && !mDecoder->IsEnded()) {
|
||||||
|
// The decoder has signaled that the download has been suspended by the
|
||||||
// media cache. So move readyState into HAVE_ENOUGH_DATA, in case there's
|
// media cache. So move readyState into HAVE_ENOUGH_DATA, in case there's
|
||||||
// script waiting for a "canplaythrough" event; without this forced
|
// script waiting for a "canplaythrough" event; without this forced
|
||||||
// transition, we will never fire the "canplaythrough" event if the
|
// transition, we will never fire the "canplaythrough" event if the
|
||||||
// media cache is too small, and scripts are bound to fail. Don't force
|
// media cache is too small, and scripts are bound to fail. Don't force
|
||||||
// this transition if the decoder is in ended state; the readyState
|
// this transition if the decoder is in ended state; the readyState
|
||||||
// should remain at HAVE_CURRENT_DATA in this case.
|
// should remain at HAVE_CURRENT_DATA in this case.
|
||||||
|
// Note that this state transition includes the case where we finished
|
||||||
|
// downloaded the whole data stream.
|
||||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
|
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -3210,7 +3179,7 @@ void HTMLMediaElement::UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatu
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef PR_LOGGING
|
#ifdef PR_LOGGING
|
||||||
static const char* gReadyStateToString[] = {
|
static const char* const gReadyStateToString[] = {
|
||||||
"HAVE_NOTHING",
|
"HAVE_NOTHING",
|
||||||
"HAVE_METADATA",
|
"HAVE_METADATA",
|
||||||
"HAVE_CURRENT_DATA",
|
"HAVE_CURRENT_DATA",
|
||||||
@ -3235,16 +3204,15 @@ void HTMLMediaElement::ChangeReadyState(nsMediaReadyState aState)
|
|||||||
|
|
||||||
// Handle raising of "waiting" event during seek (see 4.8.10.9)
|
// Handle raising of "waiting" event during seek (see 4.8.10.9)
|
||||||
if (mPlayingBeforeSeek &&
|
if (mPlayingBeforeSeek &&
|
||||||
oldState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) {
|
mReadyState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) {
|
||||||
DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
|
DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldState < nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA &&
|
if (oldState < nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA &&
|
||||||
mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA &&
|
mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA &&
|
||||||
!mLoadedFirstFrame)
|
!mLoadedDataFired) {
|
||||||
{
|
|
||||||
DispatchAsyncEvent(NS_LITERAL_STRING("loadeddata"));
|
DispatchAsyncEvent(NS_LITERAL_STRING("loadeddata"));
|
||||||
mLoadedFirstFrame = true;
|
mLoadedDataFired = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mReadyState == nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA) {
|
if (mReadyState == nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA) {
|
||||||
@ -3270,6 +3238,50 @@ void HTMLMediaElement::ChangeReadyState(nsMediaReadyState aState)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef PR_LOGGING
|
||||||
|
static const char* const gNetworkStateToString[] = {
|
||||||
|
"EMPTY",
|
||||||
|
"IDLE",
|
||||||
|
"LOADING",
|
||||||
|
"NO_SOURCE"
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void HTMLMediaElement::ChangeNetworkState(nsMediaNetworkState aState)
|
||||||
|
{
|
||||||
|
if (mNetworkState == aState) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsMediaNetworkState oldState = mNetworkState;
|
||||||
|
mNetworkState = aState;
|
||||||
|
LOG(PR_LOG_DEBUG, ("%p Network state changed to %s", this, gNetworkStateToString[aState]));
|
||||||
|
|
||||||
|
// TODO: |mBegun| reflects the download status. We should be able to remove
|
||||||
|
// it and check |mNetworkState| only.
|
||||||
|
|
||||||
|
if (oldState == nsIDOMHTMLMediaElement::NETWORK_LOADING) {
|
||||||
|
// Reset |mBegun| since we're not downloading anymore.
|
||||||
|
mBegun = false;
|
||||||
|
if (mDecoder) {
|
||||||
|
// Stop progress notification when exiting NETWORK_LOADING.
|
||||||
|
mDecoder->StopProgress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING) {
|
||||||
|
// Download is begun.
|
||||||
|
mBegun = true;
|
||||||
|
if (mDecoder) {
|
||||||
|
// Start progress notification when entering NETWORK_LOADING.
|
||||||
|
mDecoder->StartProgress();
|
||||||
|
}
|
||||||
|
} else if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_IDLE && !mError) {
|
||||||
|
// Fire 'suspend' event when entering NETWORK_IDLE and no error presented.
|
||||||
|
DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool HTMLMediaElement::CanActivateAutoplay()
|
bool HTMLMediaElement::CanActivateAutoplay()
|
||||||
{
|
{
|
||||||
// For stream inputs, we activate autoplay on HAVE_CURRENT_DATA because
|
// For stream inputs, we activate autoplay on HAVE_CURRENT_DATA because
|
||||||
@ -3278,8 +3290,7 @@ bool HTMLMediaElement::CanActivateAutoplay()
|
|||||||
return !mPausedForInactiveDocumentOrChannel &&
|
return !mPausedForInactiveDocumentOrChannel &&
|
||||||
mAutoplaying &&
|
mAutoplaying &&
|
||||||
mPaused &&
|
mPaused &&
|
||||||
(mDownloadSuspendedByCache ||
|
((mDecoder && mReadyState >= nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) ||
|
||||||
(mDecoder && mReadyState >= nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) ||
|
|
||||||
(mSrcStream && mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA)) &&
|
(mSrcStream && mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA)) &&
|
||||||
HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) &&
|
HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) &&
|
||||||
mAutoplayEnabled &&
|
mAutoplayEnabled &&
|
||||||
@ -3398,7 +3409,7 @@ bool HTMLMediaElement::IsPlaybackEnded() const
|
|||||||
// TODO:
|
// TODO:
|
||||||
// the current playback position is equal to the effective end of the media resource.
|
// the current playback position is equal to the effective end of the media resource.
|
||||||
// See bug 449157.
|
// See bug 449157.
|
||||||
return mNetworkState >= nsIDOMHTMLMediaElement::HAVE_METADATA &&
|
return mReadyState >= nsIDOMHTMLMediaElement::HAVE_METADATA &&
|
||||||
mDecoder ? mDecoder->IsEnded() : false;
|
mDecoder ? mDecoder->IsEnded() : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,4 +9,4 @@
|
|||||||
# (Fuzzy necessary due to pixel-wise comparison of different JPEGs.
|
# (Fuzzy necessary due to pixel-wise comparison of different JPEGs.
|
||||||
# The vast majority of the fuzziness comes from Linux and WinXP.)
|
# The vast majority of the fuzziness comes from Linux and WinXP.)
|
||||||
fuzzy(1,149) == bug917595-iframe-1.html bug917595-1-ref.html
|
fuzzy(1,149) == bug917595-iframe-1.html bug917595-1-ref.html
|
||||||
fuzzy(2,640) == bug917595-exif-rotated.jpg bug917595-pixel-rotated.jpg
|
fuzzy(3,640) == bug917595-exif-rotated.jpg bug917595-pixel-rotated.jpg
|
||||||
|
@ -55,10 +55,18 @@ AudioSink::Init()
|
|||||||
nullptr,
|
nullptr,
|
||||||
MEDIA_THREAD_STACK_SIZE);
|
MEDIA_THREAD_STACK_SIZE);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
|
mStateMachine->OnAudioSinkError();
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &AudioSink::AudioLoop);
|
nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &AudioSink::AudioLoop);
|
||||||
return mThread->Dispatch(event, NS_DISPATCH_NORMAL);
|
rv = mThread->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
mStateMachine->OnAudioSinkError();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t
|
int64_t
|
||||||
@ -138,6 +146,8 @@ AudioSink::AudioLoop()
|
|||||||
|
|
||||||
if (NS_FAILED(InitializeAudioStream())) {
|
if (NS_FAILED(InitializeAudioStream())) {
|
||||||
NS_WARNING("Initializing AudioStream failed.");
|
NS_WARNING("Initializing AudioStream failed.");
|
||||||
|
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||||
|
mStateMachine->OnAudioSinkError();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,10 +207,13 @@ AudioSink::InitializeAudioStream()
|
|||||||
// circumstances, so we take care to drop the decoder monitor while
|
// circumstances, so we take care to drop the decoder monitor while
|
||||||
// initializing.
|
// initializing.
|
||||||
RefPtr<AudioStream> audioStream(new AudioStream());
|
RefPtr<AudioStream> audioStream(new AudioStream());
|
||||||
audioStream->Init(mInfo.mChannels, mInfo.mRate,
|
nsresult rv = audioStream->Init(mInfo.mChannels, mInfo.mRate,
|
||||||
mChannel, AudioStream::HighLatency);
|
mChannel, AudioStream::HighLatency);
|
||||||
// TODO: Check Init's return value and bail on error. Unfortunately this
|
if (NS_FAILED(rv)) {
|
||||||
// causes some tests to fail due to playback failing.
|
audioStream->Shutdown();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||||
mAudioStream = audioStream;
|
mAudioStream = audioStream;
|
||||||
UpdateStreamSettings();
|
UpdateStreamSettings();
|
||||||
|
@ -1857,7 +1857,11 @@ MediaCacheStream::NotifyDataEnded(nsresult aStatus)
|
|||||||
mResourceID = gMediaCache->AllocateResourceID();
|
mResourceID = gMediaCache->AllocateResourceID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// It is prudent to update channel/cache status before calling
|
||||||
|
// CacheClientNotifyDataEnded() which will read |mChannelEnded|.
|
||||||
FlushPartialBlockInternal(true);
|
FlushPartialBlockInternal(true);
|
||||||
|
mChannelEnded = true;
|
||||||
|
gMediaCache->QueueUpdate();
|
||||||
|
|
||||||
MediaCache::ResourceStreamIterator iter(mResourceID);
|
MediaCache::ResourceStreamIterator iter(mResourceID);
|
||||||
while (MediaCacheStream* stream = iter.Next()) {
|
while (MediaCacheStream* stream = iter.Next()) {
|
||||||
@ -1871,9 +1875,6 @@ MediaCacheStream::NotifyDataEnded(nsresult aStatus)
|
|||||||
stream->mClient->CacheClientNotifyDataEnded(aStatus);
|
stream->mClient->CacheClientNotifyDataEnded(aStatus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mChannelEnded = true;
|
|
||||||
gMediaCache->QueueUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -139,7 +139,6 @@ void MediaDecoder::SetDormantIfNecessary(bool aDormant)
|
|||||||
|
|
||||||
if(aDormant) {
|
if(aDormant) {
|
||||||
// enter dormant state
|
// enter dormant state
|
||||||
StopProgress();
|
|
||||||
DestroyDecodedStream();
|
DestroyDecodedStream();
|
||||||
mDecoderStateMachine->SetDormant(true);
|
mDecoderStateMachine->SetDormant(true);
|
||||||
|
|
||||||
@ -440,7 +439,6 @@ MediaDecoder::MediaDecoder() :
|
|||||||
mIsExitingDormant(false),
|
mIsExitingDormant(false),
|
||||||
mPlayState(PLAY_STATE_PAUSED),
|
mPlayState(PLAY_STATE_PAUSED),
|
||||||
mNextState(PLAY_STATE_PAUSED),
|
mNextState(PLAY_STATE_PAUSED),
|
||||||
mCalledResourceLoaded(false),
|
|
||||||
mIgnoreProgressData(false),
|
mIgnoreProgressData(false),
|
||||||
mInfiniteStream(false),
|
mInfiniteStream(false),
|
||||||
mOwner(nullptr),
|
mOwner(nullptr),
|
||||||
@ -501,7 +499,8 @@ void MediaDecoder::Shutdown()
|
|||||||
|
|
||||||
ChangeState(PLAY_STATE_SHUTDOWN);
|
ChangeState(PLAY_STATE_SHUTDOWN);
|
||||||
|
|
||||||
StopProgress();
|
// If we hit this assertion, there might be a bug in network state transition.
|
||||||
|
NS_ASSERTION(!mProgressTimer, "Progress timer should've been stopped.");
|
||||||
mOwner = nullptr;
|
mOwner = nullptr;
|
||||||
|
|
||||||
MediaShutdownManager::Instance().Unregister(this);
|
MediaShutdownManager::Instance().Unregister(this);
|
||||||
@ -724,20 +723,8 @@ void MediaDecoder::MetadataLoaded(MediaInfo* aInfo, MetadataTags* aTags)
|
|||||||
mOwner->MetadataLoaded(aInfo, aTags);
|
mOwner->MetadataLoaded(aInfo, aTags);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mCalledResourceLoaded) {
|
|
||||||
StartProgress();
|
|
||||||
} else if (mOwner) {
|
|
||||||
// Resource was loaded during metadata loading, when progress
|
|
||||||
// events are being ignored. Fire the final progress event.
|
|
||||||
mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only inform the element of FirstFrameLoaded if not doing a load() in order
|
|
||||||
// to fulfill a seek, otherwise we'll get multiple loadedfirstframe events.
|
|
||||||
bool notifyResourceIsLoaded = !mCalledResourceLoaded &&
|
|
||||||
IsDataCachedToEndOfResource();
|
|
||||||
if (mOwner) {
|
if (mOwner) {
|
||||||
mOwner->FirstFrameLoaded(notifyResourceIsLoaded);
|
mOwner->FirstFrameLoaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This can run cache callbacks.
|
// This can run cache callbacks.
|
||||||
@ -756,45 +743,11 @@ void MediaDecoder::MetadataLoaded(MediaInfo* aInfo, MetadataTags* aTags)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notifyResourceIsLoaded) {
|
|
||||||
ResourceLoaded();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run NotifySuspendedStatusChanged now to give us a chance to notice
|
// Run NotifySuspendedStatusChanged now to give us a chance to notice
|
||||||
// that autoplay should run.
|
// that autoplay should run.
|
||||||
NotifySuspendedStatusChanged();
|
NotifySuspendedStatusChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaDecoder::ResourceLoaded()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
|
|
||||||
// Don't handle ResourceLoaded if we are shutting down, or if
|
|
||||||
// we need to ignore progress data due to seeking (in the case
|
|
||||||
// that the seek results in reaching end of file, we get a bogus call
|
|
||||||
// to ResourceLoaded).
|
|
||||||
if (mShuttingDown)
|
|
||||||
return;
|
|
||||||
|
|
||||||
{
|
|
||||||
// If we are seeking or loading then the resource loaded notification we get
|
|
||||||
// should be ignored, since it represents the end of the seek request.
|
|
||||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
|
||||||
if (mIgnoreProgressData || mCalledResourceLoaded || mPlayState == PLAY_STATE_LOADING)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Progress(false);
|
|
||||||
|
|
||||||
mCalledResourceLoaded = true;
|
|
||||||
StopProgress();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the final progress event gets fired
|
|
||||||
if (mOwner) {
|
|
||||||
mOwner->ResourceLoaded();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MediaDecoder::ResetConnectionState()
|
void MediaDecoder::ResetConnectionState()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
@ -989,10 +942,8 @@ void MediaDecoder::UpdatePlaybackRate()
|
|||||||
void MediaDecoder::NotifySuspendedStatusChanged()
|
void MediaDecoder::NotifySuspendedStatusChanged()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
if (!mResource)
|
if (mResource && mOwner) {
|
||||||
return;
|
bool suspended = mResource->IsSuspendedByCache();
|
||||||
bool suspended = mResource->IsSuspendedByCache();
|
|
||||||
if (mOwner) {
|
|
||||||
mOwner->NotifySuspendedByCache(suspended);
|
mOwner->NotifySuspendedByCache(suspended);
|
||||||
UpdateReadyStateForData();
|
UpdateReadyStateForData();
|
||||||
}
|
}
|
||||||
@ -1005,7 +956,6 @@ void MediaDecoder::NotifyBytesDownloaded()
|
|||||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||||
UpdatePlaybackRate();
|
UpdatePlaybackRate();
|
||||||
}
|
}
|
||||||
UpdateReadyStateForData();
|
|
||||||
Progress(false);
|
Progress(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1029,12 +979,13 @@ void MediaDecoder::NotifyDownloadEnded(nsresult aStatus)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (NS_SUCCEEDED(aStatus)) {
|
if (NS_SUCCEEDED(aStatus)) {
|
||||||
ResourceLoaded();
|
// A final progress event will be fired by the MediaResource calling
|
||||||
}
|
// DownloadSuspended on the element.
|
||||||
else if (aStatus != NS_BASE_STREAM_CLOSED) {
|
// Also NotifySuspendedStatusChanged() will be called to update readyState
|
||||||
|
// if download ended with success.
|
||||||
|
} else if (aStatus != NS_BASE_STREAM_CLOSED) {
|
||||||
NetworkError();
|
NetworkError();
|
||||||
}
|
}
|
||||||
UpdateReadyStateForData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaDecoder::NotifyPrincipalChanged()
|
void MediaDecoder::NotifyPrincipalChanged()
|
||||||
@ -1566,6 +1517,7 @@ static void ProgressCallback(nsITimer* aTimer, void* aClosure)
|
|||||||
|
|
||||||
void MediaDecoder::Progress(bool aTimer)
|
void MediaDecoder::Progress(bool aTimer)
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
if (!mOwner)
|
if (!mOwner)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -1581,7 +1533,7 @@ void MediaDecoder::Progress(bool aTimer)
|
|||||||
now - mProgressTime >= TimeDuration::FromMilliseconds(PROGRESS_MS)) &&
|
now - mProgressTime >= TimeDuration::FromMilliseconds(PROGRESS_MS)) &&
|
||||||
!mDataTime.IsNull() &&
|
!mDataTime.IsNull() &&
|
||||||
now - mDataTime <= TimeDuration::FromMilliseconds(PROGRESS_MS)) {
|
now - mDataTime <= TimeDuration::FromMilliseconds(PROGRESS_MS)) {
|
||||||
mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
|
mOwner->DownloadProgressed();
|
||||||
mProgressTime = now;
|
mProgressTime = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1595,8 +1547,8 @@ void MediaDecoder::Progress(bool aTimer)
|
|||||||
|
|
||||||
nsresult MediaDecoder::StartProgress()
|
nsresult MediaDecoder::StartProgress()
|
||||||
{
|
{
|
||||||
if (mProgressTimer)
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
return NS_OK;
|
NS_ASSERTION(!mProgressTimer, "Already started progress timer.");
|
||||||
|
|
||||||
mProgressTimer = do_CreateInstance("@mozilla.org/timer;1");
|
mProgressTimer = do_CreateInstance("@mozilla.org/timer;1");
|
||||||
return mProgressTimer->InitWithFuncCallback(ProgressCallback,
|
return mProgressTimer->InitWithFuncCallback(ProgressCallback,
|
||||||
@ -1607,8 +1559,8 @@ nsresult MediaDecoder::StartProgress()
|
|||||||
|
|
||||||
nsresult MediaDecoder::StopProgress()
|
nsresult MediaDecoder::StopProgress()
|
||||||
{
|
{
|
||||||
if (!mProgressTimer)
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
return NS_OK;
|
NS_ASSERTION(mProgressTimer, "Already stopped progress timer.");
|
||||||
|
|
||||||
nsresult rv = mProgressTimer->Cancel();
|
nsresult rv = mProgressTimer->Cancel();
|
||||||
mProgressTimer = nullptr;
|
mProgressTimer = nullptr;
|
||||||
|
@ -308,9 +308,6 @@ public:
|
|||||||
// Called in |Load| to open mResource.
|
// Called in |Load| to open mResource.
|
||||||
nsresult OpenResource(nsIStreamListener** aStreamListener);
|
nsresult OpenResource(nsIStreamListener** aStreamListener);
|
||||||
|
|
||||||
// Called when the video file has completed downloading.
|
|
||||||
virtual void ResourceLoaded();
|
|
||||||
|
|
||||||
// Called if the media file encounters a network error.
|
// Called if the media file encounters a network error.
|
||||||
virtual void NetworkError();
|
virtual void NetworkError();
|
||||||
|
|
||||||
@ -692,6 +689,12 @@ public:
|
|||||||
return mPlayState;
|
return mPlayState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Called by the media element to start timer to update download progress.
|
||||||
|
nsresult StartProgress();
|
||||||
|
|
||||||
|
// Called by the media element to stop progress information timer.
|
||||||
|
nsresult StopProgress();
|
||||||
|
|
||||||
// Fire progress events if needed according to the time and byte
|
// Fire progress events if needed according to the time and byte
|
||||||
// constraints outlined in the specification. aTimer is true
|
// constraints outlined in the specification. aTimer is true
|
||||||
// if the method is called as a result of the progress timer rather
|
// if the method is called as a result of the progress timer rather
|
||||||
@ -1159,11 +1162,6 @@ protected:
|
|||||||
// been requested. When a seek is started this is reset to invalid.
|
// been requested. When a seek is started this is reset to invalid.
|
||||||
SeekTarget mRequestedSeekTarget;
|
SeekTarget mRequestedSeekTarget;
|
||||||
|
|
||||||
// True when we have fully loaded the resource and reported that
|
|
||||||
// to the element (i.e. reached NETWORK_LOADED state).
|
|
||||||
// Accessed on the main thread only.
|
|
||||||
bool mCalledResourceLoaded;
|
|
||||||
|
|
||||||
// True when seeking or otherwise moving the play position around in
|
// True when seeking or otherwise moving the play position around in
|
||||||
// such a manner that progress event data is inaccurate. This is set
|
// such a manner that progress event data is inaccurate. This is set
|
||||||
// during seek and duration operations to prevent the progress indicator
|
// during seek and duration operations to prevent the progress indicator
|
||||||
@ -1174,12 +1172,6 @@ protected:
|
|||||||
// True if the stream is infinite (e.g. a webradio).
|
// True if the stream is infinite (e.g. a webradio).
|
||||||
bool mInfiniteStream;
|
bool mInfiniteStream;
|
||||||
|
|
||||||
// Start timer to update download progress information.
|
|
||||||
nsresult StartProgress();
|
|
||||||
|
|
||||||
// Stop progress information timer.
|
|
||||||
nsresult StopProgress();
|
|
||||||
|
|
||||||
// Ensures our media stream has been pinned.
|
// Ensures our media stream has been pinned.
|
||||||
void PinForSeek();
|
void PinForSeek();
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@ class HTMLMediaElement;
|
|||||||
class MediaDecoderOwner
|
class MediaDecoderOwner
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
// Called by the media decoder to indicate that the download is progressing.
|
||||||
|
virtual void DownloadProgressed() = 0;
|
||||||
// Called by the media decoder to indicate that the download has stalled
|
// Called by the media decoder to indicate that the download has stalled
|
||||||
// (no data has arrived for a while).
|
// (no data has arrived for a while).
|
||||||
virtual void DownloadStalled() = 0;
|
virtual void DownloadStalled() = 0;
|
||||||
@ -52,15 +54,9 @@ public:
|
|||||||
virtual void MetadataLoaded(const MediaInfo* aInfo,
|
virtual void MetadataLoaded(const MediaInfo* aInfo,
|
||||||
const MetadataTags* aTags) = 0;
|
const MetadataTags* aTags) = 0;
|
||||||
|
|
||||||
// Called by the video decoder object, on the main thread,
|
// Called by the decoder object, on the main thread,
|
||||||
// when it has read the first frame of the video
|
// when it has read the first frame of the video or audio.
|
||||||
// aResourceFullyLoaded should be true if the resource has been
|
virtual void FirstFrameLoaded() = 0;
|
||||||
// fully loaded and the caller will call ResourceLoaded next.
|
|
||||||
virtual void FirstFrameLoaded(bool aResourceFullyLoaded) = 0;
|
|
||||||
|
|
||||||
// Called by the video decoder object, on the main thread,
|
|
||||||
// when the resource has completed downloading.
|
|
||||||
virtual void ResourceLoaded() = 0;
|
|
||||||
|
|
||||||
// Called by the video decoder object, on the main thread,
|
// Called by the video decoder object, on the main thread,
|
||||||
// when the resource has a network error during loading.
|
// when the resource has a network error during loading.
|
||||||
@ -114,6 +110,8 @@ public:
|
|||||||
// The next frame of audio/video is unavailable because the decoder
|
// The next frame of audio/video is unavailable because the decoder
|
||||||
// is paused while it buffers up data
|
// is paused while it buffers up data
|
||||||
NEXT_FRAME_UNAVAILABLE_BUFFERING,
|
NEXT_FRAME_UNAVAILABLE_BUFFERING,
|
||||||
|
// The next frame of audio/video is unavailable for the decoder is seeking.
|
||||||
|
NEXT_FRAME_UNAVAILABLE_SEEKING,
|
||||||
// The next frame of audio/video is unavailable for some other reasons
|
// The next frame of audio/video is unavailable for some other reasons
|
||||||
NEXT_FRAME_UNAVAILABLE,
|
NEXT_FRAME_UNAVAILABLE,
|
||||||
// The next frame is unavailable due to waiting for more Media Source
|
// The next frame is unavailable due to waiting for more Media Source
|
||||||
|
@ -1154,9 +1154,9 @@ void MediaDecoderStateMachine::StartPlayback()
|
|||||||
SetPlayStartTime(TimeStamp::Now());
|
SetPlayStartTime(TimeStamp::Now());
|
||||||
|
|
||||||
NS_ASSERTION(IsPlaying(), "Should report playing by end of StartPlayback()");
|
NS_ASSERTION(IsPlaying(), "Should report playing by end of StartPlayback()");
|
||||||
if (NS_FAILED(StartAudioThread())) {
|
nsresult rv = StartAudioThread();
|
||||||
DECODER_WARN("Failed to create audio thread");
|
NS_ENSURE_SUCCESS_VOID(rv);
|
||||||
}
|
|
||||||
mDecoder->GetReentrantMonitor().NotifyAll();
|
mDecoder->GetReentrantMonitor().NotifyAll();
|
||||||
mDecoder->UpdateStreamBlockingForStateMachinePlaying();
|
mDecoder->UpdateStreamBlockingForStateMachinePlaying();
|
||||||
DispatchDecodeTasksIfNeeded();
|
DispatchDecodeTasksIfNeeded();
|
||||||
@ -1212,8 +1212,10 @@ void MediaDecoderStateMachine::ClearPositionChangeFlag()
|
|||||||
MediaDecoderOwner::NextFrameStatus MediaDecoderStateMachine::GetNextFrameStatus()
|
MediaDecoderOwner::NextFrameStatus MediaDecoderStateMachine::GetNextFrameStatus()
|
||||||
{
|
{
|
||||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||||
if (IsBuffering() || IsSeeking()) {
|
if (IsBuffering()) {
|
||||||
return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING;
|
return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING;
|
||||||
|
} else if (IsSeeking()) {
|
||||||
|
return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING;
|
||||||
} else if (HaveNextFrameData()) {
|
} else if (HaveNextFrameData()) {
|
||||||
return MediaDecoderOwner::NEXT_FRAME_AVAILABLE;
|
return MediaDecoderOwner::NEXT_FRAME_AVAILABLE;
|
||||||
}
|
}
|
||||||
@ -1789,15 +1791,12 @@ MediaDecoderStateMachine::StartAudioThread()
|
|||||||
mStopAudioThread = false;
|
mStopAudioThread = false;
|
||||||
if (HasAudio() && !mAudioSink) {
|
if (HasAudio() && !mAudioSink) {
|
||||||
mAudioCompleted = false;
|
mAudioCompleted = false;
|
||||||
mAudioSink = new AudioSink(this,
|
mAudioSink = new AudioSink(this, mAudioStartTime,
|
||||||
mAudioStartTime, mInfo.mAudio, mDecoder->GetAudioChannel());
|
mInfo.mAudio, mDecoder->GetAudioChannel());
|
||||||
|
// OnAudioSinkError() will be called before Init() returns if an error
|
||||||
|
// occurs during initialization.
|
||||||
nsresult rv = mAudioSink->Init();
|
nsresult rv = mAudioSink->Init();
|
||||||
if (NS_FAILED(rv)) {
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
DECODER_WARN("Changed state to SHUTDOWN because audio sink initialization failed");
|
|
||||||
SetState(DECODER_STATE_SHUTDOWN);
|
|
||||||
mScheduler->ScheduleAndShutdown();
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
mAudioSink->SetVolume(mVolume);
|
mAudioSink->SetVolume(mVolume);
|
||||||
mAudioSink->SetPlaybackRate(mPlaybackRate);
|
mAudioSink->SetPlaybackRate(mPlaybackRate);
|
||||||
@ -2886,6 +2885,10 @@ void MediaDecoderStateMachine::UpdateReadyState() {
|
|||||||
AssertCurrentThreadInMonitor();
|
AssertCurrentThreadInMonitor();
|
||||||
|
|
||||||
MediaDecoderOwner::NextFrameStatus nextFrameStatus = GetNextFrameStatus();
|
MediaDecoderOwner::NextFrameStatus nextFrameStatus = GetNextFrameStatus();
|
||||||
|
// FIXME: This optimization could result in inconsistent next frame status
|
||||||
|
// between the decoder and state machine when GetNextFrameStatus() is called
|
||||||
|
// by the decoder without updating mLastFrameStatus.
|
||||||
|
// Note not to regress bug 882027 when fixing this bug.
|
||||||
if (nextFrameStatus == mLastFrameStatus) {
|
if (nextFrameStatus == mLastFrameStatus) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -3118,6 +3121,25 @@ void MediaDecoderStateMachine::OnAudioSinkComplete()
|
|||||||
mDecoder->GetReentrantMonitor().NotifyAll();
|
mDecoder->GetReentrantMonitor().NotifyAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MediaDecoderStateMachine::OnAudioSinkError()
|
||||||
|
{
|
||||||
|
AssertCurrentThreadInMonitor();
|
||||||
|
// AudioSink not used with captured streams, so ignore errors in this case.
|
||||||
|
if (mAudioCaptured) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mAudioCompleted = true;
|
||||||
|
|
||||||
|
// Notify media decoder/element about this error.
|
||||||
|
RefPtr<nsIRunnable> task(
|
||||||
|
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::OnDecodeError));
|
||||||
|
nsresult rv = mDecodeTaskQueue->Dispatch(task);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
DECODER_WARN("Failed to dispatch OnDecodeError");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
||||||
// avoid redefined macro in unified build
|
// avoid redefined macro in unified build
|
||||||
|
@ -636,6 +636,9 @@ protected:
|
|||||||
// and the sink is shutting down.
|
// and the sink is shutting down.
|
||||||
void OnAudioSinkComplete();
|
void OnAudioSinkComplete();
|
||||||
|
|
||||||
|
// Called by the AudioSink to signal errors.
|
||||||
|
void OnAudioSinkError();
|
||||||
|
|
||||||
// The decoder object that created this state machine. The state machine
|
// The decoder object that created this state machine. The state machine
|
||||||
// holds a strong reference to the decoder to ensure that the decoder stays
|
// holds a strong reference to the decoder to ensure that the decoder stays
|
||||||
// alive once media element has started the decoder shutdown process, and has
|
// alive once media element has started the decoder shutdown process, and has
|
||||||
|
@ -905,8 +905,11 @@ void ChannelMediaResource::Resume()
|
|||||||
// There is (or may be) data to read at mOffset, so start reading it.
|
// There is (or may be) data to read at mOffset, so start reading it.
|
||||||
// Need to recreate the channel.
|
// Need to recreate the channel.
|
||||||
CacheClientSeek(mOffset, false);
|
CacheClientSeek(mOffset, false);
|
||||||
|
element->DownloadResumed();
|
||||||
|
} else {
|
||||||
|
// The channel remains dead. Do not notify DownloadResumed() which
|
||||||
|
// will leave the media element in NETWORK_LOADING state.
|
||||||
}
|
}
|
||||||
element->DownloadResumed();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -984,11 +987,24 @@ public:
|
|||||||
mDecoder(aDecoder), mStatus(aStatus) {}
|
mDecoder(aDecoder), mStatus(aStatus) {}
|
||||||
NS_IMETHOD Run() {
|
NS_IMETHOD Run() {
|
||||||
mDecoder->NotifyDownloadEnded(mStatus);
|
mDecoder->NotifyDownloadEnded(mStatus);
|
||||||
|
if (NS_SUCCEEDED(mStatus)) {
|
||||||
|
MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
|
||||||
|
if (owner) {
|
||||||
|
dom::HTMLMediaElement* element = owner->GetMediaElement();
|
||||||
|
if (element) {
|
||||||
|
element->DownloadSuspended();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// NotifySuspendedStatusChanged will tell the element that download
|
||||||
|
// has been suspended "by the cache", which is true since we never download
|
||||||
|
// anything. The element can then transition to HAVE_ENOUGH_DATA.
|
||||||
|
mDecoder->NotifySuspendedStatusChanged();
|
||||||
|
}
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
nsRefPtr<MediaDecoder> mDecoder;
|
nsRefPtr<MediaDecoder> mDecoder;
|
||||||
nsresult mStatus;
|
nsresult mStatus;
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -1248,8 +1264,8 @@ public:
|
|||||||
return std::max(aOffset, mSize);
|
return std::max(aOffset, mSize);
|
||||||
}
|
}
|
||||||
virtual bool IsDataCachedToEndOfResource(int64_t aOffset) { return true; }
|
virtual bool IsDataCachedToEndOfResource(int64_t aOffset) { return true; }
|
||||||
virtual bool IsSuspendedByCache() { return false; }
|
virtual bool IsSuspendedByCache() { return true; }
|
||||||
virtual bool IsSuspended() { return false; }
|
virtual bool IsSuspended() { return true; }
|
||||||
virtual bool IsTransportSeekable() MOZ_OVERRIDE { return true; }
|
virtual bool IsTransportSeekable() MOZ_OVERRIDE { return true; }
|
||||||
|
|
||||||
nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges);
|
nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges);
|
||||||
|
@ -2859,8 +2859,11 @@ MediaStreamGraphImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
|
|||||||
MonitorAutoLock memoryReportLock(mMemoryReportMonitor);
|
MonitorAutoLock memoryReportLock(mMemoryReportMonitor);
|
||||||
mNeedsMemoryReport = true;
|
mNeedsMemoryReport = true;
|
||||||
|
|
||||||
// Wake up the MSG thread.
|
{
|
||||||
CurrentDriver()->WakeUp();
|
// Wake up the MSG thread.
|
||||||
|
MonitorAutoLock monitorLock(mMonitor);
|
||||||
|
CurrentDriver()->WakeUp();
|
||||||
|
}
|
||||||
|
|
||||||
if (mLifecycleState >= LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN) {
|
if (mLifecycleState >= LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN) {
|
||||||
// Shutting down, nothing to report.
|
// Shutting down, nothing to report.
|
||||||
|
@ -256,6 +256,13 @@ WMFAudioMFTManager::Output(int64_t aStreamOffset,
|
|||||||
mAudioFrameOffset = 0;
|
mAudioFrameOffset = 0;
|
||||||
}
|
}
|
||||||
mMustRecaptureAudioPosition = false;
|
mMustRecaptureAudioPosition = false;
|
||||||
|
|
||||||
|
// Also update the output type, in case this segment has a different
|
||||||
|
// rate. This also triggers on the first sample, which can have a
|
||||||
|
// different rate than is advertised in the container, and sometimes
|
||||||
|
// we don't get a MF_E_TRANSFORM_STREAM_CHANGE when the rate changes.
|
||||||
|
hr = UpdateOutputType();
|
||||||
|
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||||
}
|
}
|
||||||
MOZ_ASSERT(numFramesToStrip >= 0);
|
MOZ_ASSERT(numFramesToStrip >= 0);
|
||||||
int32_t offset = std::min<int32_t>(numFramesToStrip, numFrames);
|
int32_t offset = std::min<int32_t>(numFramesToStrip, numFrames);
|
||||||
|
@ -669,7 +669,7 @@ MediaDecoderOwner::NextFrameStatus AudioOffloadPlayer::GetNextFrameStatus()
|
|||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
if (mPlayState == MediaDecoder::PLAY_STATE_SEEKING) {
|
if (mPlayState == MediaDecoder::PLAY_STATE_SEEKING) {
|
||||||
return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING;
|
return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING;
|
||||||
} else if (mPlayState == MediaDecoder::PLAY_STATE_ENDED) {
|
} else if (mPlayState == MediaDecoder::PLAY_STATE_ENDED) {
|
||||||
return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
|
return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
|
||||||
} else {
|
} else {
|
||||||
|
@ -101,7 +101,6 @@ MediaOmxCommonDecoder::PauseStateMachine()
|
|||||||
if (!mDecoderStateMachine) {
|
if (!mDecoderStateMachine) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StopProgress();
|
|
||||||
mDecoderStateMachine->SetDormant(true);
|
mDecoderStateMachine->SetDormant(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BIN
content/media/test/invalid-discard_on_multi_blocks.webm
Normal file
BIN
content/media/test/invalid-discard_on_multi_blocks.webm
Normal file
Binary file not shown.
@ -0,0 +1 @@
|
|||||||
|
Cache-Control: no-store
|
BIN
content/media/test/invalid-excess_discard.webm
Normal file
BIN
content/media/test/invalid-excess_discard.webm
Normal file
Binary file not shown.
1
content/media/test/invalid-excess_discard.webm^headers^
Normal file
1
content/media/test/invalid-excess_discard.webm^headers^
Normal file
@ -0,0 +1 @@
|
|||||||
|
Cache-Control: no-store
|
BIN
content/media/test/invalid-excess_neg_discard.webm
Normal file
BIN
content/media/test/invalid-excess_neg_discard.webm
Normal file
Binary file not shown.
@ -0,0 +1 @@
|
|||||||
|
Cache-Control: no-store
|
BIN
content/media/test/invalid-neg_discard.webm
Normal file
BIN
content/media/test/invalid-neg_discard.webm
Normal file
Binary file not shown.
1
content/media/test/invalid-neg_discard.webm^headers^
Normal file
1
content/media/test/invalid-neg_discard.webm^headers^
Normal file
@ -0,0 +1 @@
|
|||||||
|
Cache-Control: no-store
|
@ -251,6 +251,13 @@ var gInvalidTests = [
|
|||||||
{ name:"invalid-preskip.webm", type:"audio/webm; codecs=opus"},
|
{ name:"invalid-preskip.webm", type:"audio/webm; codecs=opus"},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
var gInvalidPlayTests = [
|
||||||
|
{ name:"invalid-excess_discard.webm", type:"audio/webm; codecs=opus"},
|
||||||
|
{ name:"invalid-excess_neg_discard.webm", type:"audio/webm; codecs=opus"},
|
||||||
|
{ name:"invalid-neg_discard.webm", type:"audio/webm; codecs=opus"},
|
||||||
|
{ name:"invalid-discard_on_multi_blocks.webm", type:"audio/webm; codecs=opus"},
|
||||||
|
];
|
||||||
|
|
||||||
// Files to check different cases of ogg skeleton information.
|
// Files to check different cases of ogg skeleton information.
|
||||||
// sample-fisbone-skeleton4.ogv
|
// sample-fisbone-skeleton4.ogv
|
||||||
// - Skeleton v4, w/ Content-Type,Role,Name,Language,Title for both theora/vorbis
|
// - Skeleton v4, w/ Content-Type,Role,Name,Language,Title for both theora/vorbis
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
# do ok(true, "Type not supported") and stop the test.
|
# do ok(true, "Type not supported") and stop the test.
|
||||||
|
|
||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && toolkit != 'gonk') # b2g-desktop(bug 918299)
|
skip-if = buildapp == 'mulet'
|
||||||
support-files =
|
support-files =
|
||||||
320x240.ogv
|
320x240.ogv
|
||||||
320x240.ogv^headers^
|
320x240.ogv^headers^
|
||||||
@ -157,6 +157,12 @@ support-files =
|
|||||||
invalid-cmap-s1c2.opus^headers^
|
invalid-cmap-s1c2.opus^headers^
|
||||||
invalid-cmap-short.opus
|
invalid-cmap-short.opus
|
||||||
invalid-cmap-short.opus^headers^
|
invalid-cmap-short.opus^headers^
|
||||||
|
invalid-discard_on_multi_blocks.webm
|
||||||
|
invalid-discard_on_multi_blocks.webm^headers^
|
||||||
|
invalid-excess_discard.webm
|
||||||
|
invalid-excess_discard.webm^headers^
|
||||||
|
invalid-excess_neg_discard.webm
|
||||||
|
invalid-excess_neg_discard.webm^headers^
|
||||||
invalid-m0c0.opus
|
invalid-m0c0.opus
|
||||||
invalid-m0c0.opus^headers^
|
invalid-m0c0.opus^headers^
|
||||||
invalid-m0c3.opus
|
invalid-m0c3.opus
|
||||||
@ -169,6 +175,8 @@ support-files =
|
|||||||
invalid-m2c0.opus^headers^
|
invalid-m2c0.opus^headers^
|
||||||
invalid-m2c1.opus
|
invalid-m2c1.opus
|
||||||
invalid-m2c1.opus^headers^
|
invalid-m2c1.opus^headers^
|
||||||
|
invalid-neg_discard.webm
|
||||||
|
invalid-neg_discard.webm^headers^
|
||||||
invalid-preskip.webm
|
invalid-preskip.webm
|
||||||
invalid-preskip.webm^headers^
|
invalid-preskip.webm^headers^
|
||||||
long.vtt
|
long.vtt
|
||||||
@ -291,6 +299,7 @@ support-files =
|
|||||||
wavedata_u8.wav^headers^
|
wavedata_u8.wav^headers^
|
||||||
|
|
||||||
[test_access_control.html]
|
[test_access_control.html]
|
||||||
|
skip-if = buildapp == 'b2g' && toolkit != 'gonk' # crash on b2g-desktop
|
||||||
[test_aspectratio_mp4.html]
|
[test_aspectratio_mp4.html]
|
||||||
[test_audio1.html]
|
[test_audio1.html]
|
||||||
[test_audio2.html]
|
[test_audio2.html]
|
||||||
@ -350,6 +359,7 @@ skip-if = toolkit == 'android' # bug 608634
|
|||||||
[test_imagecapture.html]
|
[test_imagecapture.html]
|
||||||
[test_info_leak.html]
|
[test_info_leak.html]
|
||||||
[test_invalid_reject.html]
|
[test_invalid_reject.html]
|
||||||
|
[test_invalid_reject_play.html]
|
||||||
[test_invalid_seek.html]
|
[test_invalid_seek.html]
|
||||||
[test_load.html]
|
[test_load.html]
|
||||||
[test_load_candidates.html]
|
[test_load_candidates.html]
|
||||||
|
@ -31,10 +31,10 @@ function startTest(test, token) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Seeing a decoder error is a success.
|
// Seeing a decoder error is a success.
|
||||||
v.addEventListener("error", function(e) {
|
v.addEventListener("error", function onerror(e) {
|
||||||
is(v.error.code, v.error.MEDIA_ERR_DECODE,
|
is(v.error.code, v.error.MEDIA_ERR_DECODE,
|
||||||
"decoder should reject " + test.name);
|
"decoder should reject " + test.name);
|
||||||
v.removeEventListener('error', arguments.callee, false);
|
v.removeEventListener('error', onerror, false);
|
||||||
manager.finished(token);
|
manager.finished(token);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
44
content/media/test/test_invalid_reject_play.html
Normal file
44
content/media/test/test_invalid_reject_play.html
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Test rejection of invalid files during playback</title>
|
||||||
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||||
|
<script type="text/javascript" src="manifest.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre id="test">
|
||||||
|
<script class="testbody" type="text/javascript">
|
||||||
|
|
||||||
|
var manager = new MediaTestManager;
|
||||||
|
|
||||||
|
function startTest(test, token) {
|
||||||
|
var v = document.createElement('video');
|
||||||
|
manager.started(token);
|
||||||
|
|
||||||
|
// Seeing a decoder error is a success.
|
||||||
|
v.addEventListener("error", function onerror(e) {
|
||||||
|
is(v.error.code, v.error.MEDIA_ERR_DECODE,
|
||||||
|
"decoder should reject " + test.name);
|
||||||
|
v.removeEventListener("error", onerror, false);
|
||||||
|
manager.finished(token);
|
||||||
|
});
|
||||||
|
|
||||||
|
v.addEventListener("ended", function onended(e) {
|
||||||
|
ok(false, "decoder should have rejected file before playback ended");
|
||||||
|
v.removeEventListener("ended", onended, false);
|
||||||
|
manager.finished(token);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.appendChild(v);
|
||||||
|
v.src = test.name;
|
||||||
|
v.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.runTests(gInvalidPlayTests, startTest);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -30,12 +30,12 @@ function maketest(attach_media, name, type, check_metadata) {
|
|||||||
manager.finished(token);
|
manager.finished(token);
|
||||||
}, false);
|
}, false);
|
||||||
} else {
|
} else {
|
||||||
e.addEventListener('error', function(event) {
|
e.addEventListener('error', function onerror(event) {
|
||||||
is(errorRun, false, "error handler should run once only!");
|
is(errorRun, false, "error handler should run once only!");
|
||||||
errorRun = true;
|
errorRun = true;
|
||||||
is(e.readyState, HTMLMediaElement.HAVE_NOTHING,
|
is(e.readyState, HTMLMediaElement.HAVE_NOTHING,
|
||||||
'test ' + token + ' readyState should be HAVE_NOTHING when load fails.');
|
'test ' + token + ' readyState should be HAVE_NOTHING when load fails.');
|
||||||
e.removeEventListener('error', arguments.callee, true);
|
e.removeEventListener('error', onerror, true);
|
||||||
removeNodeAndSource(e);
|
removeNodeAndSource(e);
|
||||||
manager.finished(token);
|
manager.finished(token);
|
||||||
}, true);
|
}, true);
|
||||||
|
@ -36,7 +36,8 @@ let VERY_SLOW_RATE = 0.1,
|
|||||||
|
|
||||||
function ontimeupdate(e) {
|
function ontimeupdate(e) {
|
||||||
var t = e.target;
|
var t = e.target;
|
||||||
if (t.gotEnded) {
|
// Skip short files for SoundTouch doesn't work well on small number of samples.
|
||||||
|
if (t.gotEnded || t.duration < 2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
t.testedForSlowdown = true;
|
t.testedForSlowdown = true;
|
||||||
|
@ -24,7 +24,8 @@
|
|||||||
using mozilla::NesteggPacketHolder;
|
using mozilla::NesteggPacketHolder;
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
class nsAutoRefTraits<NesteggPacketHolder> : public nsPointerRefTraits<NesteggPacketHolder>
|
class nsAutoRefTraits<NesteggPacketHolder> :
|
||||||
|
public nsPointerRefTraits<NesteggPacketHolder>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static void Release(NesteggPacketHolder* aHolder) { delete aHolder; }
|
static void Release(NesteggPacketHolder* aHolder) { delete aHolder; }
|
||||||
@ -39,6 +40,7 @@ using namespace layers;
|
|||||||
//#define SEEK_LOGGING
|
//#define SEEK_LOGGING
|
||||||
|
|
||||||
#ifdef PR_LOGGING
|
#ifdef PR_LOGGING
|
||||||
|
#include "prprf.h"
|
||||||
extern PRLogModuleInfo* gMediaDecoderLog;
|
extern PRLogModuleInfo* gMediaDecoderLog;
|
||||||
PRLogModuleInfo* gNesteggLog;
|
PRLogModuleInfo* gNesteggLog;
|
||||||
#define LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg)
|
#define LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg)
|
||||||
@ -60,8 +62,9 @@ static const double NS_PER_S = 1e9;
|
|||||||
// decoder from which the media resource is obtained.
|
// decoder from which the media resource is obtained.
|
||||||
static int webm_read(void *aBuffer, size_t aLength, void *aUserData)
|
static int webm_read(void *aBuffer, size_t aLength, void *aUserData)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(aUserData, "aUserData must point to a valid AbstractMediaDecoder");
|
MOZ_ASSERT(aUserData);
|
||||||
AbstractMediaDecoder* decoder = reinterpret_cast<AbstractMediaDecoder*>(aUserData);
|
AbstractMediaDecoder* decoder =
|
||||||
|
reinterpret_cast<AbstractMediaDecoder*>(aUserData);
|
||||||
MediaResource* resource = decoder->GetResource();
|
MediaResource* resource = decoder->GetResource();
|
||||||
NS_ASSERTION(resource, "Decoder has no media resource");
|
NS_ASSERTION(resource, "Decoder has no media resource");
|
||||||
|
|
||||||
@ -85,8 +88,9 @@ static int webm_read(void *aBuffer, size_t aLength, void *aUserData)
|
|||||||
|
|
||||||
static int webm_seek(int64_t aOffset, int aWhence, void *aUserData)
|
static int webm_seek(int64_t aOffset, int aWhence, void *aUserData)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(aUserData, "aUserData must point to a valid AbstractMediaDecoder");
|
MOZ_ASSERT(aUserData);
|
||||||
AbstractMediaDecoder* decoder = reinterpret_cast<AbstractMediaDecoder*>(aUserData);
|
AbstractMediaDecoder* decoder =
|
||||||
|
reinterpret_cast<AbstractMediaDecoder*>(aUserData);
|
||||||
MediaResource* resource = decoder->GetResource();
|
MediaResource* resource = decoder->GetResource();
|
||||||
NS_ASSERTION(resource, "Decoder has no media resource");
|
NS_ASSERTION(resource, "Decoder has no media resource");
|
||||||
nsresult rv = resource->Seek(aWhence, aOffset);
|
nsresult rv = resource->Seek(aWhence, aOffset);
|
||||||
@ -95,8 +99,9 @@ static int webm_seek(int64_t aOffset, int aWhence, void *aUserData)
|
|||||||
|
|
||||||
static int64_t webm_tell(void *aUserData)
|
static int64_t webm_tell(void *aUserData)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(aUserData, "aUserData must point to a valid AbstractMediaDecoder");
|
MOZ_ASSERT(aUserData);
|
||||||
AbstractMediaDecoder* decoder = reinterpret_cast<AbstractMediaDecoder*>(aUserData);
|
AbstractMediaDecoder* decoder =
|
||||||
|
reinterpret_cast<AbstractMediaDecoder*>(aUserData);
|
||||||
MediaResource* resource = decoder->GetResource();
|
MediaResource* resource = decoder->GetResource();
|
||||||
NS_ASSERTION(resource, "Decoder has no media resource");
|
NS_ASSERTION(resource, "Decoder has no media resource");
|
||||||
return resource->Tell();
|
return resource->Tell();
|
||||||
@ -142,26 +147,41 @@ static void webm_log(nestegg * context,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ogg_packet
|
||||||
|
InitOggPacket(const unsigned char* aData, size_t aLength, bool aBOS, bool aEOS,
|
||||||
|
int64_t aGranulepos, int64_t aPacketNo)
|
||||||
|
{
|
||||||
|
ogg_packet packet;
|
||||||
|
packet.packet = const_cast<unsigned char*>(aData);
|
||||||
|
packet.bytes = aLength;
|
||||||
|
packet.b_o_s = aBOS;
|
||||||
|
packet.e_o_s = aEOS;
|
||||||
|
packet.granulepos = aGranulepos;
|
||||||
|
packet.packetno = aPacketNo;
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
WebMReader::WebMReader(AbstractMediaDecoder* aDecoder)
|
WebMReader::WebMReader(AbstractMediaDecoder* aDecoder)
|
||||||
: MediaDecoderReader(aDecoder),
|
: MediaDecoderReader(aDecoder)
|
||||||
mContext(nullptr),
|
, mContext(nullptr)
|
||||||
mPacketCount(0),
|
, mPacketCount(0)
|
||||||
mChannels(0),
|
|
||||||
#ifdef MOZ_OPUS
|
#ifdef MOZ_OPUS
|
||||||
mOpusParser(nullptr),
|
, mOpusDecoder(nullptr)
|
||||||
mOpusDecoder(nullptr),
|
, mSkip(0)
|
||||||
mSkip(0),
|
, mSeekPreroll(0)
|
||||||
mSeekPreroll(0),
|
#endif
|
||||||
|
, mVideoTrack(0)
|
||||||
|
, mAudioTrack(0)
|
||||||
|
, mAudioStartUsec(-1)
|
||||||
|
, mAudioFrames(0)
|
||||||
|
, mLastVideoFrameTime(0)
|
||||||
|
, mAudioCodec(-1)
|
||||||
|
, mVideoCodec(-1)
|
||||||
|
, mHasVideo(false)
|
||||||
|
, mHasAudio(false)
|
||||||
|
#ifdef MOZ_OPUS
|
||||||
|
, mPaddingDiscarded(false)
|
||||||
#endif
|
#endif
|
||||||
mVideoTrack(0),
|
|
||||||
mAudioTrack(0),
|
|
||||||
mAudioStartUsec(-1),
|
|
||||||
mAudioFrames(0),
|
|
||||||
mLastVideoFrameTime(0),
|
|
||||||
mAudioCodec(-1),
|
|
||||||
mVideoCodec(-1),
|
|
||||||
mHasVideo(false),
|
|
||||||
mHasAudio(false)
|
|
||||||
{
|
{
|
||||||
MOZ_COUNT_CTOR(WebMReader);
|
MOZ_COUNT_CTOR(WebMReader);
|
||||||
#ifdef PR_LOGGING
|
#ifdef PR_LOGGING
|
||||||
@ -237,6 +257,7 @@ nsresult WebMReader::ResetDecode()
|
|||||||
// Reset the decoder.
|
// Reset the decoder.
|
||||||
opus_multistream_decoder_ctl(mOpusDecoder, OPUS_RESET_STATE);
|
opus_multistream_decoder_ctl(mOpusDecoder, OPUS_RESET_STATE);
|
||||||
mSkip = mOpusParser->mPreSkip;
|
mSkip = mOpusParser->mPreSkip;
|
||||||
|
mPaddingDiscarded = false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -314,10 +335,12 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
|
|||||||
|
|
||||||
// Picture region, taking into account cropping, before scaling
|
// Picture region, taking into account cropping, before scaling
|
||||||
// to the display size.
|
// to the display size.
|
||||||
|
unsigned int cropH = params.crop_right + params.crop_left;
|
||||||
|
unsigned int cropV = params.crop_bottom + params.crop_top;
|
||||||
nsIntRect pictureRect(params.crop_left,
|
nsIntRect pictureRect(params.crop_left,
|
||||||
params.crop_top,
|
params.crop_top,
|
||||||
params.width - (params.crop_right + params.crop_left),
|
params.width - cropH,
|
||||||
params.height - (params.crop_bottom + params.crop_top));
|
params.height - cropV);
|
||||||
|
|
||||||
// If the cropping data appears invalid then use the frame data
|
// If the cropping data appears invalid then use the frame data
|
||||||
if (pictureRect.width <= 0 ||
|
if (pictureRect.width <= 0 ||
|
||||||
@ -331,8 +354,8 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
|
|||||||
pictureRect.height = params.height;
|
pictureRect.height = params.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the container-reported frame and pictureRect sizes. This ensures
|
// Validate the container-reported frame and pictureRect sizes. This
|
||||||
// that our video frame creation code doesn't overflow.
|
// ensures that our video frame creation code doesn't overflow.
|
||||||
nsIntSize displaySize(params.display_width, params.display_height);
|
nsIntSize displaySize(params.display_width, params.display_height);
|
||||||
nsIntSize frameSize(params.width, params.height);
|
nsIntSize frameSize(params.width, params.height);
|
||||||
if (!IsValidVideoRegion(frameSize, pictureRect, displaySize)) {
|
if (!IsValidVideoRegion(frameSize, pictureRect, displaySize)) {
|
||||||
@ -398,7 +421,8 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
|
|||||||
Cleanup();
|
Cleanup();
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
ogg_packet opacket = InitOggPacket(data, length, header == 0, false, 0);
|
ogg_packet opacket = InitOggPacket(data, length, header == 0, false,
|
||||||
|
0, mPacketCount++);
|
||||||
|
|
||||||
r = vorbis_synthesis_headerin(&mVorbisInfo,
|
r = vorbis_synthesis_headerin(&mVorbisInfo,
|
||||||
&mVorbisComment,
|
&mVorbisComment,
|
||||||
@ -423,7 +447,6 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
|
|||||||
|
|
||||||
mInfo.mAudio.mRate = mVorbisDsp.vi->rate;
|
mInfo.mAudio.mRate = mVorbisDsp.vi->rate;
|
||||||
mInfo.mAudio.mChannels = mVorbisDsp.vi->channels;
|
mInfo.mAudio.mChannels = mVorbisDsp.vi->channels;
|
||||||
mChannels = mInfo.mAudio.mChannels;
|
|
||||||
#ifdef MOZ_OPUS
|
#ifdef MOZ_OPUS
|
||||||
} else if (mAudioCodec == NESTEGG_CODEC_OPUS) {
|
} else if (mAudioCodec == NESTEGG_CODEC_OPUS) {
|
||||||
unsigned char* data = 0;
|
unsigned char* data = 0;
|
||||||
@ -445,9 +468,10 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
|
|||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (static_cast<int64_t>(mCodecDelay) != FramesToUsecs(mOpusParser->mPreSkip, mOpusParser->mRate).value()) {
|
if (int64_t(mCodecDelay) != FramesToUsecs(mOpusParser->mPreSkip,
|
||||||
|
mOpusParser->mRate).value()) {
|
||||||
LOG(PR_LOG_WARNING,
|
LOG(PR_LOG_WARNING,
|
||||||
("Invalid Opus header: CodecDelay and pre-skip do not match!\n"));
|
("Invalid Opus header: CodecDelay and pre-skip do not match!"));
|
||||||
Cleanup();
|
Cleanup();
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
@ -455,7 +479,6 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
|
|||||||
mInfo.mAudio.mRate = mOpusParser->mRate;
|
mInfo.mAudio.mRate = mOpusParser->mRate;
|
||||||
|
|
||||||
mInfo.mAudio.mChannels = mOpusParser->mChannels;
|
mInfo.mAudio.mChannels = mOpusParser->mChannels;
|
||||||
mChannels = mInfo.mAudio.mChannels;
|
|
||||||
mSeekPreroll = params.seek_preroll;
|
mSeekPreroll = params.seek_preroll;
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
@ -486,33 +509,18 @@ bool WebMReader::InitOpusDecoder()
|
|||||||
NS_ASSERTION(mOpusDecoder == nullptr, "leaking OpusDecoder");
|
NS_ASSERTION(mOpusDecoder == nullptr, "leaking OpusDecoder");
|
||||||
|
|
||||||
mOpusDecoder = opus_multistream_decoder_create(mOpusParser->mRate,
|
mOpusDecoder = opus_multistream_decoder_create(mOpusParser->mRate,
|
||||||
mOpusParser->mChannels,
|
mOpusParser->mChannels,
|
||||||
mOpusParser->mStreams,
|
mOpusParser->mStreams,
|
||||||
mOpusParser->mCoupledStreams,
|
mOpusParser->mCoupledStreams,
|
||||||
mOpusParser->mMappingTable,
|
mOpusParser->mMappingTable,
|
||||||
&r);
|
&r);
|
||||||
mSkip = mOpusParser->mPreSkip;
|
mSkip = mOpusParser->mPreSkip;
|
||||||
|
mPaddingDiscarded = false;
|
||||||
|
|
||||||
return r == OPUS_OK;
|
return r == OPUS_OK;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ogg_packet WebMReader::InitOggPacket(unsigned char* aData,
|
|
||||||
size_t aLength,
|
|
||||||
bool aBOS,
|
|
||||||
bool aEOS,
|
|
||||||
int64_t aGranulepos)
|
|
||||||
{
|
|
||||||
ogg_packet packet;
|
|
||||||
packet.packet = aData;
|
|
||||||
packet.bytes = aLength;
|
|
||||||
packet.b_o_s = aBOS;
|
|
||||||
packet.e_o_s = aEOS;
|
|
||||||
packet.granulepos = aGranulepos;
|
|
||||||
packet.packetno = mPacketCount++;
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset)
|
bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||||
@ -530,7 +538,6 @@ bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint32_t rate = mInfo.mAudio.mRate;
|
|
||||||
uint64_t tstamp_usecs = tstamp / NS_PER_USEC;
|
uint64_t tstamp_usecs = tstamp / NS_PER_USEC;
|
||||||
if (mAudioStartUsec == -1) {
|
if (mAudioStartUsec == -1) {
|
||||||
// This is the first audio chunk. Assume the start time of our decode
|
// This is the first audio chunk. Assume the start time of our decode
|
||||||
@ -541,8 +548,9 @@ bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset)
|
|||||||
// the previous audio chunk, we need to increment the packet count so that
|
// the previous audio chunk, we need to increment the packet count so that
|
||||||
// the vorbis decode doesn't use data from before the gap to help decode
|
// the vorbis decode doesn't use data from before the gap to help decode
|
||||||
// from after the gap.
|
// from after the gap.
|
||||||
CheckedInt64 tstamp_frames = UsecsToFrames(tstamp_usecs, rate);
|
CheckedInt64 tstamp_frames = UsecsToFrames(tstamp_usecs, mInfo.mAudio.mRate);
|
||||||
CheckedInt64 decoded_frames = UsecsToFrames(mAudioStartUsec, rate);
|
CheckedInt64 decoded_frames = UsecsToFrames(mAudioStartUsec,
|
||||||
|
mInfo.mAudio.mRate);
|
||||||
if (!tstamp_frames.isValid() || !decoded_frames.isValid()) {
|
if (!tstamp_frames.isValid() || !decoded_frames.isValid()) {
|
||||||
NS_WARNING("Int overflow converting WebM times to frames");
|
NS_WARNING("Int overflow converting WebM times to frames");
|
||||||
return false;
|
return false;
|
||||||
@ -554,10 +562,11 @@ bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset)
|
|||||||
}
|
}
|
||||||
if (tstamp_frames.value() > decoded_frames.value()) {
|
if (tstamp_frames.value() > decoded_frames.value()) {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
CheckedInt64 usecs = FramesToUsecs(tstamp_frames.value() - decoded_frames.value(), rate);
|
int64_t gap_frames = tstamp_frames.value() - decoded_frames.value();
|
||||||
LOG(PR_LOG_DEBUG, ("WebMReader detected gap of %lld, %lld frames, in audio stream\n",
|
CheckedInt64 usecs = FramesToUsecs(gap_frames, mInfo.mAudio.mRate);
|
||||||
|
LOG(PR_LOG_DEBUG, ("WebMReader detected gap of %lld, %lld frames, in audio",
|
||||||
usecs.isValid() ? usecs.value() : -1,
|
usecs.isValid() ? usecs.value() : -1,
|
||||||
tstamp_frames.value() - decoded_frames.value()));
|
gap_frames));
|
||||||
#endif
|
#endif
|
||||||
mPacketCount++;
|
mPacketCount++;
|
||||||
mAudioStartUsec = tstamp_usecs;
|
mAudioStartUsec = tstamp_usecs;
|
||||||
@ -573,198 +582,235 @@ bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (mAudioCodec == NESTEGG_CODEC_VORBIS) {
|
if (mAudioCodec == NESTEGG_CODEC_VORBIS) {
|
||||||
ogg_packet opacket = InitOggPacket(data, length, false, false, -1);
|
if (!DecodeVorbis(data, length, aOffset, tstamp_usecs, &total_frames)) {
|
||||||
|
|
||||||
if (vorbis_synthesis(&mVorbisBlock, &opacket) != 0) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vorbis_synthesis_blockin(&mVorbisDsp,
|
|
||||||
&mVorbisBlock) != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
VorbisPCMValue** pcm = 0;
|
|
||||||
int32_t frames = vorbis_synthesis_pcmout(&mVorbisDsp, &pcm);
|
|
||||||
// If the first packet of audio in the media produces no data, we
|
|
||||||
// still need to produce an AudioData for it so that the correct media
|
|
||||||
// start time is calculated. Otherwise we'd end up with a media start
|
|
||||||
// time derived from the timecode of the first packet that produced
|
|
||||||
// data.
|
|
||||||
if (frames == 0 && mAudioFrames == 0) {
|
|
||||||
AudioQueue().Push(new AudioData(aOffset, tstamp_usecs, 0, 0, nullptr, mChannels, rate));
|
|
||||||
}
|
|
||||||
while (frames > 0) {
|
|
||||||
nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frames * mChannels]);
|
|
||||||
for (uint32_t j = 0; j < mChannels; ++j) {
|
|
||||||
VorbisPCMValue* channel = pcm[j];
|
|
||||||
for (uint32_t i = 0; i < uint32_t(frames); ++i) {
|
|
||||||
buffer[i*mChannels + j] = MOZ_CONVERT_VORBIS_SAMPLE(channel[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckedInt64 duration = FramesToUsecs(frames, rate);
|
|
||||||
if (!duration.isValid()) {
|
|
||||||
NS_WARNING("Int overflow converting WebM audio duration");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
CheckedInt64 total_duration = FramesToUsecs(total_frames, rate);
|
|
||||||
if (!total_duration.isValid()) {
|
|
||||||
NS_WARNING("Int overflow converting WebM audio total_duration");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckedInt64 time = total_duration + tstamp_usecs;
|
|
||||||
if (!time.isValid()) {
|
|
||||||
NS_WARNING("Int overflow adding total_duration and tstamp_usecs");
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
total_frames += frames;
|
|
||||||
AudioQueue().Push(new AudioData(aOffset,
|
|
||||||
time.value(),
|
|
||||||
duration.value(),
|
|
||||||
frames,
|
|
||||||
buffer.forget(),
|
|
||||||
mChannels,
|
|
||||||
rate));
|
|
||||||
mAudioFrames += frames;
|
|
||||||
if (vorbis_synthesis_read(&mVorbisDsp, frames) != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
frames = vorbis_synthesis_pcmout(&mVorbisDsp, &pcm);
|
|
||||||
}
|
|
||||||
} else if (mAudioCodec == NESTEGG_CODEC_OPUS) {
|
} else if (mAudioCodec == NESTEGG_CODEC_OPUS) {
|
||||||
#ifdef MOZ_OPUS
|
#ifdef MOZ_OPUS
|
||||||
uint32_t channels = mOpusParser->mChannels;
|
if (!DecodeOpus(data, length, aOffset, tstamp_usecs, aPacket)) {
|
||||||
|
|
||||||
// Maximum value is 63*2880, so there's no chance of overflow.
|
|
||||||
int32_t frames_number = opus_packet_get_nb_frames(data, length);
|
|
||||||
|
|
||||||
if (frames_number <= 0)
|
|
||||||
return false; // Invalid packet header.
|
|
||||||
int32_t samples = opus_packet_get_samples_per_frame(data,
|
|
||||||
(opus_int32) rate);
|
|
||||||
int32_t frames = frames_number*samples;
|
|
||||||
|
|
||||||
// A valid Opus packet must be between 2.5 and 120 ms long.
|
|
||||||
if (frames < 120 || frames > 5760)
|
|
||||||
return false;
|
return false;
|
||||||
nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frames * channels]);
|
|
||||||
|
|
||||||
// Decode to the appropriate sample type.
|
|
||||||
#ifdef MOZ_SAMPLE_TYPE_FLOAT32
|
|
||||||
int ret = opus_multistream_decode_float(mOpusDecoder,
|
|
||||||
data, length,
|
|
||||||
buffer, frames, false);
|
|
||||||
#else
|
|
||||||
int ret = opus_multistream_decode(mOpusDecoder,
|
|
||||||
data, length,
|
|
||||||
buffer, frames, false);
|
|
||||||
#endif
|
|
||||||
if (ret < 0)
|
|
||||||
return false;
|
|
||||||
NS_ASSERTION(ret == frames, "Opus decoded too few audio samples");
|
|
||||||
CheckedInt64 startTime = tstamp_usecs;
|
|
||||||
|
|
||||||
// Trim the initial frames while the decoder is settling.
|
|
||||||
if (mSkip > 0) {
|
|
||||||
int32_t skipFrames = std::min(mSkip, frames);
|
|
||||||
if (skipFrames == frames) {
|
|
||||||
// discard the whole packet
|
|
||||||
mSkip -= frames;
|
|
||||||
LOG(PR_LOG_DEBUG, ("Opus decoder skipping %d frames"
|
|
||||||
" (whole packet)", frames));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
int32_t keepFrames = frames - skipFrames;
|
|
||||||
int samples = keepFrames * channels;
|
|
||||||
nsAutoArrayPtr<AudioDataValue> trimBuffer(new AudioDataValue[samples]);
|
|
||||||
PodCopy(trimBuffer.get(), buffer.get() + skipFrames*channels, samples);
|
|
||||||
startTime = startTime + FramesToUsecs(skipFrames, rate);
|
|
||||||
frames = keepFrames;
|
|
||||||
buffer = trimBuffer;
|
|
||||||
|
|
||||||
mSkip -= skipFrames;
|
|
||||||
LOG(PR_LOG_DEBUG, ("Opus decoder skipping %d frames", skipFrames));
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t discardPadding = 0;
|
|
||||||
r = nestegg_packet_discard_padding(aPacket, &discardPadding);
|
|
||||||
if (discardPadding > 0) {
|
|
||||||
CheckedInt64 discardFrames = UsecsToFrames(discardPadding / NS_PER_USEC, rate);
|
|
||||||
if (!discardFrames.isValid()) {
|
|
||||||
NS_WARNING("Int overflow in DiscardPadding");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (discardFrames.value() >= frames) {
|
|
||||||
LOG(PR_LOG_DEBUG, ("Opus decoder discarding whole packet"
|
|
||||||
" (%d frames) as padding (%lld discarded)",
|
|
||||||
frames, discardFrames.value()));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
int32_t keepFrames = frames - discardFrames.value();
|
|
||||||
int32_t samples = keepFrames * channels;
|
|
||||||
nsAutoArrayPtr<AudioDataValue> trimBuffer(new AudioDataValue[samples]);
|
|
||||||
PodCopy(trimBuffer.get(), buffer.get(), samples);
|
|
||||||
frames = keepFrames;
|
|
||||||
buffer = trimBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply the header gain if one was specified.
|
|
||||||
#ifdef MOZ_SAMPLE_TYPE_FLOAT32
|
|
||||||
if (mOpusParser->mGain != 1.0f) {
|
|
||||||
float gain = mOpusParser->mGain;
|
|
||||||
int samples = frames * channels;
|
|
||||||
for (int i = 0; i < samples; i++) {
|
|
||||||
buffer[i] *= gain;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (mOpusParser->mGain_Q16 != 65536) {
|
|
||||||
int64_t gain_Q16 = mOpusParser->mGain_Q16;
|
|
||||||
int samples = frames * channels;
|
|
||||||
for (int i = 0; i < samples; i++) {
|
|
||||||
int32_t val = static_cast<int32_t>((gain_Q16*buffer[i] + 32768)>>16);
|
|
||||||
buffer[i] = static_cast<AudioDataValue>(MOZ_CLIP_TO_15(val));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// No channel mapping for more than 8 channels.
|
|
||||||
if (channels > 8) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckedInt64 duration = FramesToUsecs(frames, rate);
|
|
||||||
if (!duration.isValid()) {
|
|
||||||
NS_WARNING("Int overflow converting WebM audio duration");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
CheckedInt64 time = startTime - mCodecDelay;
|
|
||||||
if (!time.isValid()) {
|
|
||||||
NS_WARNING("Int overflow shifting tstamp by codec delay");
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
AudioQueue().Push(new AudioData(mDecoder->GetResource()->Tell(),
|
|
||||||
time.value(),
|
|
||||||
duration.value(),
|
|
||||||
frames,
|
|
||||||
buffer.forget(),
|
|
||||||
mChannels,
|
|
||||||
rate));
|
|
||||||
|
|
||||||
mAudioFrames += frames;
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif /* MOZ_OPUS */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WebMReader::DecodeVorbis(const unsigned char* aData, size_t aLength,
|
||||||
|
int64_t aOffset, uint64_t aTstampUsecs,
|
||||||
|
int32_t* aTotalFrames)
|
||||||
|
{
|
||||||
|
ogg_packet opacket = InitOggPacket(aData, aLength, false, false, -1,
|
||||||
|
mPacketCount++);
|
||||||
|
|
||||||
|
if (vorbis_synthesis(&mVorbisBlock, &opacket) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vorbis_synthesis_blockin(&mVorbisDsp,
|
||||||
|
&mVorbisBlock) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VorbisPCMValue** pcm = 0;
|
||||||
|
int32_t frames = vorbis_synthesis_pcmout(&mVorbisDsp, &pcm);
|
||||||
|
// If the first packet of audio in the media produces no data, we
|
||||||
|
// still need to produce an AudioData for it so that the correct media
|
||||||
|
// start time is calculated. Otherwise we'd end up with a media start
|
||||||
|
// time derived from the timecode of the first packet that produced
|
||||||
|
// data.
|
||||||
|
if (frames == 0 && mAudioFrames == 0) {
|
||||||
|
AudioQueue().Push(new AudioData(aOffset, aTstampUsecs, 0, 0, nullptr,
|
||||||
|
mInfo.mAudio.mChannels,
|
||||||
|
mInfo.mAudio.mRate));
|
||||||
|
}
|
||||||
|
while (frames > 0) {
|
||||||
|
uint32_t channels = mInfo.mAudio.mChannels;
|
||||||
|
nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frames*channels]);
|
||||||
|
for (uint32_t j = 0; j < channels; ++j) {
|
||||||
|
VorbisPCMValue* channel = pcm[j];
|
||||||
|
for (uint32_t i = 0; i < uint32_t(frames); ++i) {
|
||||||
|
buffer[i*channels + j] = MOZ_CONVERT_VORBIS_SAMPLE(channel[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckedInt64 duration = FramesToUsecs(frames, mInfo.mAudio.mRate);
|
||||||
|
if (!duration.isValid()) {
|
||||||
|
NS_WARNING("Int overflow converting WebM audio duration");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CheckedInt64 total_duration = FramesToUsecs(*aTotalFrames,
|
||||||
|
mInfo.mAudio.mRate);
|
||||||
|
if (!total_duration.isValid()) {
|
||||||
|
NS_WARNING("Int overflow converting WebM audio total_duration");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckedInt64 time = total_duration + aTstampUsecs;
|
||||||
|
if (!time.isValid()) {
|
||||||
|
NS_WARNING("Int overflow adding total_duration and aTstampUsecs");
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
*aTotalFrames += frames;
|
||||||
|
AudioQueue().Push(new AudioData(aOffset,
|
||||||
|
time.value(),
|
||||||
|
duration.value(),
|
||||||
|
frames,
|
||||||
|
buffer.forget(),
|
||||||
|
mInfo.mAudio.mChannels,
|
||||||
|
mInfo.mAudio.mRate));
|
||||||
|
mAudioFrames += frames;
|
||||||
|
if (vorbis_synthesis_read(&mVorbisDsp, frames) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
frames = vorbis_synthesis_pcmout(&mVorbisDsp, &pcm);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MOZ_OPUS
|
||||||
|
bool WebMReader::DecodeOpus(const unsigned char* aData, size_t aLength,
|
||||||
|
int64_t aOffset, uint64_t aTstampUsecs,
|
||||||
|
nestegg_packet* aPacket)
|
||||||
|
{
|
||||||
|
uint32_t channels = mOpusParser->mChannels;
|
||||||
|
// No channel mapping for more than 8 channels.
|
||||||
|
if (channels > 8) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mPaddingDiscarded) {
|
||||||
|
// Discard padding should be used only on the final packet, so
|
||||||
|
// decoding after a padding discard is invalid.
|
||||||
|
LOG(PR_LOG_DEBUG, ("Opus error, discard padding on interstitial packet"));
|
||||||
|
GetCallback()->OnDecodeError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maximum value is 63*2880, so there's no chance of overflow.
|
||||||
|
int32_t frames_number = opus_packet_get_nb_frames(aData, aLength);
|
||||||
|
if (frames_number <= 0) {
|
||||||
|
return false; // Invalid packet header.
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t samples =
|
||||||
|
opus_packet_get_samples_per_frame(aData, opus_int32(mInfo.mAudio.mRate));
|
||||||
|
|
||||||
|
// A valid Opus packet must be between 2.5 and 120 ms long (48kHz).
|
||||||
|
int32_t frames = frames_number*samples;
|
||||||
|
if (frames < 120 || frames > 5760)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frames * channels]);
|
||||||
|
|
||||||
|
// Decode to the appropriate sample type.
|
||||||
|
#ifdef MOZ_SAMPLE_TYPE_FLOAT32
|
||||||
|
int ret = opus_multistream_decode_float(mOpusDecoder,
|
||||||
|
aData, aLength,
|
||||||
|
buffer, frames, false);
|
||||||
|
#else
|
||||||
|
int ret = opus_multistream_decode(mOpusDecoder,
|
||||||
|
aData, aLength,
|
||||||
|
buffer, frames, false);
|
||||||
|
#endif
|
||||||
|
if (ret < 0)
|
||||||
|
return false;
|
||||||
|
NS_ASSERTION(ret == frames, "Opus decoded too few audio samples");
|
||||||
|
CheckedInt64 startTime = aTstampUsecs;
|
||||||
|
|
||||||
|
// Trim the initial frames while the decoder is settling.
|
||||||
|
if (mSkip > 0) {
|
||||||
|
int32_t skipFrames = std::min<int32_t>(mSkip, frames);
|
||||||
|
int32_t keepFrames = frames - skipFrames;
|
||||||
|
LOG(PR_LOG_DEBUG, ("Opus decoder skipping %d of %d frames",
|
||||||
|
skipFrames, frames));
|
||||||
|
PodMove(buffer.get(),
|
||||||
|
buffer.get() + skipFrames * channels,
|
||||||
|
keepFrames * channels);
|
||||||
|
startTime = startTime + FramesToUsecs(skipFrames, mInfo.mAudio.mRate);
|
||||||
|
frames = keepFrames;
|
||||||
|
mSkip -= skipFrames;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t discardPadding = 0;
|
||||||
|
(void) nestegg_packet_discard_padding(aPacket, &discardPadding);
|
||||||
|
if (discardPadding < 0) {
|
||||||
|
// Negative discard padding is invalid.
|
||||||
|
LOG(PR_LOG_DEBUG, ("Opus error, negative discard padding"));
|
||||||
|
GetCallback()->OnDecodeError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (discardPadding > 0) {
|
||||||
|
CheckedInt64 discardFrames = UsecsToFrames(discardPadding / NS_PER_USEC,
|
||||||
|
mInfo.mAudio.mRate);
|
||||||
|
if (!discardFrames.isValid()) {
|
||||||
|
NS_WARNING("Int overflow in DiscardPadding");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (discardFrames.value() > frames) {
|
||||||
|
// Discarding more than the entire packet is invalid.
|
||||||
|
LOG(PR_LOG_DEBUG, ("Opus error, discard padding larger than packet"));
|
||||||
|
GetCallback()->OnDecodeError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LOG(PR_LOG_DEBUG, ("Opus decoder discarding %d of %d frames",
|
||||||
|
int32_t(discardFrames.value()), frames));
|
||||||
|
// Padding discard is only supposed to happen on the final packet.
|
||||||
|
// Record the discard so we can return an error if another packet is
|
||||||
|
// decoded.
|
||||||
|
mPaddingDiscarded = true;
|
||||||
|
int32_t keepFrames = frames - discardFrames.value();
|
||||||
|
frames = keepFrames;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the header gain if one was specified.
|
||||||
|
#ifdef MOZ_SAMPLE_TYPE_FLOAT32
|
||||||
|
if (mOpusParser->mGain != 1.0f) {
|
||||||
|
float gain = mOpusParser->mGain;
|
||||||
|
int samples = frames * channels;
|
||||||
|
for (int i = 0; i < samples; i++) {
|
||||||
|
buffer[i] *= gain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (mOpusParser->mGain_Q16 != 65536) {
|
||||||
|
int64_t gain_Q16 = mOpusParser->mGain_Q16;
|
||||||
|
int samples = frames * channels;
|
||||||
|
for (int i = 0; i < samples; i++) {
|
||||||
|
int32_t val = static_cast<int32_t>((gain_Q16*buffer[i] + 32768)>>16);
|
||||||
|
buffer[i] = static_cast<AudioDataValue>(MOZ_CLIP_TO_15(val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CheckedInt64 duration = FramesToUsecs(frames, mInfo.mAudio.mRate);
|
||||||
|
if (!duration.isValid()) {
|
||||||
|
NS_WARNING("Int overflow converting WebM audio duration");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CheckedInt64 time = startTime - mCodecDelay;
|
||||||
|
if (!time.isValid()) {
|
||||||
|
NS_WARNING("Int overflow shifting tstamp by codec delay");
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
AudioQueue().Push(new AudioData(aOffset,
|
||||||
|
time.value(),
|
||||||
|
duration.value(),
|
||||||
|
frames,
|
||||||
|
buffer.forget(),
|
||||||
|
mInfo.mAudio.mChannels,
|
||||||
|
mInfo.mAudio.mRate));
|
||||||
|
|
||||||
|
mAudioFrames += frames;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif /* MOZ_OPUS */
|
||||||
|
|
||||||
nsReturnRef<NesteggPacketHolder> WebMReader::NextPacket(TrackType aTrackType)
|
nsReturnRef<NesteggPacketHolder> WebMReader::NextPacket(TrackType aTrackType)
|
||||||
{
|
{
|
||||||
// The packet queue that packets will be pushed on if they
|
// The packet queue that packets will be pushed on if they
|
||||||
@ -935,9 +981,9 @@ bool WebMReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
|||||||
vpx_image_t *img;
|
vpx_image_t *img;
|
||||||
|
|
||||||
while ((img = vpx_codec_get_frame(&mVPX, &iter))) {
|
while ((img = vpx_codec_get_frame(&mVPX, &iter))) {
|
||||||
NS_ASSERTION(img->fmt == VPX_IMG_FMT_I420, "WebM image format is not I420");
|
NS_ASSERTION(img->fmt == VPX_IMG_FMT_I420, "WebM image format not I420");
|
||||||
|
|
||||||
// Chroma shifts are rounded down as per the decoding examples in the VP8 SDK
|
// Chroma shifts are rounded down as per the decoding examples in the SDK
|
||||||
VideoData::YCbCrBuffer b;
|
VideoData::YCbCrBuffer b;
|
||||||
b.mPlanes[0].mData = img->planes[0];
|
b.mPlanes[0].mData = img->planes[0];
|
||||||
b.mPlanes[0].mStride = img->stride[0];
|
b.mPlanes[0].mStride = img->stride[0];
|
||||||
@ -960,9 +1006,10 @@ bool WebMReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
|||||||
IntRect picture = ToIntRect(mPicture);
|
IntRect picture = ToIntRect(mPicture);
|
||||||
if (img->d_w != static_cast<uint32_t>(mInitialFrame.width) ||
|
if (img->d_w != static_cast<uint32_t>(mInitialFrame.width) ||
|
||||||
img->d_h != static_cast<uint32_t>(mInitialFrame.height)) {
|
img->d_h != static_cast<uint32_t>(mInitialFrame.height)) {
|
||||||
// Frame size is different from what the container reports. This is legal
|
// Frame size is different from what the container reports. This is
|
||||||
// in WebM, and we will preserve the ratio of the crop rectangle as it
|
// legal in WebM, and we will preserve the ratio of the crop rectangle
|
||||||
// was reported relative to the picture size reported by the container.
|
// as it was reported relative to the picture size reported by the
|
||||||
|
// container.
|
||||||
picture.x = (mPicture.x * img->d_w) / mInitialFrame.width;
|
picture.x = (mPicture.x * img->d_w) / mInitialFrame.width;
|
||||||
picture.y = (mPicture.y * img->d_h) / mInitialFrame.height;
|
picture.y = (mPicture.y * img->d_h) / mInitialFrame.height;
|
||||||
picture.width = (img->d_w * mPicture.width) / mInitialFrame.width;
|
picture.width = (img->d_w * mPicture.width) / mInitialFrame.width;
|
||||||
@ -973,7 +1020,7 @@ bool WebMReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
|||||||
mDecoder->GetImageContainer(),
|
mDecoder->GetImageContainer(),
|
||||||
holder->mOffset,
|
holder->mOffset,
|
||||||
tstamp_usecs,
|
tstamp_usecs,
|
||||||
(next_tstamp / NS_PER_USEC) - tstamp_usecs,
|
(next_tstamp / NS_PER_USEC)-tstamp_usecs,
|
||||||
b,
|
b,
|
||||||
si.is_kf,
|
si.is_kf,
|
||||||
-1,
|
-1,
|
||||||
@ -1012,10 +1059,14 @@ nsresult WebMReader::Seek(int64_t aTarget, int64_t aStartTime, int64_t aEndTime,
|
|||||||
uint64_t target = aTarget * NS_PER_USEC;
|
uint64_t target = aTarget * NS_PER_USEC;
|
||||||
|
|
||||||
if (mSeekPreroll) {
|
if (mSeekPreroll) {
|
||||||
target = std::max(static_cast<uint64_t>(aStartTime * NS_PER_USEC), target - mSeekPreroll);
|
target = std::max(uint64_t(aStartTime * NS_PER_USEC),
|
||||||
|
target - mSeekPreroll);
|
||||||
}
|
}
|
||||||
int r = nestegg_track_seek(mContext, trackToSeek, target);
|
int r = nestegg_track_seek(mContext, trackToSeek, target);
|
||||||
if (r != 0) {
|
if (r != 0) {
|
||||||
|
LOG(PR_LOG_DEBUG, ("Reader [%p]: track_seek for track %u failed, r=%d",
|
||||||
|
this, trackToSeek, r));
|
||||||
|
|
||||||
// Try seeking directly based on cluster information in memory.
|
// Try seeking directly based on cluster information in memory.
|
||||||
int64_t offset = 0;
|
int64_t offset = 0;
|
||||||
bool rv = mBufferedState->GetOffsetForTime(target, &offset);
|
bool rv = mBufferedState->GetOffsetForTime(target, &offset);
|
||||||
@ -1024,8 +1075,8 @@ nsresult WebMReader::Seek(int64_t aTarget, int64_t aStartTime, int64_t aEndTime,
|
|||||||
}
|
}
|
||||||
|
|
||||||
r = nestegg_offset_seek(mContext, offset);
|
r = nestegg_offset_seek(mContext, offset);
|
||||||
LOG(PR_LOG_DEBUG, ("Reader [%p]: track_seek for %u failed, offset_seek to %lld r=%d",
|
LOG(PR_LOG_DEBUG, ("Reader [%p]: attempted offset_seek to %lld r=%d",
|
||||||
this, trackToSeek, offset, r));
|
this, offset, r));
|
||||||
if (r != 0) {
|
if (r != 0) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
@ -1072,7 +1123,8 @@ nsresult WebMReader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime)
|
|||||||
double endTime = (end - startOffset) / NS_PER_S;
|
double endTime = (end - startOffset) / NS_PER_S;
|
||||||
// If this range extends to the end of the file, the true end time
|
// If this range extends to the end of the file, the true end time
|
||||||
// is the file's duration.
|
// is the file's duration.
|
||||||
if (mContext && resource->IsDataCachedToEndOfResource(ranges[index].mStart)) {
|
if (mContext &&
|
||||||
|
resource->IsDataCachedToEndOfResource(ranges[index].mStart)) {
|
||||||
uint64_t duration = 0;
|
uint64_t duration = 0;
|
||||||
if (nestegg_duration(mContext, &duration) == 0) {
|
if (nestegg_duration(mContext, &duration) == 0) {
|
||||||
endTime = duration / NS_PER_S;
|
endTime = duration / NS_PER_S;
|
||||||
@ -1086,7 +1138,8 @@ nsresult WebMReader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime)
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebMReader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
|
void WebMReader::NotifyDataArrived(const char* aBuffer, uint32_t aLength,
|
||||||
|
int64_t aOffset)
|
||||||
{
|
{
|
||||||
mBufferedState->NotifyDataArrived(aBuffer, aLength, aOffset);
|
mBufferedState->NotifyDataArrived(aBuffer, aLength, aOffset);
|
||||||
}
|
}
|
||||||
|
@ -134,9 +134,11 @@ public:
|
|||||||
|
|
||||||
virtual nsresult ReadMetadata(MediaInfo* aInfo,
|
virtual nsresult ReadMetadata(MediaInfo* aInfo,
|
||||||
MetadataTags** aTags);
|
MetadataTags** aTags);
|
||||||
virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
|
virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
|
||||||
|
int64_t aCurrentTime);
|
||||||
virtual nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime);
|
virtual nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime);
|
||||||
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
|
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength,
|
||||||
|
int64_t aOffset);
|
||||||
virtual int64_t GetEvictionOffset(double aTime);
|
virtual int64_t GetEvictionOffset(double aTime);
|
||||||
|
|
||||||
virtual bool IsMediaSeekable() MOZ_OVERRIDE;
|
virtual bool IsMediaSeekable() MOZ_OVERRIDE;
|
||||||
@ -157,13 +159,6 @@ protected:
|
|||||||
// Pushes a packet to the front of the video packet queue.
|
// Pushes a packet to the front of the video packet queue.
|
||||||
virtual void PushVideoPacket(NesteggPacketHolder* aItem);
|
virtual void PushVideoPacket(NesteggPacketHolder* aItem);
|
||||||
|
|
||||||
// Returns an initialized ogg packet with data obtained from the WebM container.
|
|
||||||
ogg_packet InitOggPacket(unsigned char* aData,
|
|
||||||
size_t aLength,
|
|
||||||
bool aBOS,
|
|
||||||
bool aEOS,
|
|
||||||
int64_t aGranulepos);
|
|
||||||
|
|
||||||
#ifdef MOZ_OPUS
|
#ifdef MOZ_OPUS
|
||||||
// Setup opus decoder
|
// Setup opus decoder
|
||||||
bool InitOpusDecoder();
|
bool InitOpusDecoder();
|
||||||
@ -176,6 +171,14 @@ protected:
|
|||||||
// must be held during this call. The caller is responsible for freeing
|
// must be held during this call. The caller is responsible for freeing
|
||||||
// aPacket.
|
// aPacket.
|
||||||
bool DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset);
|
bool DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset);
|
||||||
|
bool DecodeVorbis(const unsigned char* aData, size_t aLength,
|
||||||
|
int64_t aOffset, uint64_t aTstampUsecs,
|
||||||
|
int32_t* aTotalFrames);
|
||||||
|
#ifdef MOZ_OPUS
|
||||||
|
bool DecodeOpus(const unsigned char* aData, size_t aLength,
|
||||||
|
int64_t aOffset, uint64_t aTstampUsecs,
|
||||||
|
nestegg_packet* aPacket);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Release context and set to null. Called when an error occurs during
|
// Release context and set to null. Called when an error occurs during
|
||||||
// reading metadata or destruction of the reader itself.
|
// reading metadata or destruction of the reader itself.
|
||||||
@ -194,16 +197,14 @@ private:
|
|||||||
vorbis_comment mVorbisComment;
|
vorbis_comment mVorbisComment;
|
||||||
vorbis_dsp_state mVorbisDsp;
|
vorbis_dsp_state mVorbisDsp;
|
||||||
vorbis_block mVorbisBlock;
|
vorbis_block mVorbisBlock;
|
||||||
uint32_t mPacketCount;
|
int64_t mPacketCount;
|
||||||
uint32_t mChannels;
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef MOZ_OPUS
|
#ifdef MOZ_OPUS
|
||||||
// Opus decoder state
|
// Opus decoder state
|
||||||
nsAutoPtr<OpusParser> mOpusParser;
|
nsAutoPtr<OpusParser> mOpusParser;
|
||||||
OpusMSDecoder *mOpusDecoder;
|
OpusMSDecoder *mOpusDecoder;
|
||||||
int mSkip; // Number of samples left to trim before playback.
|
uint16_t mSkip; // Samples left to trim before playback.
|
||||||
uint64_t mSeekPreroll; // Number of nanoseconds that must be discarded after seeking.
|
uint64_t mSeekPreroll; // Nanoseconds to discard after seeking.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Queue of video and audio packets that have been read but not decoded. These
|
// Queue of video and audio packets that have been read but not decoded. These
|
||||||
@ -247,6 +248,13 @@ private:
|
|||||||
// Booleans to indicate if we have audio and/or video data
|
// Booleans to indicate if we have audio and/or video data
|
||||||
bool mHasVideo;
|
bool mHasVideo;
|
||||||
bool mHasAudio;
|
bool mHasAudio;
|
||||||
|
|
||||||
|
#ifdef MOZ_OPUS
|
||||||
|
// Opus padding should only be discarded on the final packet. Once this
|
||||||
|
// is set to true, if the reader attempts to decode any further packets it
|
||||||
|
// will raise an error so we can indicate that the file is invalid.
|
||||||
|
bool mPaddingDiscarded;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "CanvasRenderingContext2D.h"
|
#include "CanvasRenderingContext2D.h"
|
||||||
|
|
||||||
|
#include "mozilla/gfx/Helpers.h"
|
||||||
#include "nsXULElement.h"
|
#include "nsXULElement.h"
|
||||||
|
|
||||||
#include "nsIServiceManager.h"
|
#include "nsIServiceManager.h"
|
||||||
@ -376,7 +377,7 @@ public:
|
|||||||
RefPtr<SourceSurface> strokePaint =
|
RefPtr<SourceSurface> strokePaint =
|
||||||
DoSourcePaint(mStrokePaintRect, CanvasRenderingContext2D::Style::STROKE);
|
DoSourcePaint(mStrokePaintRect, CanvasRenderingContext2D::Style::STROKE);
|
||||||
|
|
||||||
AutoSaveTransform autoSaveTransform(mFinalTarget);
|
AutoRestoreTransform autoRestoreTransform(mFinalTarget);
|
||||||
mFinalTarget->SetTransform(Matrix());
|
mFinalTarget->SetTransform(Matrix());
|
||||||
|
|
||||||
mgfx::FilterSupport::RenderFilterDescription(
|
mgfx::FilterSupport::RenderFilterDescription(
|
||||||
@ -2168,7 +2169,8 @@ public:
|
|||||||
gfxTextPerfMetrics* tp = mPresContext->GetTextPerfMetrics();
|
gfxTextPerfMetrics* tp = mPresContext->GetTextPerfMetrics();
|
||||||
nsRefPtr<nsFontMetrics> fontMetrics;
|
nsRefPtr<nsFontMetrics> fontMetrics;
|
||||||
nsDeviceContext* dc = mPresContext->DeviceContext();
|
nsDeviceContext* dc = mPresContext->DeviceContext();
|
||||||
dc->GetMetricsFor(mFont, mFontLanguage, nullptr, tp,
|
dc->GetMetricsFor(mFont, mFontLanguage, gfxFont::eHorizontal,
|
||||||
|
nullptr, tp,
|
||||||
*getter_AddRefs(fontMetrics));
|
*getter_AddRefs(fontMetrics));
|
||||||
return NSAppUnitsToFloatPixels(fontMetrics->XHeight(),
|
return NSAppUnitsToFloatPixels(fontMetrics->XHeight(),
|
||||||
nsPresContext::AppUnitsPerCSSPixel());
|
nsPresContext::AppUnitsPerCSSPixel());
|
||||||
@ -3442,7 +3444,7 @@ CanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText,
|
|||||||
// offset pt.y based on text baseline
|
// offset pt.y based on text baseline
|
||||||
processor.mFontgrp->UpdateUserFonts(); // ensure user font generation is current
|
processor.mFontgrp->UpdateUserFonts(); // ensure user font generation is current
|
||||||
const gfxFont::Metrics& fontMetrics =
|
const gfxFont::Metrics& fontMetrics =
|
||||||
processor.mFontgrp->GetFirstValidFont()->GetMetrics();
|
processor.mFontgrp->GetFirstValidFont()->GetMetrics(gfxFont::eHorizontal); // XXX vertical?
|
||||||
|
|
||||||
gfxFloat anchorY;
|
gfxFloat anchorY;
|
||||||
|
|
||||||
@ -3983,6 +3985,10 @@ CanvasRenderingContext2D::DrawDirectlyToCanvas(
|
|||||||
std::ceil(imgSize.height * scale.height));
|
std::ceil(imgSize.height * scale.height));
|
||||||
src.Scale(scale.width, scale.height);
|
src.Scale(scale.width, scale.height);
|
||||||
|
|
||||||
|
// We're wrapping tempTarget's (our) DrawTarget here, so we need to restore
|
||||||
|
// the matrix even though this is a temp gfxContext.
|
||||||
|
AutoRestoreTransform autoRestoreTransform(mTarget);
|
||||||
|
|
||||||
nsRefPtr<gfxContext> context = new gfxContext(tempTarget);
|
nsRefPtr<gfxContext> context = new gfxContext(tempTarget);
|
||||||
context->SetMatrix(contextMatrix.
|
context->SetMatrix(contextMatrix.
|
||||||
Scale(1.0 / contextScale.width,
|
Scale(1.0 / contextScale.width,
|
||||||
|
@ -297,9 +297,13 @@ Headers::IsForbiddenResponseHeader(const nsACString& aName) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Headers::Fill(const Headers& aInit, ErrorResult&)
|
Headers::Fill(const Headers& aInit, ErrorResult& aRv)
|
||||||
{
|
{
|
||||||
mList = aInit.mList;
|
const nsTArray<Entry>& list = aInit.mList;
|
||||||
|
for (uint32_t i = 0; i < list.Length() && !aRv.Failed(); ++i) {
|
||||||
|
const Entry& entry = list[i];
|
||||||
|
Append(entry.mName, entry.mValue, aRv);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -3910,20 +3910,7 @@ protected:
|
|||||||
FactoryOp(Factory* aFactory,
|
FactoryOp(Factory* aFactory,
|
||||||
already_AddRefed<ContentParent> aContentParent,
|
already_AddRefed<ContentParent> aContentParent,
|
||||||
const CommonFactoryRequestParams& aCommonParams,
|
const CommonFactoryRequestParams& aCommonParams,
|
||||||
bool aDeleting)
|
bool aDeleting);
|
||||||
: mFactory(aFactory)
|
|
||||||
, mContentParent(Move(aContentParent))
|
|
||||||
, mCommonParams(aCommonParams)
|
|
||||||
, mState(State_Initial)
|
|
||||||
, mStoragePrivilege(mozilla::dom::quota::Content)
|
|
||||||
, mEnforcingQuota(true)
|
|
||||||
, mDeleting(aDeleting)
|
|
||||||
, mBlockedQuotaManager(false)
|
|
||||||
, mChromeWriteAccessAllowed(false)
|
|
||||||
{
|
|
||||||
AssertIsOnBackgroundThread();
|
|
||||||
MOZ_ASSERT(aFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual
|
virtual
|
||||||
~FactoryOp()
|
~FactoryOp()
|
||||||
@ -5075,20 +5062,42 @@ public:
|
|||||||
static QuotaClient*
|
static QuotaClient*
|
||||||
GetInstance()
|
GetInstance()
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
return sInstance;
|
return sInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static bool
|
||||||
NoteBackgroundThread(nsIEventTarget* aBackgroundThread);
|
IsShuttingDownOnMainThread()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
if (sInstance) {
|
||||||
|
return sInstance->mShutdownRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QuotaManager::IsShuttingDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
IsShuttingDownOnNonMainThread()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(!NS_IsMainThread());
|
||||||
|
|
||||||
|
return QuotaManager::IsShuttingDown();
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
HasShutDown() const
|
IsShuttingDown() const
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
return mShutdownRequested;
|
return mShutdownRequested;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
NoteBackgroundThread(nsIEventTarget* aBackgroundThread);
|
||||||
|
|
||||||
NS_INLINE_DECL_REFCOUNTING(QuotaClient)
|
NS_INLINE_DECL_REFCOUNTING(QuotaClient)
|
||||||
|
|
||||||
virtual mozilla::dom::quota::Client::Type
|
virtual mozilla::dom::quota::Client::Type
|
||||||
@ -5557,6 +5566,10 @@ AllocPBackgroundIDBFactoryParent(PBackgroundParent* aManager,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread())) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
nsRefPtr<Factory> actor = Factory::Create(aOptionalWindowId);
|
nsRefPtr<Factory> actor = Factory::Create(aOptionalWindowId);
|
||||||
return actor.forget().take();
|
return actor.forget().take();
|
||||||
}
|
}
|
||||||
@ -5568,6 +5581,7 @@ RecvPBackgroundIDBFactoryConstructor(PBackgroundParent* /* aManager */,
|
|||||||
{
|
{
|
||||||
AssertIsOnBackgroundThread();
|
AssertIsOnBackgroundThread();
|
||||||
MOZ_ASSERT(aActor);
|
MOZ_ASSERT(aActor);
|
||||||
|
MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -5751,6 +5765,7 @@ Factory::Factory(const OptionalWindowId& aOptionalWindowId)
|
|||||||
, mActorDestroyed(false)
|
, mActorDestroyed(false)
|
||||||
{
|
{
|
||||||
AssertIsOnBackgroundThread();
|
AssertIsOnBackgroundThread();
|
||||||
|
MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread());
|
||||||
}
|
}
|
||||||
|
|
||||||
Factory::~Factory()
|
Factory::~Factory()
|
||||||
@ -5763,6 +5778,7 @@ already_AddRefed<Factory>
|
|||||||
Factory::Create(const OptionalWindowId& aOptionalWindowId)
|
Factory::Create(const OptionalWindowId& aOptionalWindowId)
|
||||||
{
|
{
|
||||||
AssertIsOnBackgroundThread();
|
AssertIsOnBackgroundThread();
|
||||||
|
MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread());
|
||||||
|
|
||||||
// If this is the first instance then we need to do some initialization.
|
// If this is the first instance then we need to do some initialization.
|
||||||
if (!sFactoryInstanceCount) {
|
if (!sFactoryInstanceCount) {
|
||||||
@ -5872,6 +5888,10 @@ Factory::AllocPBackgroundIDBFactoryRequestParent(
|
|||||||
AssertIsOnBackgroundThread();
|
AssertIsOnBackgroundThread();
|
||||||
MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None);
|
MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None);
|
||||||
|
|
||||||
|
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread())) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
const CommonFactoryRequestParams* commonParams;
|
const CommonFactoryRequestParams* commonParams;
|
||||||
|
|
||||||
switch (aParams.type()) {
|
switch (aParams.type()) {
|
||||||
@ -5933,6 +5953,7 @@ Factory::RecvPBackgroundIDBFactoryRequestConstructor(
|
|||||||
AssertIsOnBackgroundThread();
|
AssertIsOnBackgroundThread();
|
||||||
MOZ_ASSERT(aActor);
|
MOZ_ASSERT(aActor);
|
||||||
MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None);
|
MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None);
|
||||||
|
MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread());
|
||||||
|
|
||||||
auto* op = static_cast<FactoryOp*>(aActor);
|
auto* op = static_cast<FactoryOp*>(aActor);
|
||||||
|
|
||||||
@ -10304,6 +10325,25 @@ AutoSetProgressHandler::Register(
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FactoryOp::FactoryOp(Factory* aFactory,
|
||||||
|
already_AddRefed<ContentParent> aContentParent,
|
||||||
|
const CommonFactoryRequestParams& aCommonParams,
|
||||||
|
bool aDeleting)
|
||||||
|
: mFactory(aFactory)
|
||||||
|
, mContentParent(Move(aContentParent))
|
||||||
|
, mCommonParams(aCommonParams)
|
||||||
|
, mState(State_Initial)
|
||||||
|
, mStoragePrivilege(mozilla::dom::quota::Content)
|
||||||
|
, mEnforcingQuota(true)
|
||||||
|
, mDeleting(aDeleting)
|
||||||
|
, mBlockedQuotaManager(false)
|
||||||
|
, mChromeWriteAccessAllowed(false)
|
||||||
|
{
|
||||||
|
AssertIsOnBackgroundThread();
|
||||||
|
MOZ_ASSERT(aFactory);
|
||||||
|
MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread());
|
||||||
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
FactoryOp::Open()
|
FactoryOp::Open()
|
||||||
{
|
{
|
||||||
@ -10314,7 +10354,8 @@ FactoryOp::Open()
|
|||||||
nsRefPtr<ContentParent> contentParent;
|
nsRefPtr<ContentParent> contentParent;
|
||||||
mContentParent.swap(contentParent);
|
mContentParent.swap(contentParent);
|
||||||
|
|
||||||
if (!OperationMayProceed()) {
|
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnMainThread()) ||
|
||||||
|
!OperationMayProceed()) {
|
||||||
IDB_REPORT_INTERNAL_ERR();
|
IDB_REPORT_INTERNAL_ERR();
|
||||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
}
|
}
|
||||||
@ -10392,7 +10433,8 @@ FactoryOp::RetryCheckPermission()
|
|||||||
nsRefPtr<ContentParent> contentParent;
|
nsRefPtr<ContentParent> contentParent;
|
||||||
mContentParent.swap(contentParent);
|
mContentParent.swap(contentParent);
|
||||||
|
|
||||||
if (!OperationMayProceed()) {
|
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnMainThread()) ||
|
||||||
|
!OperationMayProceed()) {
|
||||||
IDB_REPORT_INTERNAL_ERR();
|
IDB_REPORT_INTERNAL_ERR();
|
||||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
}
|
}
|
||||||
@ -10428,16 +10470,14 @@ FactoryOp::SendToIOThread()
|
|||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
MOZ_ASSERT(mState == State_OpenPending);
|
MOZ_ASSERT(mState == State_OpenPending);
|
||||||
|
|
||||||
if (!OperationMayProceed()) {
|
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnMainThread()) ||
|
||||||
|
!OperationMayProceed()) {
|
||||||
IDB_REPORT_INTERNAL_ERR();
|
IDB_REPORT_INTERNAL_ERR();
|
||||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
QuotaManager* quotaManager = QuotaManager::Get();
|
QuotaManager* quotaManager = QuotaManager::Get();
|
||||||
if (NS_WARN_IF(!quotaManager)) {
|
MOZ_ASSERT(quotaManager);
|
||||||
IDB_REPORT_INTERNAL_ERR();
|
|
||||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must set this before dispatching otherwise we will race with the IO thread.
|
// Must set this before dispatching otherwise we will race with the IO thread.
|
||||||
mState = State_DatabaseWorkOpen;
|
mState = State_DatabaseWorkOpen;
|
||||||
@ -10819,6 +10859,7 @@ FactoryOp::FinishOpen()
|
|||||||
MOZ_ASSERT(!mDatabaseId.IsEmpty());
|
MOZ_ASSERT(!mDatabaseId.IsEmpty());
|
||||||
MOZ_ASSERT(!mBlockedQuotaManager);
|
MOZ_ASSERT(!mBlockedQuotaManager);
|
||||||
MOZ_ASSERT(!mContentParent);
|
MOZ_ASSERT(!mContentParent);
|
||||||
|
MOZ_ASSERT(!QuotaClient::IsShuttingDownOnMainThread());
|
||||||
|
|
||||||
PersistenceType persistenceType = mCommonParams.metadata().persistenceType();
|
PersistenceType persistenceType = mCommonParams.metadata().persistenceType();
|
||||||
|
|
||||||
@ -10830,10 +10871,8 @@ FactoryOp::FinishOpen()
|
|||||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
QuotaManager* quotaManager;
|
QuotaManager* quotaManager = QuotaManager::GetOrCreate();
|
||||||
|
if (NS_WARN_IF(!quotaManager)) {
|
||||||
if (QuotaManager::IsShuttingDown() ||
|
|
||||||
!(quotaManager = QuotaManager::GetOrCreate())) {
|
|
||||||
IDB_REPORT_INTERNAL_ERR();
|
IDB_REPORT_INTERNAL_ERR();
|
||||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
}
|
}
|
||||||
@ -11002,9 +11041,8 @@ OpenDatabaseOp::QuotaManagerOpen()
|
|||||||
MOZ_ASSERT(!mOfflineStorage);
|
MOZ_ASSERT(!mOfflineStorage);
|
||||||
|
|
||||||
QuotaClient* quotaClient = QuotaClient::GetInstance();
|
QuotaClient* quotaClient = QuotaClient::GetInstance();
|
||||||
MOZ_ASSERT(quotaClient);
|
if (NS_WARN_IF(!quotaClient) ||
|
||||||
|
NS_WARN_IF(quotaClient->IsShuttingDown())) {
|
||||||
if (NS_WARN_IF(quotaClient->HasShutDown())) {
|
|
||||||
IDB_REPORT_INTERNAL_ERR();
|
IDB_REPORT_INTERNAL_ERR();
|
||||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
}
|
}
|
||||||
@ -11051,7 +11089,8 @@ OpenDatabaseOp::DoDatabaseWork()
|
|||||||
"OpenDatabaseHelper::DoDatabaseWork",
|
"OpenDatabaseHelper::DoDatabaseWork",
|
||||||
js::ProfileEntry::Category::STORAGE);
|
js::ProfileEntry::Category::STORAGE);
|
||||||
|
|
||||||
if (NS_WARN_IF(QuotaManager::IsShuttingDown()) || !OperationMayProceed()) {
|
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) ||
|
||||||
|
!OperationMayProceed()) {
|
||||||
IDB_REPORT_INTERNAL_ERR();
|
IDB_REPORT_INTERNAL_ERR();
|
||||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
}
|
}
|
||||||
@ -11502,7 +11541,9 @@ OpenDatabaseOp::BeginVersionChange()
|
|||||||
MOZ_ASSERT(!mDatabase);
|
MOZ_ASSERT(!mDatabase);
|
||||||
MOZ_ASSERT(!mVersionChangeTransaction);
|
MOZ_ASSERT(!mVersionChangeTransaction);
|
||||||
|
|
||||||
if (IsActorDestroyed()) {
|
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) ||
|
||||||
|
!OperationMayProceed() ||
|
||||||
|
IsActorDestroyed()) {
|
||||||
IDB_REPORT_INTERNAL_ERR();
|
IDB_REPORT_INTERNAL_ERR();
|
||||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
}
|
}
|
||||||
@ -11658,7 +11699,9 @@ OpenDatabaseOp::SendUpgradeNeeded()
|
|||||||
MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
|
MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
|
||||||
MOZ_ASSERT_IF(!IsActorDestroyed(), mDatabase);
|
MOZ_ASSERT_IF(!IsActorDestroyed(), mDatabase);
|
||||||
|
|
||||||
if (IsActorDestroyed()) {
|
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) ||
|
||||||
|
!OperationMayProceed() ||
|
||||||
|
IsActorDestroyed()) {
|
||||||
IDB_REPORT_INTERNAL_ERR();
|
IDB_REPORT_INTERNAL_ERR();
|
||||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
}
|
}
|
||||||
@ -12066,6 +12109,12 @@ VersionChangeOp::DoDatabaseWork(TransactionBase* aTransaction)
|
|||||||
aTransaction->AssertIsOnTransactionThread();
|
aTransaction->AssertIsOnTransactionThread();
|
||||||
MOZ_ASSERT(mOpenDatabaseOp->mState == State_DatabaseWorkVersionChange);
|
MOZ_ASSERT(mOpenDatabaseOp->mState == State_DatabaseWorkVersionChange);
|
||||||
|
|
||||||
|
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) ||
|
||||||
|
!OperationMayProceed()) {
|
||||||
|
IDB_REPORT_INTERNAL_ERR();
|
||||||
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
PROFILER_LABEL("IndexedDB",
|
PROFILER_LABEL("IndexedDB",
|
||||||
"VersionChangeOp::DoDatabaseWork",
|
"VersionChangeOp::DoDatabaseWork",
|
||||||
js::ProfileEntry::Category::STORAGE);
|
js::ProfileEntry::Category::STORAGE);
|
||||||
@ -12267,7 +12316,8 @@ DeleteDatabaseOp::DoDatabaseWork()
|
|||||||
"DeleteDatabaseOp::DoDatabaseWork",
|
"DeleteDatabaseOp::DoDatabaseWork",
|
||||||
js::ProfileEntry::Category::STORAGE);
|
js::ProfileEntry::Category::STORAGE);
|
||||||
|
|
||||||
if (!OperationMayProceed()) {
|
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) ||
|
||||||
|
!OperationMayProceed()) {
|
||||||
IDB_REPORT_INTERNAL_ERR();
|
IDB_REPORT_INTERNAL_ERR();
|
||||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
}
|
}
|
||||||
@ -12343,7 +12393,9 @@ DeleteDatabaseOp::BeginVersionChange()
|
|||||||
MOZ_ASSERT(mState == State_BeginVersionChange);
|
MOZ_ASSERT(mState == State_BeginVersionChange);
|
||||||
MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
|
MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
|
||||||
|
|
||||||
if (IsActorDestroyed()) {
|
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) ||
|
||||||
|
!OperationMayProceed() ||
|
||||||
|
IsActorDestroyed()) {
|
||||||
IDB_REPORT_INTERNAL_ERR();
|
IDB_REPORT_INTERNAL_ERR();
|
||||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
}
|
}
|
||||||
@ -12381,7 +12433,9 @@ DeleteDatabaseOp::DispatchToWorkThread()
|
|||||||
MOZ_ASSERT(mState == State_WaitingForTransactionsToComplete);
|
MOZ_ASSERT(mState == State_WaitingForTransactionsToComplete);
|
||||||
MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
|
MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
|
||||||
|
|
||||||
if (IsActorDestroyed()) {
|
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) ||
|
||||||
|
!OperationMayProceed() ||
|
||||||
|
IsActorDestroyed()) {
|
||||||
IDB_REPORT_INTERNAL_ERR();
|
IDB_REPORT_INTERNAL_ERR();
|
||||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
}
|
}
|
||||||
@ -12474,6 +12528,12 @@ VersionChangeOp::RunOnMainThread()
|
|||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
MOZ_ASSERT(mDeleteDatabaseOp->mState == State_DatabaseWorkVersionChange);
|
MOZ_ASSERT(mDeleteDatabaseOp->mState == State_DatabaseWorkVersionChange);
|
||||||
|
|
||||||
|
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnMainThread()) ||
|
||||||
|
!OperationMayProceed()) {
|
||||||
|
IDB_REPORT_INTERNAL_ERR();
|
||||||
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
QuotaManager* quotaManager = QuotaManager::Get();
|
QuotaManager* quotaManager = QuotaManager::Get();
|
||||||
MOZ_ASSERT(quotaManager);
|
MOZ_ASSERT(quotaManager);
|
||||||
|
|
||||||
@ -12497,7 +12557,8 @@ VersionChangeOp::RunOnIOThread()
|
|||||||
"DeleteDatabaseOp::VersionChangeOp::RunOnIOThread",
|
"DeleteDatabaseOp::VersionChangeOp::RunOnIOThread",
|
||||||
js::ProfileEntry::Category::STORAGE);
|
js::ProfileEntry::Category::STORAGE);
|
||||||
|
|
||||||
if (!OperationMayProceed()) {
|
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) ||
|
||||||
|
!OperationMayProceed()) {
|
||||||
IDB_REPORT_INTERNAL_ERR();
|
IDB_REPORT_INTERNAL_ERR();
|
||||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "TabChild.h"
|
#include "TabChild.h"
|
||||||
|
|
||||||
#include "mozilla/Attributes.h"
|
#include "mozilla/Attributes.h"
|
||||||
|
#include "mozilla/a11y/DocAccessibleChild.h"
|
||||||
#include "mozilla/Preferences.h"
|
#include "mozilla/Preferences.h"
|
||||||
#include "mozilla/dom/ContentBridgeChild.h"
|
#include "mozilla/dom/ContentBridgeChild.h"
|
||||||
#include "mozilla/dom/ContentBridgeParent.h"
|
#include "mozilla/dom/ContentBridgeParent.h"
|
||||||
@ -707,6 +708,20 @@ ContentChild::InitXPCOM()
|
|||||||
InitOnContentProcessCreated();
|
InitOnContentProcessCreated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a11y::PDocAccessibleChild*
|
||||||
|
ContentChild::AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(false, "should never call this!");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ContentChild::DeallocPDocAccessibleChild(a11y::PDocAccessibleChild* aChild)
|
||||||
|
{
|
||||||
|
delete static_cast<mozilla::a11y::DocAccessibleChild*>(aChild);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
PMemoryReportRequestChild*
|
PMemoryReportRequestChild*
|
||||||
ContentChild::AllocPMemoryReportRequestChild(const uint32_t& aGeneration,
|
ContentChild::AllocPMemoryReportRequestChild(const uint32_t& aGeneration,
|
||||||
const bool &aAnonymize,
|
const bool &aAnonymize,
|
||||||
|
@ -380,6 +380,8 @@ public:
|
|||||||
const uint64_t& aID,
|
const uint64_t& aID,
|
||||||
const bool& aIsForApp,
|
const bool& aIsForApp,
|
||||||
const bool& aIsForBrowser) MOZ_OVERRIDE;
|
const bool& aIsForBrowser) MOZ_OVERRIDE;
|
||||||
|
virtual PDocAccessibleChild* AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&) MOZ_OVERRIDE;
|
||||||
|
virtual bool DeallocPDocAccessibleChild(PDocAccessibleChild*) MOZ_OVERRIDE;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
|
virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
|
||||||
|
@ -30,6 +30,8 @@
|
|||||||
#include "CrashReporterParent.h"
|
#include "CrashReporterParent.h"
|
||||||
#include "IHistory.h"
|
#include "IHistory.h"
|
||||||
#include "mozIApplication.h"
|
#include "mozIApplication.h"
|
||||||
|
#include "mozilla/a11y/DocAccessibleParent.h"
|
||||||
|
#include "nsAccessibilityService.h"
|
||||||
#include "mozilla/ClearOnShutdown.h"
|
#include "mozilla/ClearOnShutdown.h"
|
||||||
#include "mozilla/dom/DataStoreService.h"
|
#include "mozilla/dom/DataStoreService.h"
|
||||||
#include "mozilla/dom/DOMStorageIPC.h"
|
#include "mozilla/dom/DOMStorageIPC.h"
|
||||||
@ -2697,6 +2699,34 @@ ContentParent::Observe(nsISupports* aSubject,
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a11y::PDocAccessibleParent*
|
||||||
|
ContentParent::AllocPDocAccessibleParent(PDocAccessibleParent* aParent, const uint64_t&)
|
||||||
|
{
|
||||||
|
return new a11y::DocAccessibleParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ContentParent::DeallocPDocAccessibleParent(PDocAccessibleParent* aParent)
|
||||||
|
{
|
||||||
|
delete static_cast<a11y::DocAccessibleParent*>(aParent);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ContentParent::RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc, PDocAccessibleParent* aParentDoc, const uint64_t& aParentID)
|
||||||
|
{
|
||||||
|
auto doc = static_cast<a11y::DocAccessibleParent*>(aDoc);
|
||||||
|
if (aParentDoc) {
|
||||||
|
MOZ_ASSERT(aParentID);
|
||||||
|
auto parentDoc = static_cast<a11y::DocAccessibleParent*>(aParentDoc);
|
||||||
|
return parentDoc->AddChildDoc(doc, aParentID);
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(!aParentID);
|
||||||
|
GetAccService()->RemoteDocAdded(doc);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
PCompositorParent*
|
PCompositorParent*
|
||||||
ContentParent::AllocPCompositorParent(mozilla::ipc::Transport* aTransport,
|
ContentParent::AllocPCompositorParent(mozilla::ipc::Transport* aTransport,
|
||||||
base::ProcessId aOtherProcess)
|
base::ProcessId aOtherProcess)
|
||||||
@ -3822,9 +3852,6 @@ ContentParent::RecvKeywordToURI(const nsCString& aKeyword, OptionalInputStreamPa
|
|||||||
bool
|
bool
|
||||||
ContentParent::ShouldContinueFromReplyTimeout()
|
ContentParent::ShouldContinueFromReplyTimeout()
|
||||||
{
|
{
|
||||||
// The only time ContentParent sends blocking messages is for CPOWs, so
|
|
||||||
// timeouts should only ever occur in electrolysis-enabled sessions.
|
|
||||||
MOZ_ASSERT(BrowserTabsRemote());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -666,6 +666,11 @@ private:
|
|||||||
int32_t* aSliceRefCnt,
|
int32_t* aSliceRefCnt,
|
||||||
bool* aResult) MOZ_OVERRIDE;
|
bool* aResult) MOZ_OVERRIDE;
|
||||||
|
|
||||||
|
virtual PDocAccessibleParent* AllocPDocAccessibleParent(PDocAccessibleParent*, const uint64_t&) MOZ_OVERRIDE;
|
||||||
|
virtual bool DeallocPDocAccessibleParent(PDocAccessibleParent*) MOZ_OVERRIDE;
|
||||||
|
virtual bool RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc,
|
||||||
|
PDocAccessibleParent* aParentDoc, const uint64_t& aParentID) MOZ_OVERRIDE;
|
||||||
|
|
||||||
// If you add strong pointers to cycle collected objects here, be sure to
|
// If you add strong pointers to cycle collected objects here, be sure to
|
||||||
// release these objects in ShutDownProcess. See the comment there for more
|
// release these objects in ShutDownProcess. See the comment there for more
|
||||||
// details.
|
// details.
|
||||||
|
@ -14,6 +14,7 @@ include protocol PCompositor;
|
|||||||
include protocol PContentBridge;
|
include protocol PContentBridge;
|
||||||
include protocol PCycleCollectWithLogs;
|
include protocol PCycleCollectWithLogs;
|
||||||
include protocol PCrashReporter;
|
include protocol PCrashReporter;
|
||||||
|
include protocol PDocAccessible;
|
||||||
include protocol PExternalHelperApp;
|
include protocol PExternalHelperApp;
|
||||||
include protocol PDeviceStorageRequest;
|
include protocol PDeviceStorageRequest;
|
||||||
include protocol PFileDescriptorSet;
|
include protocol PFileDescriptorSet;
|
||||||
@ -332,6 +333,7 @@ intr protocol PContent
|
|||||||
manages PCellBroadcast;
|
manages PCellBroadcast;
|
||||||
manages PCrashReporter;
|
manages PCrashReporter;
|
||||||
manages PCycleCollectWithLogs;
|
manages PCycleCollectWithLogs;
|
||||||
|
manages PDocAccessible;
|
||||||
manages PDeviceStorageRequest;
|
manages PDeviceStorageRequest;
|
||||||
manages PFileSystemRequest;
|
manages PFileSystemRequest;
|
||||||
manages PExternalHelperApp;
|
manages PExternalHelperApp;
|
||||||
@ -488,6 +490,14 @@ child:
|
|||||||
OnAppThemeChanged();
|
OnAppThemeChanged();
|
||||||
|
|
||||||
parent:
|
parent:
|
||||||
|
/**
|
||||||
|
* Tell the parent process a new accessible document has been created.
|
||||||
|
* aParentDoc is the accessible document it was created in if any, and
|
||||||
|
* aParentAcc is the id of the accessible in that document the new document
|
||||||
|
* is a child of.
|
||||||
|
*/
|
||||||
|
PDocAccessible(nullable PDocAccessible aParentDoc, uint64_t aParentAcc);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tell the content process some attributes of itself. This is
|
* Tell the content process some attributes of itself. This is
|
||||||
* among the first information queried by content processes after
|
* among the first information queried by content processes after
|
||||||
|
@ -106,7 +106,6 @@ static const CSSSize kDefaultViewportSize(980, 480);
|
|||||||
static const char BROWSER_ZOOM_TO_RECT[] = "browser-zoom-to-rect";
|
static const char BROWSER_ZOOM_TO_RECT[] = "browser-zoom-to-rect";
|
||||||
static const char BEFORE_FIRST_PAINT[] = "before-first-paint";
|
static const char BEFORE_FIRST_PAINT[] = "before-first-paint";
|
||||||
|
|
||||||
static bool sCpowsEnabled = false;
|
|
||||||
static int32_t sActiveDurationMs = 10;
|
static int32_t sActiveDurationMs = 10;
|
||||||
static bool sActiveDurationMsSet = false;
|
static bool sActiveDurationMsSet = false;
|
||||||
|
|
||||||
@ -2783,12 +2782,6 @@ TabChild::InitRenderingState()
|
|||||||
BEFORE_FIRST_PAINT,
|
BEFORE_FIRST_PAINT,
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This state can't change during the lifetime of the child.
|
|
||||||
sCpowsEnabled = BrowserTabsRemote();
|
|
||||||
if (Preferences::GetBool("dom.ipc.cpows.force-enabled", false))
|
|
||||||
sCpowsEnabled = true;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2917,10 +2910,8 @@ TabChild::DoSendBlockingMessage(JSContext* aCx,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
InfallibleTArray<CpowEntry> cpows;
|
InfallibleTArray<CpowEntry> cpows;
|
||||||
if (sCpowsEnabled) {
|
if (!Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
|
||||||
if (!Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (aIsSync) {
|
if (aIsSync) {
|
||||||
return SendSyncMessage(PromiseFlatString(aMessage), data, cpows,
|
return SendSyncMessage(PromiseFlatString(aMessage), data, cpows,
|
||||||
@ -2943,10 +2934,8 @@ TabChild::DoSendAsyncMessage(JSContext* aCx,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
InfallibleTArray<CpowEntry> cpows;
|
InfallibleTArray<CpowEntry> cpows;
|
||||||
if (sCpowsEnabled) {
|
if (!Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
|
||||||
if (!Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return SendAsyncMessage(PromiseFlatString(aMessage), data, cpows,
|
return SendAsyncMessage(PromiseFlatString(aMessage), data, cpows,
|
||||||
Principal(aPrincipal));
|
Principal(aPrincipal));
|
||||||
|
@ -8,6 +8,8 @@ CrossSiteRequestBlocked=Cross-Origin Request Blocked: The Same Origin Policy dis
|
|||||||
|
|
||||||
# LOCALIZATION NOTE: Do not translate "Strict-Transport-Security" or "HSTS"
|
# LOCALIZATION NOTE: Do not translate "Strict-Transport-Security" or "HSTS"
|
||||||
InvalidSTSHeaders=The site specified an invalid Strict-Transport-Security header.
|
InvalidSTSHeaders=The site specified an invalid Strict-Transport-Security header.
|
||||||
|
# LOCALIZATION NOTE: Do not translate "Public-Key-Pins or HPKP"
|
||||||
|
InvalidPKPHeaders=The site specified an invalid Public-Key-Pins header.
|
||||||
InsecurePasswordsPresentOnPage=Password fields present on an insecure (http://) page. This is a security risk that allows user login credentials to be stolen.
|
InsecurePasswordsPresentOnPage=Password fields present on an insecure (http://) page. This is a security risk that allows user login credentials to be stolen.
|
||||||
InsecureFormActionPasswordsPresent=Password fields present in a form with an insecure (http://) form action. This is a security risk that allows user login credentials to be stolen.
|
InsecureFormActionPasswordsPresent=Password fields present in a form with an insecure (http://) form action. This is a security risk that allows user login credentials to be stolen.
|
||||||
InsecurePasswordsPresentOnIframe=Password fields present on an insecure (http://) iframe. This is a security risk that allows user login credentials to be stolen.
|
InsecurePasswordsPresentOnIframe=Password fields present on an insecure (http://) iframe. This is a security risk that allows user login credentials to be stolen.
|
||||||
|
@ -156,17 +156,7 @@ function loadAndSelectTestTab() {
|
|||||||
gBrowser.selectedTab = tab;
|
gBrowser.selectedTab = tab;
|
||||||
|
|
||||||
let browser = gBrowser.getBrowserForTab(tab);
|
let browser = gBrowser.getBrowserForTab(tab);
|
||||||
|
browser.messageManager.loadFrameScript(kContentScript, false);
|
||||||
// Bug 687194 - Mochitest registers its chrome URLs after browser
|
|
||||||
// initialization, so the content processes don't pick them up. That
|
|
||||||
// means we can't load our frame script from its chrome URI, because
|
|
||||||
// the content process won't be able to find it.
|
|
||||||
//
|
|
||||||
// Instead, we resolve the chrome URI for the script to a file URI, which
|
|
||||||
// we can then pass to the content process, which it is able to find.
|
|
||||||
let registry = Cc['@mozilla.org/chrome/chrome-registry;1'].getService(Ci.nsIChromeRegistry);
|
|
||||||
let fileURI = registry.convertChromeURL(Services.io.newURI(kContentScript, null, null)).spec;
|
|
||||||
browser.messageManager.loadFrameScript(fileURI, false);
|
|
||||||
|
|
||||||
let deferred = Promise.defer();
|
let deferred = Promise.defer();
|
||||||
browser.addEventListener("DOMContentLoaded", function onBrowserLoad(aEvent) {
|
browser.addEventListener("DOMContentLoaded", function onBrowserLoad(aEvent) {
|
||||||
|
@ -3077,6 +3077,12 @@ WifiWorker.prototype = {
|
|||||||
debug("Invalid SSID value.");
|
debug("Invalid SSID value.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
// Truncate ssid if its length of encoded to utf8 is longer than 32.
|
||||||
|
while (unescape(encodeURIComponent(ssid)).length > 32)
|
||||||
|
{
|
||||||
|
ssid = ssid.substring(0, ssid.length-1);
|
||||||
|
}
|
||||||
|
|
||||||
if (securityType != WIFI_SECURITY_TYPE_NONE &&
|
if (securityType != WIFI_SECURITY_TYPE_NONE &&
|
||||||
securityType != WIFI_SECURITY_TYPE_WPA_PSK &&
|
securityType != WIFI_SECURITY_TYPE_WPA_PSK &&
|
||||||
securityType != WIFI_SECURITY_TYPE_WPA2_PSK) {
|
securityType != WIFI_SECURITY_TYPE_WPA2_PSK) {
|
||||||
|
@ -136,7 +136,7 @@ InterpolationQualityFromFilter(Filter aFilter)
|
|||||||
case Filter::POINT:
|
case Filter::POINT:
|
||||||
return kCGInterpolationNone;
|
return kCGInterpolationNone;
|
||||||
case Filter::GOOD:
|
case Filter::GOOD:
|
||||||
return kCGInterpolationDefault;
|
return kCGInterpolationLow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,17 +11,29 @@
|
|||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace gfx {
|
namespace gfx {
|
||||||
|
|
||||||
class AutoSaveTransform
|
class AutoRestoreTransform
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit AutoSaveTransform(DrawTarget *aTarget)
|
explicit AutoRestoreTransform(DrawTarget *aTarget)
|
||||||
: mDrawTarget(aTarget),
|
: mDrawTarget(aTarget),
|
||||||
mOldTransform(aTarget->GetTransform())
|
mOldTransform(aTarget->GetTransform())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
~AutoSaveTransform()
|
|
||||||
|
void Init(DrawTarget *aTarget)
|
||||||
{
|
{
|
||||||
mDrawTarget->SetTransform(mOldTransform);
|
MOZ_ASSERT(!mDrawTarget || aTarget == mDrawTarget);
|
||||||
|
if (!mDrawTarget) {
|
||||||
|
mDrawTarget = aTarget;
|
||||||
|
mOldTransform = aTarget->GetTransform();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~AutoRestoreTransform()
|
||||||
|
{
|
||||||
|
if (mDrawTarget) {
|
||||||
|
mDrawTarget->SetTransform(mOldTransform);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -251,7 +251,7 @@ BasicCompositor::DrawQuad(const gfx::Rect& aRect,
|
|||||||
RefPtr<DrawTarget> dest = buffer;
|
RefPtr<DrawTarget> dest = buffer;
|
||||||
|
|
||||||
buffer->PushClipRect(aClipRect);
|
buffer->PushClipRect(aClipRect);
|
||||||
AutoSaveTransform autoSaveTransform(dest);
|
AutoRestoreTransform autoRestoreTransform(dest);
|
||||||
|
|
||||||
Matrix newTransform;
|
Matrix newTransform;
|
||||||
Rect transformBounds;
|
Rect transformBounds;
|
||||||
|
@ -62,6 +62,7 @@ public:
|
|||||||
void Destroy();
|
void Destroy();
|
||||||
|
|
||||||
nsresult GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
|
nsresult GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
|
||||||
|
gfxFont::Orientation aOrientation,
|
||||||
gfxUserFontSet* aUserFontSet,
|
gfxUserFontSet* aUserFontSet,
|
||||||
gfxTextPerfMetrics* aTextPerf,
|
gfxTextPerfMetrics* aTextPerf,
|
||||||
nsFontMetrics*& aMetrics);
|
nsFontMetrics*& aMetrics);
|
||||||
@ -122,6 +123,7 @@ nsFontCache::Observe(nsISupports*, const char* aTopic, const char16_t*)
|
|||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
|
nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
|
||||||
|
gfxFont::Orientation aOrientation,
|
||||||
gfxUserFontSet* aUserFontSet,
|
gfxUserFontSet* aUserFontSet,
|
||||||
gfxTextPerfMetrics* aTextPerf,
|
gfxTextPerfMetrics* aTextPerf,
|
||||||
nsFontMetrics*& aMetrics)
|
nsFontMetrics*& aMetrics)
|
||||||
@ -137,7 +139,7 @@ nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
|
|||||||
for (int32_t i = n; i >= 0; --i) {
|
for (int32_t i = n; i >= 0; --i) {
|
||||||
fm = mFontMetrics[i];
|
fm = mFontMetrics[i];
|
||||||
if (fm->Font().Equals(aFont) && fm->GetUserFontSet() == aUserFontSet &&
|
if (fm->Font().Equals(aFont) && fm->GetUserFontSet() == aUserFontSet &&
|
||||||
fm->Language() == aLanguage) {
|
fm->Language() == aLanguage && fm->Orientation() == aOrientation) {
|
||||||
if (i != n) {
|
if (i != n) {
|
||||||
// promote it to the end of the cache
|
// promote it to the end of the cache
|
||||||
mFontMetrics.RemoveElementAt(i);
|
mFontMetrics.RemoveElementAt(i);
|
||||||
@ -153,7 +155,8 @@ nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
|
|||||||
|
|
||||||
fm = new nsFontMetrics();
|
fm = new nsFontMetrics();
|
||||||
NS_ADDREF(fm);
|
NS_ADDREF(fm);
|
||||||
nsresult rv = fm->Init(aFont, aLanguage, mContext, aUserFontSet, aTextPerf);
|
nsresult rv = fm->Init(aFont, aLanguage, aOrientation, mContext,
|
||||||
|
aUserFontSet, aTextPerf);
|
||||||
if (NS_SUCCEEDED(rv)) {
|
if (NS_SUCCEEDED(rv)) {
|
||||||
// the mFontMetrics list has the "head" at the end, because append
|
// the mFontMetrics list has the "head" at the end, because append
|
||||||
// is cheaper than insert
|
// is cheaper than insert
|
||||||
@ -172,7 +175,8 @@ nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
|
|||||||
Compact();
|
Compact();
|
||||||
fm = new nsFontMetrics();
|
fm = new nsFontMetrics();
|
||||||
NS_ADDREF(fm);
|
NS_ADDREF(fm);
|
||||||
rv = fm->Init(aFont, aLanguage, mContext, aUserFontSet, aTextPerf);
|
rv = fm->Init(aFont, aLanguage, aOrientation, mContext, aUserFontSet,
|
||||||
|
aTextPerf);
|
||||||
if (NS_SUCCEEDED(rv)) {
|
if (NS_SUCCEEDED(rv)) {
|
||||||
mFontMetrics.AppendElement(fm);
|
mFontMetrics.AppendElement(fm);
|
||||||
aMetrics = fm;
|
aMetrics = fm;
|
||||||
@ -260,6 +264,7 @@ nsDeviceContext::~nsDeviceContext()
|
|||||||
nsresult
|
nsresult
|
||||||
nsDeviceContext::GetMetricsFor(const nsFont& aFont,
|
nsDeviceContext::GetMetricsFor(const nsFont& aFont,
|
||||||
nsIAtom* aLanguage,
|
nsIAtom* aLanguage,
|
||||||
|
gfxFont::Orientation aOrientation,
|
||||||
gfxUserFontSet* aUserFontSet,
|
gfxUserFontSet* aUserFontSet,
|
||||||
gfxTextPerfMetrics* aTextPerf,
|
gfxTextPerfMetrics* aTextPerf,
|
||||||
nsFontMetrics*& aMetrics)
|
nsFontMetrics*& aMetrics)
|
||||||
@ -270,8 +275,8 @@ nsDeviceContext::GetMetricsFor(const nsFont& aFont,
|
|||||||
mFontCache->Init(this);
|
mFontCache->Init(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mFontCache->GetMetricsFor(aFont, aLanguage, aUserFontSet,
|
return mFontCache->GetMetricsFor(aFont, aLanguage, aOrientation,
|
||||||
aTextPerf, aMetrics);
|
aUserFontSet, aTextPerf, aMetrics);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <stdint.h> // for uint32_t
|
#include <stdint.h> // for uint32_t
|
||||||
#include <sys/types.h> // for int32_t
|
#include <sys/types.h> // for int32_t
|
||||||
#include "gfxTypes.h" // for gfxFloat
|
#include "gfxTypes.h" // for gfxFloat
|
||||||
|
#include "gfxFont.h" // for gfxFont::Orientation
|
||||||
#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
|
#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
|
||||||
#include "nsAutoPtr.h" // for nsRefPtr
|
#include "nsAutoPtr.h" // for nsRefPtr
|
||||||
#include "nsCOMPtr.h" // for nsCOMPtr
|
#include "nsCOMPtr.h" // for nsCOMPtr
|
||||||
@ -118,6 +119,7 @@ public:
|
|||||||
* @return error status
|
* @return error status
|
||||||
*/
|
*/
|
||||||
nsresult GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
|
nsresult GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
|
||||||
|
gfxFont::Orientation aOrientation,
|
||||||
gfxUserFontSet* aUserFontSet,
|
gfxUserFontSet* aUserFontSet,
|
||||||
gfxTextPerfMetrics* aTextPerf,
|
gfxTextPerfMetrics* aTextPerf,
|
||||||
nsFontMetrics*& aMetrics);
|
nsFontMetrics*& aMetrics);
|
||||||
|
@ -104,6 +104,7 @@ nsFontMetrics::~nsFontMetrics()
|
|||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsFontMetrics::Init(const nsFont& aFont, nsIAtom* aLanguage,
|
nsFontMetrics::Init(const nsFont& aFont, nsIAtom* aLanguage,
|
||||||
|
gfxFont::Orientation aOrientation,
|
||||||
nsDeviceContext *aContext,
|
nsDeviceContext *aContext,
|
||||||
gfxUserFontSet *aUserFontSet,
|
gfxUserFontSet *aUserFontSet,
|
||||||
gfxTextPerfMetrics *aTextPerf)
|
gfxTextPerfMetrics *aTextPerf)
|
||||||
@ -112,6 +113,7 @@ nsFontMetrics::Init(const nsFont& aFont, nsIAtom* aLanguage,
|
|||||||
|
|
||||||
mFont = aFont;
|
mFont = aFont;
|
||||||
mLanguage = aLanguage;
|
mLanguage = aLanguage;
|
||||||
|
mOrientation = aOrientation;
|
||||||
mDeviceContext = aContext;
|
mDeviceContext = aContext;
|
||||||
mP2A = mDeviceContext->AppUnitsPerDevPixel();
|
mP2A = mDeviceContext->AppUnitsPerDevPixel();
|
||||||
|
|
||||||
@ -147,7 +149,7 @@ nsFontMetrics::Destroy()
|
|||||||
|
|
||||||
const gfxFont::Metrics& nsFontMetrics::GetMetrics() const
|
const gfxFont::Metrics& nsFontMetrics::GetMetrics() const
|
||||||
{
|
{
|
||||||
return mFontGroup->GetFirstValidFont()->GetMetrics();
|
return mFontGroup->GetFirstValidFont()->GetMetrics(mOrientation);
|
||||||
}
|
}
|
||||||
|
|
||||||
nscoord
|
nscoord
|
||||||
|
@ -57,6 +57,7 @@ public:
|
|||||||
* @see nsDeviceContext#GetMetricsFor()
|
* @see nsDeviceContext#GetMetricsFor()
|
||||||
*/
|
*/
|
||||||
nsresult Init(const nsFont& aFont, nsIAtom* aLanguage,
|
nsresult Init(const nsFont& aFont, nsIAtom* aLanguage,
|
||||||
|
gfxFont::Orientation aOrientation,
|
||||||
nsDeviceContext *aContext,
|
nsDeviceContext *aContext,
|
||||||
gfxUserFontSet *aUserFontSet,
|
gfxUserFontSet *aUserFontSet,
|
||||||
gfxTextPerfMetrics *aTextPerf);
|
gfxTextPerfMetrics *aTextPerf);
|
||||||
@ -174,6 +175,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
nsIAtom* Language() { return mLanguage; }
|
nsIAtom* Language() { return mLanguage; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the orientation (horizontal/vertical) of these metrics.
|
||||||
|
*/
|
||||||
|
gfxFont::Orientation Orientation() { return mOrientation; }
|
||||||
|
|
||||||
int32_t GetMaxStringLength();
|
int32_t GetMaxStringLength();
|
||||||
|
|
||||||
// Get the width for this string. aWidth will be updated with the
|
// Get the width for this string. aWidth will be updated with the
|
||||||
@ -223,6 +229,7 @@ private:
|
|||||||
nsDeviceContext *mDeviceContext;
|
nsDeviceContext *mDeviceContext;
|
||||||
int32_t mP2A;
|
int32_t mP2A;
|
||||||
bool mTextRunRTL;
|
bool mTextRunRTL;
|
||||||
|
gfxFont::Orientation mOrientation;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* NSFONTMETRICS__H__ */
|
#endif /* NSFONTMETRICS__H__ */
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user