зеркало из https://github.com/mozilla/gecko-dev.git
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
Коммит
22ae53697c
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -97,7 +97,7 @@ private:
|
|||
|
||||
static EAvailableAtkSignals gAvailableAtkSignals;
|
||||
|
||||
uint16_t CreateMaiInterfaces(void);
|
||||
uint16_t CreateMaiInterfaces();
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
|
|
|
@ -35,6 +35,7 @@ LOCAL_INCLUDES += [
|
|||
'/accessible/base',
|
||||
'/accessible/generic',
|
||||
'/accessible/html',
|
||||
'/accessible/ipc',
|
||||
'/accessible/xpcom',
|
||||
'/accessible/xul',
|
||||
'/other-licenses/atk-1.0',
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -783,7 +783,9 @@ enum Role {
|
|||
/**
|
||||
* Represent a keyboard or keypad key (ARIA role "key").
|
||||
*/
|
||||
KEY = 129
|
||||
KEY = 129,
|
||||
|
||||
LAST_ROLE = KEY
|
||||
};
|
||||
|
||||
} // namespace role
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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*
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,28 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
IPDL_SOURCES += ['PDocAccessible.ipdl']
|
||||
|
||||
EXPORTS.mozilla.a11y += [
|
||||
'DocAccessibleChild.h',
|
||||
'DocAccessibleParent.h',
|
||||
'ProxyAccessible.h'
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'DocAccessibleChild.cpp',
|
||||
'DocAccessibleParent.cpp',
|
||||
'ProxyAccessible.cpp'
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../base',
|
||||
'../generic',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
|
@ -20,6 +20,8 @@ XPCOMUtils.defineLazyModuleGetter(this, 'ContentControl',
|
|||
'resource://gre/modules/accessibility/ContentControl.jsm');
|
||||
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(
|
||||
|
|
|
@ -33,6 +33,15 @@ PlatformShutdown()
|
|||
{
|
||||
}
|
||||
|
||||
void
|
||||
ProxyCreated(ProxyAccessible*)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ProxyDestroyed(ProxyAccessible*)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -18,3 +18,13 @@ void
|
|||
a11y::PlatformShutdown()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
a11y::ProxyCreated(ProxyAccessible*)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
a11y::ProxyDestroyed(ProxyAccessible*)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -34,3 +34,12 @@ a11y::PlatformShutdown()
|
|||
nsWinUtils::ShutdownWindowEmulation();
|
||||
}
|
||||
|
||||
void
|
||||
a11y::ProxyCreated(ProxyAccessible*)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
a11y::ProxyDestroyed(ProxyAccessible*)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -269,6 +269,7 @@ let consoleWatcher = {
|
|||
'Mixed Content Message',
|
||||
'CSP',
|
||||
'Invalid HSTS Headers',
|
||||
'Invalid HPKP Headers',
|
||||
'Insecure Password Field',
|
||||
'SSL',
|
||||
'CORS'
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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));
|
||||
},
|
||||
|
||||
|
|
|
@ -2563,7 +2563,6 @@
|
|||
]]>
|
||||
</body>
|
||||
</method>
|
||||
#endif
|
||||
|
||||
<method name="moveTabTo">
|
||||
<parameter name="aTab"/>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
const INVALIDPIN1 = "pin-sha256=\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\";";
|
||||
const INVALIDPIN2 = "pin-sha256=\"AAAAAAAAAAAAAAAAAAAAAAAAAj0e1Md7GkYYkVoZWmM=\";";
|
||||
const VALIDPIN = "pin-sha256=\"hXweb81C3HnmM2Ai1dnUzFba40UJMhuu8qZmvN/6WWc=\";";
|
||||
|
||||
function handleRequest(request, response)
|
||||
{
|
||||
// avoid confusing cache behaviors
|
||||
response.setHeader("Cache-Control", "no-cache", false);
|
||||
|
||||
response.setHeader("Content-Type", "text/plain; charset=utf-8", false);
|
||||
switch (request.queryString) {
|
||||
case "zeromaxagevalid":
|
||||
response.setHeader("Public-Key-Pins", "max-age=0;" + VALIDPIN +
|
||||
INVALIDPIN2 + "includeSubdomains");
|
||||
break;
|
||||
case "valid":
|
||||
default:
|
||||
response.setHeader("Public-Key-Pins", "max-age=50000;" + VALIDPIN +
|
||||
INVALIDPIN2 + "includeSubdomains");
|
||||
}
|
||||
|
||||
response.write("Hello world!" + request.host);
|
||||
}
|
|
@ -1043,33 +1043,31 @@ if (Services.prefs.getBoolPref("privacy.panicButton.enabled")) {
|
|||
|
||||
#ifdef E10S_TESTING_ONLY
|
||||
/**
|
||||
* 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
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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":
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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-----
|
Двоичные данные
build/pgo/certs/cert8.db
Двоичные данные
build/pgo/certs/cert8.db
Двоичный файл не отображается.
Двоичные данные
build/pgo/certs/key3.db
Двоичные данные
build/pgo/certs/key3.db
Двоичный файл не отображается.
|
@ -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
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -101,7 +101,6 @@ MediaOmxCommonDecoder::PauseStateMachine()
|
|||
if (!mDecoderStateMachine) {
|
||||
return;
|
||||
}
|
||||
StopProgress();
|
||||
mDecoderStateMachine->SetDormant(true);
|
||||
}
|
||||
|
||||
|
|
Двоичный файл не отображается.
|
@ -0,0 +1 @@
|
|||
Cache-Control: no-store
|
Двоичный файл не отображается.
|
@ -0,0 +1 @@
|
|||
Cache-Control: no-store
|
Двоичный файл не отображается.
|
@ -0,0 +1 @@
|
|||
Cache-Control: no-store
|
Двоичный файл не отображается.
|
@ -0,0 +1 @@
|
|||
Cache-Control: no-store
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Test rejection of invalid files during playback</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var manager = new MediaTestManager;
|
||||
|
||||
function startTest(test, token) {
|
||||
var v = document.createElement('video');
|
||||
manager.started(token);
|
||||
|
||||
// Seeing a decoder error is a success.
|
||||
v.addEventListener("error", function onerror(e) {
|
||||
is(v.error.code, v.error.MEDIA_ERR_DECODE,
|
||||
"decoder should reject " + test.name);
|
||||
v.removeEventListener("error", onerror, false);
|
||||
manager.finished(token);
|
||||
});
|
||||
|
||||
v.addEventListener("ended", function onended(e) {
|
||||
ok(false, "decoder should have rejected file before playback ended");
|
||||
v.removeEventListener("ended", onended, false);
|
||||
manager.finished(token);
|
||||
});
|
||||
|
||||
document.body.appendChild(v);
|
||||
v.src = test.name;
|
||||
v.play();
|
||||
}
|
||||
|
||||
manager.runTests(gInvalidPlayTests, startTest);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -30,12 +30,12 @@ function maketest(attach_media, name, type, check_metadata) {
|
|||
manager.finished(token);
|
||||
}, 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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -136,7 +136,7 @@ InterpolationQualityFromFilter(Filter aFilter)
|
|||
case Filter::POINT:
|
||||
return kCGInterpolationNone;
|
||||
case Filter::GOOD:
|
||||
return kCGInterpolationDefault;
|
||||
return kCGInterpolationLow;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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__ */
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче