merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2014-09-30 15:02:58 +02:00
commit 22ae53697c
227 changed files with 3911 additions and 1275 deletions

View File

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

View File

@ -97,7 +97,7 @@ private:
static EAvailableAtkSignals gAvailableAtkSignals; static EAvailableAtkSignals gAvailableAtkSignals;
uint16_t CreateMaiInterfaces(void); uint16_t CreateMaiInterfaces();
}; };
} // namespace a11y } // namespace a11y

View File

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

View File

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

View File

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

View File

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

View File

@ -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;
}; };
/** /**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View 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

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

View 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

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

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

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

View File

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

View File

@ -33,6 +33,15 @@ PlatformShutdown()
{ {
} }
void
ProxyCreated(ProxyAccessible*)
{
}
void
ProxyDestroyed(ProxyAccessible*)
{
}
} }
} }

View File

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

View File

@ -18,3 +18,13 @@ void
a11y::PlatformShutdown() a11y::PlatformShutdown()
{ {
} }
void
a11y::ProxyCreated(ProxyAccessible*)
{
}
void
a11y::ProxyDestroyed(ProxyAccessible*)
{
}

View File

@ -34,3 +34,12 @@ a11y::PlatformShutdown()
nsWinUtils::ShutdownWindowEmulation(); nsWinUtils::ShutdownWindowEmulation();
} }
void
a11y::ProxyCreated(ProxyAccessible*)
{
}
void
a11y::ProxyDestroyed(ProxyAccessible*)
{
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2563,7 +2563,6 @@
]]> ]]>
</body> </body>
</method> </method>
#endif
<method name="moveTabTo"> <method name="moveTabTo">
<parameter name="aTab"/> <parameter name="aTab"/>

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -101,7 +101,6 @@ MediaOmxCommonDecoder::PauseStateMachine()
if (!mDecoderStateMachine) { if (!mDecoderStateMachine) {
return; return;
} }
StopProgress();
mDecoderStateMachine->SetDormant(true); mDecoderStateMachine->SetDormant(true);
} }

View File

@ -0,0 +1 @@
Cache-Control: no-store

Binary file not shown.

View File

@ -0,0 +1 @@
Cache-Control: no-store

Binary file not shown.

View File

@ -0,0 +1 @@
Cache-Control: no-store

Binary file not shown.

View File

@ -0,0 +1 @@
Cache-Control: no-store

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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