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 "nsIAccessibleRelation.h"
#include "nsIAccessibleTable.h"
#include "ProxyAccessible.h"
#include "RootAccessible.h"
#include "nsIAccessibleValue.h"
#include "nsMai.h"
@ -20,6 +21,7 @@
#include "nsAutoPtr.h"
#include "prprf.h"
#include "nsStateMap.h"
#include "mozilla/a11y/Platform.h"
#include "Relation.h"
#include "RootAccessible.h"
#include "States.h"
@ -133,9 +135,13 @@ struct MaiAtkObject
* The AccessibleWrap whose properties and features are exported
* 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
{
AtkObjectClass parent_class;
@ -248,7 +254,7 @@ AccessibleWrap::ShutdownAtkObject()
{
if (mAtkObject) {
if (IS_MAI_OBJECT(mAtkObject)) {
MAI_ATK_OBJECT(mAtkObject)->accWrap = nullptr;
MAI_ATK_OBJECT(mAtkObject)->accWrap = 0;
}
SetMaiHyperlink(nullptr);
g_object_unref(mAtkObject);
@ -582,8 +588,7 @@ initializeCB(AtkObject *aAtkObj, gpointer aData)
ATK_OBJECT_CLASS(parent_class)->initialize(aAtkObj, aData);
/* initialize object */
MAI_ATK_OBJECT(aAtkObj)->accWrap =
static_cast<AccessibleWrap*>(aData);
MAI_ATK_OBJECT(aAtkObj)->accWrap = reinterpret_cast<uintptr_t>(aData);
}
void
@ -591,7 +596,7 @@ finalizeCB(GObject *aObj)
{
if (!IS_MAI_OBJECT(aObj))
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
// finalize of GObjectClass will unref the accessible parent if has
@ -663,25 +668,33 @@ getDescriptionCB(AtkObject *aAtkObj)
AtkRole
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)
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, \
msaaRole, ia2Role, nameRule) \
case roles::geckoRole: \
aAtkObj->role = atkRole; \
break;
switch (accWrap->Role()) {
switch (role) {
#include "RoleMap.h"
default:
MOZ_CRASH("Unknown role.");
@ -946,7 +959,13 @@ AccessibleWrap*
GetAccessibleWrap(AtkObject* aAtkObj)
{
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.
if (!accWrap)
@ -961,6 +980,49 @@ GetAccessibleWrap(AtkObject* aAtkObj)
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
AccessibleWrap::HandleAccEvent(AccEvent* aEvent)
{

View File

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

View File

@ -35,6 +35,7 @@ LOCAL_INCLUDES += [
'/accessible/base',
'/accessible/generic',
'/accessible/html',
'/accessible/ipc',
'/accessible/xpcom',
'/accessible/xul',
'/other-licenses/atk-1.0',

View File

@ -13,6 +13,12 @@
#include "AccessibleWrap.h"
namespace mozilla {
namespace a11y {
class ProxyAccessible;
}
}
#define MAI_TYPE_ATK_OBJECT (mai_atk_object_get_type ())
#define MAI_ATK_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
MAI_TYPE_ATK_OBJECT, MaiAtkObject))
@ -29,6 +35,7 @@
GType mai_atk_object_get_type(void);
GType mai_util_get_type();
mozilla::a11y::AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj);
mozilla::a11y::ProxyAccessible* GetProxy(AtkObject* aAtkObj);
extern int atkMajorVersion, atkMinorVersion;

View File

@ -232,6 +232,8 @@ public:
bool IsShow() const { return mEventType == nsIAccessibleEvent::EVENT_SHOW; }
bool IsHide() const { return mEventType == nsIAccessibleEvent::EVENT_HIDE; }
Accessible* Parent() const { return mParent; }
protected:
nsCOMPtr<nsINode> mNode;
nsRefPtr<Accessible> mParent;

View File

@ -8,6 +8,7 @@
#include "ApplicationAccessible.h"
#include "ARIAMap.h"
#include "DocAccessible-inl.h"
#include "DocAccessibleChild.h"
#include "nsAccessibilityService.h"
#include "RootAccessibleWrap.h"
@ -27,6 +28,8 @@
#include "nsServiceManagerUtils.h"
#include "nsIWebProgress.h"
#include "nsCoreUtils.h"
#include "nsXULAppAPI.h"
#include "mozilla/dom/ContentChild.h"
using namespace mozilla;
using namespace mozilla::a11y;
@ -418,6 +421,12 @@ DocManager::CreateDocOrRootAccessible(nsIDocument* aDocument)
docAcc->FireDelayedEvent(nsIAccessibleEvent::EVENT_REORDER,
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 {
parentDocAcc->BindChildDocument(docAcc);
}

View File

@ -17,6 +17,7 @@ namespace a11y {
class Accessible;
class DocAccessible;
class DocAccessibleParent;
/**
* Manage the document accessible life cycle.
@ -65,6 +66,25 @@ public:
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
bool IsProcessingRefreshDriverNotification() const;
#endif
@ -144,6 +164,11 @@ private:
#endif
DocAccessibleHashtable mDocAccessibleCache;
/*
* The list of remote top level documents.
*/
nsTArray<DocAccessibleParent*> mRemoteDocuments;
};
/**

View File

@ -8,6 +8,7 @@
#include "Accessible-inl.h"
#include "nsEventShell.h"
#include "DocAccessible.h"
#include "DocAccessibleChild.h"
#include "nsAccessibilityService.h"
#include "nsTextEquivUtils.h"
#ifdef A11Y_LOG
@ -555,5 +556,15 @@ EventQueue::ProcessEventQueue()
if (!mDocument)
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 "DocAccessible-inl.h"
#include "DocAccessibleChild.h"
#include "TextLeafAccessible.h"
#include "TextUpdater.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/Element.h"
#include "mozilla/Telemetry.h"
@ -217,8 +219,19 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
if (ownerContent) {
Accessible* outerDocAcc = mDocument->GetAccessible(ownerContent);
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;
}
outerDocAcc->RemoveChild(childDoc);
}

View File

@ -7,6 +7,8 @@
namespace mozilla {
namespace a11y {
class ProxyAccessible;
enum EPlatformDisabledState {
ePlatformIsForceEnabled = -1,
ePlatformIsEnabled = 0,
@ -47,6 +49,17 @@ void PlatformInit();
*/
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 mozilla

View File

@ -783,7 +783,9 @@ enum Role {
/**
* Represent a keyboard or keypad key (ARIA role "key").
*/
KEY = 129
KEY = 129,
LAST_ROLE = KEY
};
} // namespace role

View File

@ -60,6 +60,7 @@ if CONFIG['A11Y_LOG']:
LOCAL_INCLUDES += [
'/accessible/generic',
'/accessible/html',
'/accessible/ipc',
'/accessible/xpcom',
'/accessible/xul',
'/dom/xbl',
@ -93,3 +94,5 @@ FINAL_LIBRARY = 'xul'
if CONFIG['MOZ_ENABLE_GTK']:
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
include('/ipc/chromium/chromium-config.mozbuild')

View File

@ -240,7 +240,7 @@ Accessible::Description(nsString& aDescription)
if (!aDescription.IsEmpty()) {
aDescription.CompressWhitespace();
nsAutoString name;
ENameValueFlag nameFlag = Name(name);
Name(name);
// Don't expose a description if it is the same as the name.
if (aDescription.Equals(name))
aDescription.Truncate();

View File

@ -6,6 +6,7 @@
#include "Accessible-inl.h"
#include "AccIterator.h"
#include "DocAccessible-inl.h"
#include "DocAccessibleChild.h"
#include "HTMLImageMapAccessible.h"
#include "nsAccCache.h"
#include "nsAccessiblePivot.h"
@ -83,7 +84,7 @@ DocAccessible::
mScrollPositionChangedTicks(0),
mLoadState(eTreeConstructionPending), mDocFlags(0), mLoadEventType(0),
mVirtualCursor(nullptr),
mPresShell(aPresShell)
mPresShell(aPresShell), mIPCDoc(nullptr)
{
mGenericTypes |= eDocument;
mStateFlags |= eNotNodeMapEntry;
@ -473,6 +474,12 @@ DocAccessible::Shutdown()
mChildDocuments.Clear();
// XXX thinking about ordering?
if (XRE_GetProcessType() != GeckoProcessType_Default) {
DocAccessibleChild::Send__delete__(mIPCDoc);
MOZ_ASSERT(!mIPCDoc);
}
if (mVirtualCursor) {
mVirtualCursor->RemoveObserver(this);
mVirtualCursor = nullptr;
@ -1436,6 +1443,13 @@ DocAccessible::DoInitialUpdate()
nsRefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(Parent());
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

View File

@ -33,6 +33,7 @@ namespace a11y {
class DocManager;
class NotificationController;
class DocAccessibleChild;
class RelatedAccIterator;
template<class Class, class Arg>
class TNotification;
@ -519,6 +520,20 @@ protected:
*/
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.
*
@ -642,6 +657,9 @@ protected:
private:
nsIPresShell* mPresShell;
// Exclusively owned by IPDL so don't manually delete it!
DocAccessibleChild* mIPCDoc;
};
inline DocAccessible*

View File

@ -28,6 +28,7 @@ UNIFIED_SOURCES += [
LOCAL_INCLUDES += [
'/accessible/base',
'/accessible/html',
'/accessible/ipc',
'/accessible/xpcom',
'/accessible/xul',
'/content/base/src',
@ -54,3 +55,5 @@ else:
]
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');
XPCOMUtils.defineLazyModuleGetter(this, 'Roles',
'resource://gre/modules/accessibility/Constants.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'States',
'resource://gre/modules/accessibility/Constants.jsm');
Logger.debug('content-script.js');
@ -142,7 +144,20 @@ addMessageListener(
eventManager.inTest = m.json.inTest;
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(

View File

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

View File

@ -15,7 +15,7 @@ elif toolkit == 'cocoa':
else:
DIRS += ['other']
DIRS += ['base', 'generic', 'html', 'interfaces', 'jsat', 'xpcom']
DIRS += ['base', 'generic', 'html', 'interfaces', 'ipc', 'jsat', 'xpcom']
if CONFIG['MOZ_XUL']:
DIRS += ['xul']

View File

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

View File

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

View File

@ -26,7 +26,6 @@
#define ASSERT(x) if (!(x)) { MOZ_CRASH(); }
// Functions being loaded by XPCOMGlue
XRE_ProcLoaderServiceRunType XRE_ProcLoaderServiceRun;
XRE_ProcLoaderClientInitType XRE_ProcLoaderClientInit;
@ -43,6 +42,10 @@ static const nsDynamicFunctionLoad kXULFuncs[] = {
{ nullptr, nullptr }
};
typedef mozilla::Vector<int> FdArray;
static const int kReservedFileDescriptors = 5;
static const int kBeginReserveFileDescriptor = STDERR_FILENO + 1;
static int
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);
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;
strcpy(afterSlash, XPCOM_DLL);
return true;
@ -181,7 +184,7 @@ LoadStaticData(int argc, const char *argv[])
* The parent is the b2g process and child for Nuwa.
*/
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
@ -212,15 +215,50 @@ RunProcesses(int argc, const char *argv[])
* The b2g process would send a IPC message of loading Nuwa
* 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
int childPid = pid;
XRE_ProcLoaderClientInit(childPid, parentSock);
XRE_ProcLoaderClientInit(childPid, parentSock, aReservedFds);
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
* Nuwa process. It forks into the parent process, for the b2g
@ -234,7 +272,12 @@ RunProcesses(int argc, const char *argv[])
int
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
* sharing.
@ -244,5 +287,5 @@ main(int argc, const char* argv[])
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
pref("ui.showHideScrollbars", 1);
pref("ui.useOverlayScrollbars", 1);
pref("ui.scrollbarFadeBeginDelay", 450);
pref("ui.scrollbarFadeDuration", 200);
// Enable the ProcessPriorityManager, and give processes with no visible
// documents a 1s grace period before they're eligible to be marked as

View File

@ -269,6 +269,7 @@ let consoleWatcher = {
'Mixed Content Message',
'CSP',
'Invalid HSTS Headers',
'Invalid HPKP Headers',
'Insecure Password Field',
'SSL',
'CORS'

View File

@ -1177,16 +1177,9 @@ pref("dom.ipc.plugins.enabled.x86_64", true);
pref("dom.ipc.plugins.enabled", true);
#endif
#if defined(NIGHTLY_BUILD)
// 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
// Start the browser in e10s mode
pref("browser.tabs.remote.autostart", false);
pref("browser.tabs.remote.desktopbehavior", true);
#if defined(MOZ_CONTENT_SANDBOX) && defined(XP_WIN)
// This controls whether the content process on Windows is sandboxed.

View File

@ -6792,11 +6792,11 @@ var gIdentityHandler = {
if (gURLBar.getAttribute("pageproxystate") != "valid")
return;
var value = content.location.href;
var urlString = value + "\n" + content.document.title;
var htmlString = "<a href=\"" + value + "\">" + value + "</a>";
let value = gBrowser.currentURI.spec;
let urlString = value + "\n" + gBrowser.contentTitle;
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/uri-list", value);
dt.setData("text/plain", value);
@ -6952,20 +6952,16 @@ let gRemoteTabsUI = {
return;
}
let remoteTabs = Services.appinfo.browserTabsRemote;
let autostart = Services.appinfo.browserTabsRemoteAutostart;
let newRemoteWindow = document.getElementById("menu_newRemoteWindow");
let newNonRemoteWindow = document.getElementById("menu_newNonRemoteWindow");
if (!remoteTabs) {
newRemoteWindow.hidden = true;
newNonRemoteWindow.hidden = true;
return;
}
#ifdef E10S_TESTING_ONLY
let autostart = Services.appinfo.browserTabsRemoteAutostart;
newRemoteWindow.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);
return gDevTools.showToolbox(tt, "inspector").then(function(toolbox) {
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));
},

View File

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

View File

@ -65,6 +65,7 @@ support-files =
offlineQuotaNotification.html
page_style_sample.html
parsingTestHelpers.jsm
pinning_headers.sjs
popup_blocker.html
print_postdata.sjs
redirect_bug623155.sjs
@ -493,4 +494,5 @@ skip-if = e10s # Bug ?????? - test directly manipulates content (content.documen
[browser_bug1045809.js]
skip-if = e10s # Bug 1068360 - [e10s] Mixed content blocker doorhanger doesn't work
[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
/**
* 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
* 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.
*/
if (Services.prefs.getBoolPref("browser.tabs.remote")) {
let getCommandFunction = function(aOpenRemote) {
return function(aEvent) {
let win = aEvent.view;
if (win && typeof win.OpenBrowserWindow == "function") {
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),
});
* 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
* 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.
*/
let getCommandFunction = function(aOpenRemote) {
return function(aEvent) {
let win = aEvent.view;
if (win && typeof win.OpenBrowserWindow == "function") {
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),
});
#endif

View File

@ -16,6 +16,11 @@ Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/
Services.prefs.setBoolPref("browser.uiCustomization.skipSourceNodeCheck", true);
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;
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;
info("Loading the helper frame script " + FRAME_SCRIPT_URL);
// 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(FRAME_SCRIPT_URL, null, null)).spec;
browser.messageManager.loadFrameScript(fileURI, false);
browser.messageManager.loadFrameScript(FRAME_SCRIPT_URL, false);
browser.addEventListener("load", function onload() {
browser.removeEventListener("load", onload, true);

View File

@ -4674,6 +4674,7 @@ var Utils = {
case "Mixed Content Message":
case "CSP":
case "Invalid HSTS Headers":
case "Invalid HPKP Headers":
case "Insecure Password Field":
case "SSL":
case "CORS":

View File

@ -1451,7 +1451,7 @@ Tab.prototype = {
browser.setAttribute("type", "content-targetable");
let useRemote = Services.appinfo.browserTabsRemote;
let useRemote = Services.appinfo.browserTabsRemoteAutostart;
let useLocal = Util.isLocalScheme(aURI);
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("browser.chromeURL", "chrome://browser/content/");
pref("browser.tabs.remote", false);
// Telemetry
#ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
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-dev.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() {
// We don't want to set browser.tabs.remote to true, but still have CPOWs enabled.
SpecialPowers.pushPrefEnv({"set": [["dom.ipc.cpows.force-enabled", true]]}, function() {
window.open("cpows_parent.xul", "", "chrome");
});
window.open("cpows_parent.xul", "", "chrome");
});
]]></script>
</window>

View File

@ -162,15 +162,9 @@ public:
virtual void MetadataLoaded(const MediaInfo* aInfo,
const MetadataTags* aTags) MOZ_FINAL MOZ_OVERRIDE;
// Called by the video decoder object, on the main thread,
// when it has read the first frame of the video
// aResourceFullyLoaded should be true if the resource has been
// 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 decoder object, on the main thread,
// when it has read the first frame of the video or audio.
virtual void FirstFrameLoaded() MOZ_FINAL MOZ_OVERRIDE;
// Called by the video decoder object, on the main thread,
// when the resource has a network error during loading.
@ -210,6 +204,9 @@ public:
// ongoing.
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
// (no data has arrived for a while).
virtual void DownloadStalled() MOZ_FINAL MOZ_OVERRIDE;
@ -238,10 +235,6 @@ public:
// HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA.
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.
bool CanActivateAutoplay();
@ -540,7 +533,7 @@ public:
already_AddRefed<Promise> SetMediaKeys(MediaKeys* mediaKeys,
ErrorResult& aRv);
MediaWaitingFor WaitingFor() const;
mozilla::dom::EventHandlerNonNull* GetOnencrypted();
@ -648,6 +641,17 @@ protected:
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
* 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
// next block of audio samples) preceeding seek target.
void Seek(double aTime, SeekTarget::Type aSeekType, ErrorResult& aRv);
// Update the audio channel playing state
void UpdateAudioChannelPlayingState();
@ -1104,9 +1108,8 @@ protected:
// Set to false when completed, or not yet started.
bool mBegun;
// True when the decoder has loaded enough data to display the
// first frame of the content.
bool mLoadedFirstFrame;
// True if loadeddata has been fired.
bool mLoadedDataFired;
// Indicates whether current playback is a result of user action
// (ie. calling of the Play method), or automatic playback due to

View File

@ -618,6 +618,11 @@ void HTMLMediaElement::ShutdownDecoder()
{
RemoveMediaElementFromURITable();
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 = nullptr;
}
@ -661,7 +666,7 @@ void HTMLMediaElement::AbortExistingLoads()
}
mError = nullptr;
mLoadedFirstFrame = false;
mLoadedDataFired = false;
mAutoplaying = true;
mIsLoadingFromSourceChildren = false;
mSuspendedAfterFirstFrame = false;
@ -674,8 +679,8 @@ void HTMLMediaElement::AbortExistingLoads()
mTags = nullptr;
if (mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
NS_ASSERTION(!mDecoder && !mSrcStream, "How did someone setup a new stream/decoder already?");
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY);
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING);
mPaused = true;
@ -701,7 +706,7 @@ void HTMLMediaElement::NoSupportedMediaSourceError()
NS_ASSERTION(mDelayingLoadEvent, "Load event not delayed during source selection?");
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"));
// This clears mDelayingLoadEvent, so AddRemoveSelfReference will be called
ChangeDelayLoadStatus(false);
@ -745,7 +750,7 @@ void HTMLMediaElement::RunInStableState(nsIRunnable* aRunnable)
void HTMLMediaElement::QueueLoadFromSourceTask()
{
ChangeDelayLoadStatus(true);
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_LOADING);
RunInStableState(
NS_NewRunnableMethod(this, &HTMLMediaElement::LoadFromSourceChildren));
}
@ -756,7 +761,7 @@ void HTMLMediaElement::QueueSelectResourceTask()
if (mHaveQueuedSelectResource)
return;
mHaveQueuedSelectResource = true;
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE;
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE);
RunInStableState(
NS_NewRunnableMethod(this, &HTMLMediaElement::SelectResourceWrapper));
}
@ -817,7 +822,7 @@ void HTMLMediaElement::SelectResource()
!HasSourceChildren(this)) {
// The media element has neither a src attribute nor any source
// element children, abort the load.
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY);
// This clears mDelayingLoadEvent, so AddRemoveSelfReference will be called
ChangeDelayLoadStatus(false);
return;
@ -825,7 +830,7 @@ void HTMLMediaElement::SelectResource()
ChangeDelayLoadStatus(true);
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_LOADING);
// Load event was delayed, and still is, so no need to call
// AddRemoveSelfReference, since it must still be held
DispatchAsyncEvent(NS_LITERAL_STRING("loadstart"));
@ -921,7 +926,7 @@ void HTMLMediaElement::LoadFromSourceChildren()
// Exhausted candidates, wait for more candidates to be appended to
// the media element.
mLoadWaitStatus = WAITING_FOR_SOURCE;
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE;
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE);
ChangeDelayLoadStatus(false);
ReportLoadError("MediaLoadExhaustedCandidates");
return;
@ -990,8 +995,7 @@ void HTMLMediaElement::LoadFromSourceChildren()
void HTMLMediaElement::SuspendLoad()
{
mSuspendedForPreloadNone = true;
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
ChangeDelayLoadStatus(false);
}
@ -1002,7 +1006,7 @@ void HTMLMediaElement::ResumeLoad(PreloadAction aAction)
mSuspendedForPreloadNone = false;
mPreloadAction = aAction;
ChangeDelayLoadStatus(true);
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_LOADING);
if (!mIsLoadingFromSourceChildren) {
// We were loading from the element's src attribute.
if (NS_FAILED(LoadResource())) {
@ -2027,7 +2031,7 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
mPlayed(new TimeRanges),
mCurrentPlayRangeStart(-1.0),
mBegun(false),
mLoadedFirstFrame(false),
mLoadedDataFired(false),
mAutoplaying(true),
mAutoplayEnabled(true),
mPaused(true),
@ -2154,11 +2158,10 @@ void HTMLMediaElement::SetPlayedOrSeeked(bool aValue)
void
HTMLMediaElement::ResetConnectionState()
{
mBegun = false;
SetCurrentTime(0);
FireTimeUpdate(false);
DispatchAsyncEvent(NS_LITERAL_STRING("ended"));
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY);
AddRemoveSelfReference();
ChangeDelayLoadStatus(false);
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING);
@ -2646,7 +2649,7 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder,
nsIStreamListener** aListener,
MediaDecoder* aCloneDonor)
{
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_LOADING);
// Force a same-origin check before allowing events for this media resource.
mMediaSecurityVerified = false;
@ -2667,6 +2670,8 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder,
mDecoder->SetVolume(mMuted ? 0.0 : mVolume);
mDecoder->SetPreservesPitch(mPreservesPitch);
mDecoder->SetPlaybackRate(mPlaybackRate);
// Start progress timer for we are in NETWORK_LOADING.
mDecoder->StartProgress();
#ifdef MOZ_EME
if (mMediaKeys) {
@ -2718,7 +2723,6 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder,
NS_ASSERTION(NS_SUCCEEDED(rv) == (MediaElementTableCount(this, mLoadingSrc) == 1),
"Media element should have single table entry if decode initialized");
mBegun = true;
return rv;
}
@ -2776,7 +2780,7 @@ public:
mHaveCurrentData = true;
if (mElement) {
nsRefPtr<HTMLMediaElement> deathGrip = mElement;
mElement->FirstFrameLoaded(false);
mElement->FirstFrameLoaded();
}
UpdateReadyStateForData();
DoNotifyOutput();
@ -2867,11 +2871,9 @@ void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream)
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata"));
DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
AddRemoveSelfReference();
// FirstFrameLoaded(false) will be called when the stream has current data,
// to complete the setup by entering the HAVE_CURRENT_DATA state.
// FirstFrameLoaded() will be called when the stream has current data.
}
void HTMLMediaElement::EndSrcMediaStreamPlayback()
@ -2921,6 +2923,7 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
{
mHasAudio = aInfo->HasAudio();
mTags = aTags;
mLoadedDataFired = false;
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
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");
ChangeDelayLoadStatus(false);
if (mDecoder && mAllowSuspendAfterFirstFrame && mPaused &&
!aResourceFullyLoaded &&
!HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) &&
mPreloadAction == HTMLMediaElement::PRELOAD_METADATA) {
mSuspendedAfterFirstFrame = true;
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()
{
Error(nsIDOMMediaError::MEDIA_ERR_NETWORK);
@ -3033,13 +2998,12 @@ void HTMLMediaElement::Error(uint16_t aErrorCode)
aErrorCode == nsIDOMMediaError::MEDIA_ERR_ABORTED,
"Only use nsIDOMMediaError codes!");
mError = new MediaError(this, aErrorCode);
mBegun = false;
DispatchAsyncEvent(NS_LITERAL_STRING("error"));
if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) {
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY);
DispatchAsyncEvent(NS_LITERAL_STRING("emptied"));
} else {
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
}
AddRemoveSelfReference();
ChangeDelayLoadStatus(false);
@ -3083,7 +3047,6 @@ void HTMLMediaElement::SeekStarted()
if(mPlayingThroughTheAudioChannel) {
mPlayingThroughTheAudioChannelBeforeSeek = true;
}
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
FireTimeUpdate(false);
}
@ -3107,29 +3070,32 @@ void HTMLMediaElement::SeekCompleted()
void HTMLMediaElement::NotifySuspendedByCache(bool 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()
{
DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
DownloadProgressed();
if (mBegun) {
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
AddRemoveSelfReference();
DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
}
}
void HTMLMediaElement::DownloadResumed(bool aForceNetworkLoading)
{
if (mBegun || aForceNetworkLoading) {
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_LOADING);
AddRemoveSelfReference();
}
}
void HTMLMediaElement::DownloadProgressed()
{
if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING) {
DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
}
}
void HTMLMediaElement::DownloadStalled()
{
if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING) {
@ -3146,8 +3112,7 @@ void HTMLMediaElement::UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatu
{
if (mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
// aNextFrame might have a next frame because the decoder can advance
// on its own thread before ResourceLoaded or MetadataLoaded gets
// a chance to run.
// on its own thread before MetadataLoaded gets a chance to run.
// The arrival of more data can't change us out of this readyState.
return;
}
@ -3160,17 +3125,21 @@ void HTMLMediaElement::UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatu
return;
}
if (mReadyState > nsIDOMHTMLMediaElement::HAVE_METADATA &&
mDownloadSuspendedByCache &&
mDecoder &&
!mDecoder->IsEnded()) {
// The decoder has signalled that the download has been suspended by the
if (aNextFrame == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING) {
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
return;
}
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
// script waiting for a "canplaythrough" event; without this forced
// transition, we will never fire the "canplaythrough" event if the
// 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
// 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);
return;
}
@ -3210,7 +3179,7 @@ void HTMLMediaElement::UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatu
}
#ifdef PR_LOGGING
static const char* gReadyStateToString[] = {
static const char* const gReadyStateToString[] = {
"HAVE_NOTHING",
"HAVE_METADATA",
"HAVE_CURRENT_DATA",
@ -3235,16 +3204,15 @@ void HTMLMediaElement::ChangeReadyState(nsMediaReadyState aState)
// Handle raising of "waiting" event during seek (see 4.8.10.9)
if (mPlayingBeforeSeek &&
oldState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) {
mReadyState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) {
DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
}
if (oldState < nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA &&
mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA &&
!mLoadedFirstFrame)
{
!mLoadedDataFired) {
DispatchAsyncEvent(NS_LITERAL_STRING("loadeddata"));
mLoadedFirstFrame = true;
mLoadedDataFired = true;
}
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()
{
// For stream inputs, we activate autoplay on HAVE_CURRENT_DATA because
@ -3278,8 +3290,7 @@ bool HTMLMediaElement::CanActivateAutoplay()
return !mPausedForInactiveDocumentOrChannel &&
mAutoplaying &&
mPaused &&
(mDownloadSuspendedByCache ||
(mDecoder && mReadyState >= nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) ||
((mDecoder && mReadyState >= nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) ||
(mSrcStream && mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA)) &&
HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) &&
mAutoplayEnabled &&
@ -3398,7 +3409,7 @@ bool HTMLMediaElement::IsPlaybackEnded() const
// TODO:
// the current playback position is equal to the effective end of the media resource.
// See bug 449157.
return mNetworkState >= nsIDOMHTMLMediaElement::HAVE_METADATA &&
return mReadyState >= nsIDOMHTMLMediaElement::HAVE_METADATA &&
mDecoder ? mDecoder->IsEnded() : false;
}

View File

@ -9,4 +9,4 @@
# (Fuzzy necessary due to pixel-wise comparison of different JPEGs.
# The vast majority of the fuzziness comes from Linux and WinXP.)
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,
MEDIA_THREAD_STACK_SIZE);
if (NS_FAILED(rv)) {
mStateMachine->OnAudioSinkError();
return rv;
}
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
@ -138,6 +146,8 @@ AudioSink::AudioLoop()
if (NS_FAILED(InitializeAudioStream())) {
NS_WARNING("Initializing AudioStream failed.");
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mStateMachine->OnAudioSinkError();
return;
}
@ -197,10 +207,13 @@ AudioSink::InitializeAudioStream()
// circumstances, so we take care to drop the decoder monitor while
// initializing.
RefPtr<AudioStream> audioStream(new AudioStream());
audioStream->Init(mInfo.mChannels, mInfo.mRate,
mChannel, AudioStream::HighLatency);
// TODO: Check Init's return value and bail on error. Unfortunately this
// causes some tests to fail due to playback failing.
nsresult rv = audioStream->Init(mInfo.mChannels, mInfo.mRate,
mChannel, AudioStream::HighLatency);
if (NS_FAILED(rv)) {
audioStream->Shutdown();
return rv;
}
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mAudioStream = audioStream;
UpdateStreamSettings();

View File

@ -1857,7 +1857,11 @@ MediaCacheStream::NotifyDataEnded(nsresult aStatus)
mResourceID = gMediaCache->AllocateResourceID();
}
// It is prudent to update channel/cache status before calling
// CacheClientNotifyDataEnded() which will read |mChannelEnded|.
FlushPartialBlockInternal(true);
mChannelEnded = true;
gMediaCache->QueueUpdate();
MediaCache::ResourceStreamIterator iter(mResourceID);
while (MediaCacheStream* stream = iter.Next()) {
@ -1871,9 +1875,6 @@ MediaCacheStream::NotifyDataEnded(nsresult aStatus)
stream->mClient->CacheClientNotifyDataEnded(aStatus);
}
}
mChannelEnded = true;
gMediaCache->QueueUpdate();
}
void

View File

@ -139,7 +139,6 @@ void MediaDecoder::SetDormantIfNecessary(bool aDormant)
if(aDormant) {
// enter dormant state
StopProgress();
DestroyDecodedStream();
mDecoderStateMachine->SetDormant(true);
@ -440,7 +439,6 @@ MediaDecoder::MediaDecoder() :
mIsExitingDormant(false),
mPlayState(PLAY_STATE_PAUSED),
mNextState(PLAY_STATE_PAUSED),
mCalledResourceLoaded(false),
mIgnoreProgressData(false),
mInfiniteStream(false),
mOwner(nullptr),
@ -501,7 +499,8 @@ void MediaDecoder::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;
MediaShutdownManager::Instance().Unregister(this);
@ -724,20 +723,8 @@ void MediaDecoder::MetadataLoaded(MediaInfo* aInfo, MetadataTags* 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) {
mOwner->FirstFrameLoaded(notifyResourceIsLoaded);
mOwner->FirstFrameLoaded();
}
// 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
// that autoplay should run.
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()
{
MOZ_ASSERT(NS_IsMainThread());
@ -989,10 +942,8 @@ void MediaDecoder::UpdatePlaybackRate()
void MediaDecoder::NotifySuspendedStatusChanged()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mResource)
return;
bool suspended = mResource->IsSuspendedByCache();
if (mOwner) {
if (mResource && mOwner) {
bool suspended = mResource->IsSuspendedByCache();
mOwner->NotifySuspendedByCache(suspended);
UpdateReadyStateForData();
}
@ -1005,7 +956,6 @@ void MediaDecoder::NotifyBytesDownloaded()
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
UpdatePlaybackRate();
}
UpdateReadyStateForData();
Progress(false);
}
@ -1029,12 +979,13 @@ void MediaDecoder::NotifyDownloadEnded(nsresult aStatus)
}
if (NS_SUCCEEDED(aStatus)) {
ResourceLoaded();
}
else if (aStatus != NS_BASE_STREAM_CLOSED) {
// A final progress event will be fired by the MediaResource calling
// DownloadSuspended on the element.
// Also NotifySuspendedStatusChanged() will be called to update readyState
// if download ended with success.
} else if (aStatus != NS_BASE_STREAM_CLOSED) {
NetworkError();
}
UpdateReadyStateForData();
}
void MediaDecoder::NotifyPrincipalChanged()
@ -1566,6 +1517,7 @@ static void ProgressCallback(nsITimer* aTimer, void* aClosure)
void MediaDecoder::Progress(bool aTimer)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mOwner)
return;
@ -1581,7 +1533,7 @@ void MediaDecoder::Progress(bool aTimer)
now - mProgressTime >= TimeDuration::FromMilliseconds(PROGRESS_MS)) &&
!mDataTime.IsNull() &&
now - mDataTime <= TimeDuration::FromMilliseconds(PROGRESS_MS)) {
mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
mOwner->DownloadProgressed();
mProgressTime = now;
}
@ -1595,8 +1547,8 @@ void MediaDecoder::Progress(bool aTimer)
nsresult MediaDecoder::StartProgress()
{
if (mProgressTimer)
return NS_OK;
MOZ_ASSERT(NS_IsMainThread());
NS_ASSERTION(!mProgressTimer, "Already started progress timer.");
mProgressTimer = do_CreateInstance("@mozilla.org/timer;1");
return mProgressTimer->InitWithFuncCallback(ProgressCallback,
@ -1607,8 +1559,8 @@ nsresult MediaDecoder::StartProgress()
nsresult MediaDecoder::StopProgress()
{
if (!mProgressTimer)
return NS_OK;
MOZ_ASSERT(NS_IsMainThread());
NS_ASSERTION(mProgressTimer, "Already stopped progress timer.");
nsresult rv = mProgressTimer->Cancel();
mProgressTimer = nullptr;

View File

@ -308,9 +308,6 @@ public:
// Called in |Load| to open mResource.
nsresult OpenResource(nsIStreamListener** aStreamListener);
// Called when the video file has completed downloading.
virtual void ResourceLoaded();
// Called if the media file encounters a network error.
virtual void NetworkError();
@ -692,6 +689,12 @@ public:
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
// constraints outlined in the specification. aTimer is true
// 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.
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
// such a manner that progress event data is inaccurate. This is set
// 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).
bool mInfiniteStream;
// Start timer to update download progress information.
nsresult StartProgress();
// Stop progress information timer.
nsresult StopProgress();
// Ensures our media stream has been pinned.
void PinForSeek();

View File

@ -18,6 +18,8 @@ class HTMLMediaElement;
class MediaDecoderOwner
{
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
// (no data has arrived for a while).
virtual void DownloadStalled() = 0;
@ -52,15 +54,9 @@ public:
virtual void MetadataLoaded(const MediaInfo* aInfo,
const MetadataTags* aTags) = 0;
// Called by the video decoder object, on the main thread,
// when it has read the first frame of the video
// aResourceFullyLoaded should be true if the resource has been
// 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 decoder object, on the main thread,
// when it has read the first frame of the video or audio.
virtual void FirstFrameLoaded() = 0;
// Called by the video decoder object, on the main thread,
// 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
// is paused while it buffers up data
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
NEXT_FRAME_UNAVAILABLE,
// The next frame is unavailable due to waiting for more Media Source

View File

@ -1154,9 +1154,9 @@ void MediaDecoderStateMachine::StartPlayback()
SetPlayStartTime(TimeStamp::Now());
NS_ASSERTION(IsPlaying(), "Should report playing by end of StartPlayback()");
if (NS_FAILED(StartAudioThread())) {
DECODER_WARN("Failed to create audio thread");
}
nsresult rv = StartAudioThread();
NS_ENSURE_SUCCESS_VOID(rv);
mDecoder->GetReentrantMonitor().NotifyAll();
mDecoder->UpdateStreamBlockingForStateMachinePlaying();
DispatchDecodeTasksIfNeeded();
@ -1212,8 +1212,10 @@ void MediaDecoderStateMachine::ClearPositionChangeFlag()
MediaDecoderOwner::NextFrameStatus MediaDecoderStateMachine::GetNextFrameStatus()
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
if (IsBuffering() || IsSeeking()) {
if (IsBuffering()) {
return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING;
} else if (IsSeeking()) {
return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING;
} else if (HaveNextFrameData()) {
return MediaDecoderOwner::NEXT_FRAME_AVAILABLE;
}
@ -1789,15 +1791,12 @@ MediaDecoderStateMachine::StartAudioThread()
mStopAudioThread = false;
if (HasAudio() && !mAudioSink) {
mAudioCompleted = false;
mAudioSink = new AudioSink(this,
mAudioStartTime, mInfo.mAudio, mDecoder->GetAudioChannel());
mAudioSink = new AudioSink(this, mAudioStartTime,
mInfo.mAudio, mDecoder->GetAudioChannel());
// OnAudioSinkError() will be called before Init() returns if an error
// occurs during initialization.
nsresult rv = mAudioSink->Init();
if (NS_FAILED(rv)) {
DECODER_WARN("Changed state to SHUTDOWN because audio sink initialization failed");
SetState(DECODER_STATE_SHUTDOWN);
mScheduler->ScheduleAndShutdown();
return rv;
}
NS_ENSURE_SUCCESS(rv, rv);
mAudioSink->SetVolume(mVolume);
mAudioSink->SetPlaybackRate(mPlaybackRate);
@ -2886,6 +2885,10 @@ void MediaDecoderStateMachine::UpdateReadyState() {
AssertCurrentThreadInMonitor();
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) {
return;
}
@ -3118,6 +3121,25 @@ void MediaDecoderStateMachine::OnAudioSinkComplete()
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
// avoid redefined macro in unified build

View File

@ -636,6 +636,9 @@ protected:
// and the sink is shutting down.
void OnAudioSinkComplete();
// Called by the AudioSink to signal errors.
void OnAudioSinkError();
// The decoder object that created this state machine. The state machine
// 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

View File

@ -905,8 +905,11 @@ void ChannelMediaResource::Resume()
// There is (or may be) data to read at mOffset, so start reading it.
// Need to recreate the channel.
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) {}
NS_IMETHOD Run() {
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;
}
private:
nsRefPtr<MediaDecoder> mDecoder;
nsresult mStatus;
nsresult mStatus;
};
void
@ -1248,8 +1264,8 @@ public:
return std::max(aOffset, mSize);
}
virtual bool IsDataCachedToEndOfResource(int64_t aOffset) { return true; }
virtual bool IsSuspendedByCache() { return false; }
virtual bool IsSuspended() { return false; }
virtual bool IsSuspendedByCache() { return true; }
virtual bool IsSuspended() { return true; }
virtual bool IsTransportSeekable() MOZ_OVERRIDE { return true; }
nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges);

View File

@ -2859,8 +2859,11 @@ MediaStreamGraphImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
MonitorAutoLock memoryReportLock(mMemoryReportMonitor);
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) {
// Shutting down, nothing to report.

View File

@ -256,6 +256,13 @@ WMFAudioMFTManager::Output(int64_t aStreamOffset,
mAudioFrameOffset = 0;
}
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);
int32_t offset = std::min<int32_t>(numFramesToStrip, numFrames);

View File

@ -669,7 +669,7 @@ MediaDecoderOwner::NextFrameStatus AudioOffloadPlayer::GetNextFrameStatus()
{
MOZ_ASSERT(NS_IsMainThread());
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) {
return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
} else {

View File

@ -101,7 +101,6 @@ MediaOmxCommonDecoder::PauseStateMachine()
if (!mDecoderStateMachine) {
return;
}
StopProgress();
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"},
];
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.
// sample-fisbone-skeleton4.ogv
// - 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.
[DEFAULT]
skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && toolkit != 'gonk') # b2g-desktop(bug 918299)
skip-if = buildapp == 'mulet'
support-files =
320x240.ogv
320x240.ogv^headers^
@ -157,6 +157,12 @@ support-files =
invalid-cmap-s1c2.opus^headers^
invalid-cmap-short.opus
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^headers^
invalid-m0c3.opus
@ -169,6 +175,8 @@ support-files =
invalid-m2c0.opus^headers^
invalid-m2c1.opus
invalid-m2c1.opus^headers^
invalid-neg_discard.webm
invalid-neg_discard.webm^headers^
invalid-preskip.webm
invalid-preskip.webm^headers^
long.vtt
@ -291,6 +299,7 @@ support-files =
wavedata_u8.wav^headers^
[test_access_control.html]
skip-if = buildapp == 'b2g' && toolkit != 'gonk' # crash on b2g-desktop
[test_aspectratio_mp4.html]
[test_audio1.html]
[test_audio2.html]
@ -350,6 +359,7 @@ skip-if = toolkit == 'android' # bug 608634
[test_imagecapture.html]
[test_info_leak.html]
[test_invalid_reject.html]
[test_invalid_reject_play.html]
[test_invalid_seek.html]
[test_load.html]
[test_load_candidates.html]

View File

@ -31,10 +31,10 @@ function startTest(test, token) {
});
// 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,
"decoder should reject " + test.name);
v.removeEventListener('error', arguments.callee, false);
v.removeEventListener('error', onerror, false);
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);
}, false);
} else {
e.addEventListener('error', function(event) {
e.addEventListener('error', function onerror(event) {
is(errorRun, false, "error handler should run once only!");
errorRun = true;
is(e.readyState, HTMLMediaElement.HAVE_NOTHING,
'test ' + token + ' readyState should be HAVE_NOTHING when load fails.');
e.removeEventListener('error', arguments.callee, true);
e.removeEventListener('error', onerror, true);
removeNodeAndSource(e);
manager.finished(token);
}, true);

View File

@ -36,7 +36,8 @@ let VERY_SLOW_RATE = 0.1,
function ontimeupdate(e) {
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;
}
t.testedForSlowdown = true;

View File

@ -24,7 +24,8 @@
using mozilla::NesteggPacketHolder;
template <>
class nsAutoRefTraits<NesteggPacketHolder> : public nsPointerRefTraits<NesteggPacketHolder>
class nsAutoRefTraits<NesteggPacketHolder> :
public nsPointerRefTraits<NesteggPacketHolder>
{
public:
static void Release(NesteggPacketHolder* aHolder) { delete aHolder; }
@ -39,6 +40,7 @@ using namespace layers;
//#define SEEK_LOGGING
#ifdef PR_LOGGING
#include "prprf.h"
extern PRLogModuleInfo* gMediaDecoderLog;
PRLogModuleInfo* gNesteggLog;
#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.
static int webm_read(void *aBuffer, size_t aLength, void *aUserData)
{
NS_ASSERTION(aUserData, "aUserData must point to a valid AbstractMediaDecoder");
AbstractMediaDecoder* decoder = reinterpret_cast<AbstractMediaDecoder*>(aUserData);
MOZ_ASSERT(aUserData);
AbstractMediaDecoder* decoder =
reinterpret_cast<AbstractMediaDecoder*>(aUserData);
MediaResource* resource = decoder->GetResource();
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)
{
NS_ASSERTION(aUserData, "aUserData must point to a valid AbstractMediaDecoder");
AbstractMediaDecoder* decoder = reinterpret_cast<AbstractMediaDecoder*>(aUserData);
MOZ_ASSERT(aUserData);
AbstractMediaDecoder* decoder =
reinterpret_cast<AbstractMediaDecoder*>(aUserData);
MediaResource* resource = decoder->GetResource();
NS_ASSERTION(resource, "Decoder has no media resource");
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)
{
NS_ASSERTION(aUserData, "aUserData must point to a valid AbstractMediaDecoder");
AbstractMediaDecoder* decoder = reinterpret_cast<AbstractMediaDecoder*>(aUserData);
MOZ_ASSERT(aUserData);
AbstractMediaDecoder* decoder =
reinterpret_cast<AbstractMediaDecoder*>(aUserData);
MediaResource* resource = decoder->GetResource();
NS_ASSERTION(resource, "Decoder has no media resource");
return resource->Tell();
@ -142,26 +147,41 @@ static void webm_log(nestegg * context,
#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)
: MediaDecoderReader(aDecoder),
mContext(nullptr),
mPacketCount(0),
mChannels(0),
: MediaDecoderReader(aDecoder)
, mContext(nullptr)
, mPacketCount(0)
#ifdef MOZ_OPUS
mOpusParser(nullptr),
mOpusDecoder(nullptr),
mSkip(0),
mSeekPreroll(0),
, mOpusDecoder(nullptr)
, mSkip(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
mVideoTrack(0),
mAudioTrack(0),
mAudioStartUsec(-1),
mAudioFrames(0),
mLastVideoFrameTime(0),
mAudioCodec(-1),
mVideoCodec(-1),
mHasVideo(false),
mHasAudio(false)
{
MOZ_COUNT_CTOR(WebMReader);
#ifdef PR_LOGGING
@ -237,6 +257,7 @@ nsresult WebMReader::ResetDecode()
// Reset the decoder.
opus_multistream_decoder_ctl(mOpusDecoder, OPUS_RESET_STATE);
mSkip = mOpusParser->mPreSkip;
mPaddingDiscarded = false;
}
#endif
}
@ -314,10 +335,12 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
// Picture region, taking into account cropping, before scaling
// 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,
params.crop_top,
params.width - (params.crop_right + params.crop_left),
params.height - (params.crop_bottom + params.crop_top));
params.width - cropH,
params.height - cropV);
// If the cropping data appears invalid then use the frame data
if (pictureRect.width <= 0 ||
@ -331,8 +354,8 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
pictureRect.height = params.height;
}
// Validate the container-reported frame and pictureRect sizes. This ensures
// that our video frame creation code doesn't overflow.
// Validate the container-reported frame and pictureRect sizes. This
// ensures that our video frame creation code doesn't overflow.
nsIntSize displaySize(params.display_width, params.display_height);
nsIntSize frameSize(params.width, params.height);
if (!IsValidVideoRegion(frameSize, pictureRect, displaySize)) {
@ -398,7 +421,8 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
Cleanup();
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,
&mVorbisComment,
@ -423,7 +447,6 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
mInfo.mAudio.mRate = mVorbisDsp.vi->rate;
mInfo.mAudio.mChannels = mVorbisDsp.vi->channels;
mChannels = mInfo.mAudio.mChannels;
#ifdef MOZ_OPUS
} else if (mAudioCodec == NESTEGG_CODEC_OPUS) {
unsigned char* data = 0;
@ -445,9 +468,10 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
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,
("Invalid Opus header: CodecDelay and pre-skip do not match!\n"));
("Invalid Opus header: CodecDelay and pre-skip do not match!"));
Cleanup();
return NS_ERROR_FAILURE;
}
@ -455,7 +479,6 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
mInfo.mAudio.mRate = mOpusParser->mRate;
mInfo.mAudio.mChannels = mOpusParser->mChannels;
mChannels = mInfo.mAudio.mChannels;
mSeekPreroll = params.seek_preroll;
#endif
} else {
@ -486,33 +509,18 @@ bool WebMReader::InitOpusDecoder()
NS_ASSERTION(mOpusDecoder == nullptr, "leaking OpusDecoder");
mOpusDecoder = opus_multistream_decoder_create(mOpusParser->mRate,
mOpusParser->mChannels,
mOpusParser->mStreams,
mOpusParser->mCoupledStreams,
mOpusParser->mMappingTable,
&r);
mOpusParser->mChannels,
mOpusParser->mStreams,
mOpusParser->mCoupledStreams,
mOpusParser->mMappingTable,
&r);
mSkip = mOpusParser->mPreSkip;
mPaddingDiscarded = false;
return r == OPUS_OK;
}
#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)
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
@ -530,7 +538,6 @@ bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset)
return false;
}
const uint32_t rate = mInfo.mAudio.mRate;
uint64_t tstamp_usecs = tstamp / NS_PER_USEC;
if (mAudioStartUsec == -1) {
// 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 vorbis decode doesn't use data from before the gap to help decode
// from after the gap.
CheckedInt64 tstamp_frames = UsecsToFrames(tstamp_usecs, rate);
CheckedInt64 decoded_frames = UsecsToFrames(mAudioStartUsec, rate);
CheckedInt64 tstamp_frames = UsecsToFrames(tstamp_usecs, mInfo.mAudio.mRate);
CheckedInt64 decoded_frames = UsecsToFrames(mAudioStartUsec,
mInfo.mAudio.mRate);
if (!tstamp_frames.isValid() || !decoded_frames.isValid()) {
NS_WARNING("Int overflow converting WebM times to frames");
return false;
@ -554,10 +562,11 @@ bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset)
}
if (tstamp_frames.value() > decoded_frames.value()) {
#ifdef DEBUG
CheckedInt64 usecs = FramesToUsecs(tstamp_frames.value() - decoded_frames.value(), rate);
LOG(PR_LOG_DEBUG, ("WebMReader detected gap of %lld, %lld frames, in audio stream\n",
int64_t gap_frames = tstamp_frames.value() - decoded_frames.value();
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,
tstamp_frames.value() - decoded_frames.value()));
gap_frames));
#endif
mPacketCount++;
mAudioStartUsec = tstamp_usecs;
@ -573,198 +582,235 @@ bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset)
return false;
}
if (mAudioCodec == NESTEGG_CODEC_VORBIS) {
ogg_packet opacket = InitOggPacket(data, length, false, false, -1);
if (vorbis_synthesis(&mVorbisBlock, &opacket) != 0) {
if (!DecodeVorbis(data, length, aOffset, tstamp_usecs, &total_frames)) {
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) {
#ifdef MOZ_OPUS
uint32_t channels = mOpusParser->mChannels;
// 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)
if (!DecodeOpus(data, length, aOffset, tstamp_usecs, aPacket)) {
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
// 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;
}
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)
{
// The packet queue that packets will be pushed on if they
@ -935,9 +981,9 @@ bool WebMReader::DecodeVideoFrame(bool &aKeyframeSkip,
vpx_image_t *img;
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;
b.mPlanes[0].mData = img->planes[0];
b.mPlanes[0].mStride = img->stride[0];
@ -960,9 +1006,10 @@ bool WebMReader::DecodeVideoFrame(bool &aKeyframeSkip,
IntRect picture = ToIntRect(mPicture);
if (img->d_w != static_cast<uint32_t>(mInitialFrame.width) ||
img->d_h != static_cast<uint32_t>(mInitialFrame.height)) {
// Frame size is different from what the container reports. This is legal
// in WebM, and we will preserve the ratio of the crop rectangle as it
// was reported relative to the picture size reported by the container.
// Frame size is different from what the container reports. This is
// legal in WebM, and we will preserve the ratio of the crop rectangle
// as it was reported relative to the picture size reported by the
// container.
picture.x = (mPicture.x * img->d_w) / mInitialFrame.width;
picture.y = (mPicture.y * img->d_h) / mInitialFrame.height;
picture.width = (img->d_w * mPicture.width) / mInitialFrame.width;
@ -973,7 +1020,7 @@ bool WebMReader::DecodeVideoFrame(bool &aKeyframeSkip,
mDecoder->GetImageContainer(),
holder->mOffset,
tstamp_usecs,
(next_tstamp / NS_PER_USEC) - tstamp_usecs,
(next_tstamp / NS_PER_USEC)-tstamp_usecs,
b,
si.is_kf,
-1,
@ -1012,10 +1059,14 @@ nsresult WebMReader::Seek(int64_t aTarget, int64_t aStartTime, int64_t aEndTime,
uint64_t target = aTarget * NS_PER_USEC;
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);
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.
int64_t offset = 0;
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);
LOG(PR_LOG_DEBUG, ("Reader [%p]: track_seek for %u failed, offset_seek to %lld r=%d",
this, trackToSeek, offset, r));
LOG(PR_LOG_DEBUG, ("Reader [%p]: attempted offset_seek to %lld r=%d",
this, offset, r));
if (r != 0) {
return NS_ERROR_FAILURE;
}
@ -1072,7 +1123,8 @@ nsresult WebMReader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime)
double endTime = (end - startOffset) / NS_PER_S;
// If this range extends to the end of the file, the true end time
// is the file's duration.
if (mContext && resource->IsDataCachedToEndOfResource(ranges[index].mStart)) {
if (mContext &&
resource->IsDataCachedToEndOfResource(ranges[index].mStart)) {
uint64_t duration = 0;
if (nestegg_duration(mContext, &duration) == 0) {
endTime = duration / NS_PER_S;
@ -1086,7 +1138,8 @@ nsresult WebMReader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime)
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);
}

View File

@ -134,9 +134,11 @@ public:
virtual nsresult ReadMetadata(MediaInfo* aInfo,
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 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 bool IsMediaSeekable() MOZ_OVERRIDE;
@ -157,13 +159,6 @@ protected:
// Pushes a packet to the front of the video packet queue.
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
// Setup opus decoder
bool InitOpusDecoder();
@ -176,6 +171,14 @@ protected:
// must be held during this call. The caller is responsible for freeing
// aPacket.
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
// reading metadata or destruction of the reader itself.
@ -194,16 +197,14 @@ private:
vorbis_comment mVorbisComment;
vorbis_dsp_state mVorbisDsp;
vorbis_block mVorbisBlock;
uint32_t mPacketCount;
uint32_t mChannels;
int64_t mPacketCount;
#ifdef MOZ_OPUS
// Opus decoder state
nsAutoPtr<OpusParser> mOpusParser;
OpusMSDecoder *mOpusDecoder;
int mSkip; // Number of samples left to trim before playback.
uint64_t mSeekPreroll; // Number of nanoseconds that must be discarded after seeking.
uint16_t mSkip; // Samples left to trim before playback.
uint64_t mSeekPreroll; // Nanoseconds to discard after seeking.
#endif
// 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
bool mHasVideo;
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

View File

@ -5,6 +5,7 @@
#include "CanvasRenderingContext2D.h"
#include "mozilla/gfx/Helpers.h"
#include "nsXULElement.h"
#include "nsIServiceManager.h"
@ -376,7 +377,7 @@ public:
RefPtr<SourceSurface> strokePaint =
DoSourcePaint(mStrokePaintRect, CanvasRenderingContext2D::Style::STROKE);
AutoSaveTransform autoSaveTransform(mFinalTarget);
AutoRestoreTransform autoRestoreTransform(mFinalTarget);
mFinalTarget->SetTransform(Matrix());
mgfx::FilterSupport::RenderFilterDescription(
@ -2168,7 +2169,8 @@ public:
gfxTextPerfMetrics* tp = mPresContext->GetTextPerfMetrics();
nsRefPtr<nsFontMetrics> fontMetrics;
nsDeviceContext* dc = mPresContext->DeviceContext();
dc->GetMetricsFor(mFont, mFontLanguage, nullptr, tp,
dc->GetMetricsFor(mFont, mFontLanguage, gfxFont::eHorizontal,
nullptr, tp,
*getter_AddRefs(fontMetrics));
return NSAppUnitsToFloatPixels(fontMetrics->XHeight(),
nsPresContext::AppUnitsPerCSSPixel());
@ -3442,7 +3444,7 @@ CanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText,
// offset pt.y based on text baseline
processor.mFontgrp->UpdateUserFonts(); // ensure user font generation is current
const gfxFont::Metrics& fontMetrics =
processor.mFontgrp->GetFirstValidFont()->GetMetrics();
processor.mFontgrp->GetFirstValidFont()->GetMetrics(gfxFont::eHorizontal); // XXX vertical?
gfxFloat anchorY;
@ -3983,6 +3985,10 @@ CanvasRenderingContext2D::DrawDirectlyToCanvas(
std::ceil(imgSize.height * 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);
context->SetMatrix(contextMatrix.
Scale(1.0 / contextScale.width,

View File

@ -297,9 +297,13 @@ Headers::IsForbiddenResponseHeader(const nsACString& aName) const
}
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

View File

@ -3910,20 +3910,7 @@ protected:
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);
}
bool aDeleting);
virtual
~FactoryOp()
@ -5075,20 +5062,42 @@ public:
static QuotaClient*
GetInstance()
{
MOZ_ASSERT(NS_IsMainThread());
return sInstance;
}
void
NoteBackgroundThread(nsIEventTarget* aBackgroundThread);
static bool
IsShuttingDownOnMainThread()
{
MOZ_ASSERT(NS_IsMainThread());
if (sInstance) {
return sInstance->mShutdownRequested;
}
return QuotaManager::IsShuttingDown();
}
static bool
IsShuttingDownOnNonMainThread()
{
MOZ_ASSERT(!NS_IsMainThread());
return QuotaManager::IsShuttingDown();
}
bool
HasShutDown() const
IsShuttingDown() const
{
MOZ_ASSERT(NS_IsMainThread());
return mShutdownRequested;
}
void
NoteBackgroundThread(nsIEventTarget* aBackgroundThread);
NS_INLINE_DECL_REFCOUNTING(QuotaClient)
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);
return actor.forget().take();
}
@ -5568,6 +5581,7 @@ RecvPBackgroundIDBFactoryConstructor(PBackgroundParent* /* aManager */,
{
AssertIsOnBackgroundThread();
MOZ_ASSERT(aActor);
MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread());
return true;
}
@ -5751,6 +5765,7 @@ Factory::Factory(const OptionalWindowId& aOptionalWindowId)
, mActorDestroyed(false)
{
AssertIsOnBackgroundThread();
MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread());
}
Factory::~Factory()
@ -5763,6 +5778,7 @@ already_AddRefed<Factory>
Factory::Create(const OptionalWindowId& aOptionalWindowId)
{
AssertIsOnBackgroundThread();
MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread());
// If this is the first instance then we need to do some initialization.
if (!sFactoryInstanceCount) {
@ -5872,6 +5888,10 @@ Factory::AllocPBackgroundIDBFactoryRequestParent(
AssertIsOnBackgroundThread();
MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None);
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread())) {
return nullptr;
}
const CommonFactoryRequestParams* commonParams;
switch (aParams.type()) {
@ -5933,6 +5953,7 @@ Factory::RecvPBackgroundIDBFactoryRequestConstructor(
AssertIsOnBackgroundThread();
MOZ_ASSERT(aActor);
MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None);
MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread());
auto* op = static_cast<FactoryOp*>(aActor);
@ -10304,6 +10325,25 @@ AutoSetProgressHandler::Register(
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
FactoryOp::Open()
{
@ -10314,7 +10354,8 @@ FactoryOp::Open()
nsRefPtr<ContentParent> contentParent;
mContentParent.swap(contentParent);
if (!OperationMayProceed()) {
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnMainThread()) ||
!OperationMayProceed()) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
@ -10392,7 +10433,8 @@ FactoryOp::RetryCheckPermission()
nsRefPtr<ContentParent> contentParent;
mContentParent.swap(contentParent);
if (!OperationMayProceed()) {
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnMainThread()) ||
!OperationMayProceed()) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
@ -10428,16 +10470,14 @@ FactoryOp::SendToIOThread()
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mState == State_OpenPending);
if (!OperationMayProceed()) {
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnMainThread()) ||
!OperationMayProceed()) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
QuotaManager* quotaManager = QuotaManager::Get();
if (NS_WARN_IF(!quotaManager)) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
MOZ_ASSERT(quotaManager);
// Must set this before dispatching otherwise we will race with the IO thread.
mState = State_DatabaseWorkOpen;
@ -10819,6 +10859,7 @@ FactoryOp::FinishOpen()
MOZ_ASSERT(!mDatabaseId.IsEmpty());
MOZ_ASSERT(!mBlockedQuotaManager);
MOZ_ASSERT(!mContentParent);
MOZ_ASSERT(!QuotaClient::IsShuttingDownOnMainThread());
PersistenceType persistenceType = mCommonParams.metadata().persistenceType();
@ -10830,10 +10871,8 @@ FactoryOp::FinishOpen()
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
QuotaManager* quotaManager;
if (QuotaManager::IsShuttingDown() ||
!(quotaManager = QuotaManager::GetOrCreate())) {
QuotaManager* quotaManager = QuotaManager::GetOrCreate();
if (NS_WARN_IF(!quotaManager)) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
@ -11002,9 +11041,8 @@ OpenDatabaseOp::QuotaManagerOpen()
MOZ_ASSERT(!mOfflineStorage);
QuotaClient* quotaClient = QuotaClient::GetInstance();
MOZ_ASSERT(quotaClient);
if (NS_WARN_IF(quotaClient->HasShutDown())) {
if (NS_WARN_IF(!quotaClient) ||
NS_WARN_IF(quotaClient->IsShuttingDown())) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
@ -11051,7 +11089,8 @@ OpenDatabaseOp::DoDatabaseWork()
"OpenDatabaseHelper::DoDatabaseWork",
js::ProfileEntry::Category::STORAGE);
if (NS_WARN_IF(QuotaManager::IsShuttingDown()) || !OperationMayProceed()) {
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) ||
!OperationMayProceed()) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
@ -11502,7 +11541,9 @@ OpenDatabaseOp::BeginVersionChange()
MOZ_ASSERT(!mDatabase);
MOZ_ASSERT(!mVersionChangeTransaction);
if (IsActorDestroyed()) {
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) ||
!OperationMayProceed() ||
IsActorDestroyed()) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
@ -11658,7 +11699,9 @@ OpenDatabaseOp::SendUpgradeNeeded()
MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
MOZ_ASSERT_IF(!IsActorDestroyed(), mDatabase);
if (IsActorDestroyed()) {
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) ||
!OperationMayProceed() ||
IsActorDestroyed()) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
@ -12066,6 +12109,12 @@ VersionChangeOp::DoDatabaseWork(TransactionBase* aTransaction)
aTransaction->AssertIsOnTransactionThread();
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",
"VersionChangeOp::DoDatabaseWork",
js::ProfileEntry::Category::STORAGE);
@ -12267,7 +12316,8 @@ DeleteDatabaseOp::DoDatabaseWork()
"DeleteDatabaseOp::DoDatabaseWork",
js::ProfileEntry::Category::STORAGE);
if (!OperationMayProceed()) {
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) ||
!OperationMayProceed()) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
@ -12343,7 +12393,9 @@ DeleteDatabaseOp::BeginVersionChange()
MOZ_ASSERT(mState == State_BeginVersionChange);
MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
if (IsActorDestroyed()) {
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) ||
!OperationMayProceed() ||
IsActorDestroyed()) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
@ -12381,7 +12433,9 @@ DeleteDatabaseOp::DispatchToWorkThread()
MOZ_ASSERT(mState == State_WaitingForTransactionsToComplete);
MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
if (IsActorDestroyed()) {
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) ||
!OperationMayProceed() ||
IsActorDestroyed()) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
@ -12474,6 +12528,12 @@ VersionChangeOp::RunOnMainThread()
MOZ_ASSERT(NS_IsMainThread());
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();
MOZ_ASSERT(quotaManager);
@ -12497,7 +12557,8 @@ VersionChangeOp::RunOnIOThread()
"DeleteDatabaseOp::VersionChangeOp::RunOnIOThread",
js::ProfileEntry::Category::STORAGE);
if (!OperationMayProceed()) {
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) ||
!OperationMayProceed()) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}

View File

@ -19,6 +19,7 @@
#include "TabChild.h"
#include "mozilla/Attributes.h"
#include "mozilla/a11y/DocAccessibleChild.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/ContentBridgeChild.h"
#include "mozilla/dom/ContentBridgeParent.h"
@ -707,6 +708,20 @@ ContentChild::InitXPCOM()
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*
ContentChild::AllocPMemoryReportRequestChild(const uint32_t& aGeneration,
const bool &aAnonymize,

View File

@ -380,6 +380,8 @@ public:
const uint64_t& aID,
const bool& aIsForApp,
const bool& aIsForBrowser) MOZ_OVERRIDE;
virtual PDocAccessibleChild* AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&) MOZ_OVERRIDE;
virtual bool DeallocPDocAccessibleChild(PDocAccessibleChild*) MOZ_OVERRIDE;
private:
virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;

View File

@ -30,6 +30,8 @@
#include "CrashReporterParent.h"
#include "IHistory.h"
#include "mozIApplication.h"
#include "mozilla/a11y/DocAccessibleParent.h"
#include "nsAccessibilityService.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/dom/DataStoreService.h"
#include "mozilla/dom/DOMStorageIPC.h"
@ -2697,6 +2699,34 @@ ContentParent::Observe(nsISupports* aSubject,
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*
ContentParent::AllocPCompositorParent(mozilla::ipc::Transport* aTransport,
base::ProcessId aOtherProcess)
@ -3822,9 +3852,6 @@ ContentParent::RecvKeywordToURI(const nsCString& aKeyword, OptionalInputStreamPa
bool
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;
}

View File

@ -666,6 +666,11 @@ private:
int32_t* aSliceRefCnt,
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
// release these objects in ShutDownProcess. See the comment there for more
// details.

View File

@ -14,6 +14,7 @@ include protocol PCompositor;
include protocol PContentBridge;
include protocol PCycleCollectWithLogs;
include protocol PCrashReporter;
include protocol PDocAccessible;
include protocol PExternalHelperApp;
include protocol PDeviceStorageRequest;
include protocol PFileDescriptorSet;
@ -332,6 +333,7 @@ intr protocol PContent
manages PCellBroadcast;
manages PCrashReporter;
manages PCycleCollectWithLogs;
manages PDocAccessible;
manages PDeviceStorageRequest;
manages PFileSystemRequest;
manages PExternalHelperApp;
@ -488,6 +490,14 @@ child:
OnAppThemeChanged();
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
* 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 BEFORE_FIRST_PAINT[] = "before-first-paint";
static bool sCpowsEnabled = false;
static int32_t sActiveDurationMs = 10;
static bool sActiveDurationMsSet = false;
@ -2783,12 +2782,6 @@ TabChild::InitRenderingState()
BEFORE_FIRST_PAINT,
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;
}
@ -2917,10 +2910,8 @@ TabChild::DoSendBlockingMessage(JSContext* aCx,
return false;
}
InfallibleTArray<CpowEntry> cpows;
if (sCpowsEnabled) {
if (!Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
return false;
}
if (!Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
return false;
}
if (aIsSync) {
return SendSyncMessage(PromiseFlatString(aMessage), data, cpows,
@ -2943,10 +2934,8 @@ TabChild::DoSendAsyncMessage(JSContext* aCx,
return false;
}
InfallibleTArray<CpowEntry> cpows;
if (sCpowsEnabled) {
if (!Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
return false;
}
if (!Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
return false;
}
return SendAsyncMessage(PromiseFlatString(aMessage), data, cpows,
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"
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.
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.

View File

@ -156,17 +156,7 @@ function loadAndSelectTestTab() {
gBrowser.selectedTab = tab;
let browser = gBrowser.getBrowserForTab(tab);
// 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);
browser.messageManager.loadFrameScript(kContentScript, false);
let deferred = Promise.defer();
browser.addEventListener("DOMContentLoaded", function onBrowserLoad(aEvent) {

View File

@ -3077,6 +3077,12 @@ WifiWorker.prototype = {
debug("Invalid SSID value.");
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 &&
securityType != WIFI_SECURITY_TYPE_WPA_PSK &&
securityType != WIFI_SECURITY_TYPE_WPA2_PSK) {

View File

@ -136,7 +136,7 @@ InterpolationQualityFromFilter(Filter aFilter)
case Filter::POINT:
return kCGInterpolationNone;
case Filter::GOOD:
return kCGInterpolationDefault;
return kCGInterpolationLow;
}
}

View File

@ -11,17 +11,29 @@
namespace mozilla {
namespace gfx {
class AutoSaveTransform
class AutoRestoreTransform
{
public:
explicit AutoSaveTransform(DrawTarget *aTarget)
explicit AutoRestoreTransform(DrawTarget *aTarget)
: mDrawTarget(aTarget),
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:

View File

@ -251,7 +251,7 @@ BasicCompositor::DrawQuad(const gfx::Rect& aRect,
RefPtr<DrawTarget> dest = buffer;
buffer->PushClipRect(aClipRect);
AutoSaveTransform autoSaveTransform(dest);
AutoRestoreTransform autoRestoreTransform(dest);
Matrix newTransform;
Rect transformBounds;

View File

@ -62,6 +62,7 @@ public:
void Destroy();
nsresult GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
gfxFont::Orientation aOrientation,
gfxUserFontSet* aUserFontSet,
gfxTextPerfMetrics* aTextPerf,
nsFontMetrics*& aMetrics);
@ -122,6 +123,7 @@ nsFontCache::Observe(nsISupports*, const char* aTopic, const char16_t*)
nsresult
nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
gfxFont::Orientation aOrientation,
gfxUserFontSet* aUserFontSet,
gfxTextPerfMetrics* aTextPerf,
nsFontMetrics*& aMetrics)
@ -137,7 +139,7 @@ nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
for (int32_t i = n; i >= 0; --i) {
fm = mFontMetrics[i];
if (fm->Font().Equals(aFont) && fm->GetUserFontSet() == aUserFontSet &&
fm->Language() == aLanguage) {
fm->Language() == aLanguage && fm->Orientation() == aOrientation) {
if (i != n) {
// promote it to the end of the cache
mFontMetrics.RemoveElementAt(i);
@ -153,7 +155,8 @@ nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
fm = new nsFontMetrics();
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)) {
// the mFontMetrics list has the "head" at the end, because append
// is cheaper than insert
@ -172,7 +175,8 @@ nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
Compact();
fm = new nsFontMetrics();
NS_ADDREF(fm);
rv = fm->Init(aFont, aLanguage, mContext, aUserFontSet, aTextPerf);
rv = fm->Init(aFont, aLanguage, aOrientation, mContext, aUserFontSet,
aTextPerf);
if (NS_SUCCEEDED(rv)) {
mFontMetrics.AppendElement(fm);
aMetrics = fm;
@ -260,6 +264,7 @@ nsDeviceContext::~nsDeviceContext()
nsresult
nsDeviceContext::GetMetricsFor(const nsFont& aFont,
nsIAtom* aLanguage,
gfxFont::Orientation aOrientation,
gfxUserFontSet* aUserFontSet,
gfxTextPerfMetrics* aTextPerf,
nsFontMetrics*& aMetrics)
@ -270,8 +275,8 @@ nsDeviceContext::GetMetricsFor(const nsFont& aFont,
mFontCache->Init(this);
}
return mFontCache->GetMetricsFor(aFont, aLanguage, aUserFontSet,
aTextPerf, aMetrics);
return mFontCache->GetMetricsFor(aFont, aLanguage, aOrientation,
aUserFontSet, aTextPerf, aMetrics);
}
nsresult

View File

@ -9,6 +9,7 @@
#include <stdint.h> // for uint32_t
#include <sys/types.h> // for int32_t
#include "gfxTypes.h" // for gfxFloat
#include "gfxFont.h" // for gfxFont::Orientation
#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
#include "nsAutoPtr.h" // for nsRefPtr
#include "nsCOMPtr.h" // for nsCOMPtr
@ -118,6 +119,7 @@ public:
* @return error status
*/
nsresult GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
gfxFont::Orientation aOrientation,
gfxUserFontSet* aUserFontSet,
gfxTextPerfMetrics* aTextPerf,
nsFontMetrics*& aMetrics);

View File

@ -104,6 +104,7 @@ nsFontMetrics::~nsFontMetrics()
nsresult
nsFontMetrics::Init(const nsFont& aFont, nsIAtom* aLanguage,
gfxFont::Orientation aOrientation,
nsDeviceContext *aContext,
gfxUserFontSet *aUserFontSet,
gfxTextPerfMetrics *aTextPerf)
@ -112,6 +113,7 @@ nsFontMetrics::Init(const nsFont& aFont, nsIAtom* aLanguage,
mFont = aFont;
mLanguage = aLanguage;
mOrientation = aOrientation;
mDeviceContext = aContext;
mP2A = mDeviceContext->AppUnitsPerDevPixel();
@ -147,7 +149,7 @@ nsFontMetrics::Destroy()
const gfxFont::Metrics& nsFontMetrics::GetMetrics() const
{
return mFontGroup->GetFirstValidFont()->GetMetrics();
return mFontGroup->GetFirstValidFont()->GetMetrics(mOrientation);
}
nscoord

View File

@ -57,6 +57,7 @@ public:
* @see nsDeviceContext#GetMetricsFor()
*/
nsresult Init(const nsFont& aFont, nsIAtom* aLanguage,
gfxFont::Orientation aOrientation,
nsDeviceContext *aContext,
gfxUserFontSet *aUserFontSet,
gfxTextPerfMetrics *aTextPerf);
@ -174,6 +175,11 @@ public:
*/
nsIAtom* Language() { return mLanguage; }
/**
* Returns the orientation (horizontal/vertical) of these metrics.
*/
gfxFont::Orientation Orientation() { return mOrientation; }
int32_t GetMaxStringLength();
// Get the width for this string. aWidth will be updated with the
@ -223,6 +229,7 @@ private:
nsDeviceContext *mDeviceContext;
int32_t mP2A;
bool mTextRunRTL;
gfxFont::Orientation mOrientation;
};
#endif /* NSFONTMETRICS__H__ */

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