This commit is contained in:
Wes Kocher 2015-09-28 16:32:47 -07:00
Родитель d23f75e97a ecad490bd0
Коммит a95e0f8358
237 изменённых файлов: 5255 добавлений и 1467 удалений

Просмотреть файл

@ -738,6 +738,13 @@ DocAccessible::AttributeWillChange(nsIDocument* aDocument,
mStateBitWasOn = accessible->Unavailable();
}
void
DocAccessible::NativeAnonymousChildListChange(nsIDocument* aDocument,
nsIContent* aContent,
bool aIsRemove)
{
}
void
DocAccessible::AttributeChanged(nsIDocument* aDocument,
dom::Element* aElement,
@ -1357,6 +1364,11 @@ DocAccessible::ProcessInvalidationList()
continue;
}
if (!child->Parent()) {
NS_ERROR("The accessible is in document but doesn't have a parent");
continue;
}
// XXX: update context flags
{
Accessible* oldParent = child->Parent();

Просмотреть файл

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="01ffe82cf088ca8fda9fe6783dc5cad2c3dde01c"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4c0a6d4e8501368db8e5d6029a41db985ef1252a"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>

Просмотреть файл

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="01ffe82cf088ca8fda9fe6783dc5cad2c3dde01c"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4c0a6d4e8501368db8e5d6029a41db985ef1252a"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>

Просмотреть файл

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="01ffe82cf088ca8fda9fe6783dc5cad2c3dde01c"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="4c0a6d4e8501368db8e5d6029a41db985ef1252a"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="27eb2f04e149fc2c9976d881b1b5984bbe7ee089"/>

Просмотреть файл

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="01ffe82cf088ca8fda9fe6783dc5cad2c3dde01c"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4c0a6d4e8501368db8e5d6029a41db985ef1252a"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="828317e64d28138f24d578ab340c2a0ff8552df0"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f004530b30a63c08a16d82536858600446b2abf5"/>

Просмотреть файл

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="01ffe82cf088ca8fda9fe6783dc5cad2c3dde01c"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4c0a6d4e8501368db8e5d6029a41db985ef1252a"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

Просмотреть файл

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="01ffe82cf088ca8fda9fe6783dc5cad2c3dde01c"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4c0a6d4e8501368db8e5d6029a41db985ef1252a"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

Просмотреть файл

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="01ffe82cf088ca8fda9fe6783dc5cad2c3dde01c"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="4c0a6d4e8501368db8e5d6029a41db985ef1252a"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="27eb2f04e149fc2c9976d881b1b5984bbe7ee089"/>

Просмотреть файл

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="01ffe82cf088ca8fda9fe6783dc5cad2c3dde01c"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4c0a6d4e8501368db8e5d6029a41db985ef1252a"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>

Просмотреть файл

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "01ffe82cf088ca8fda9fe6783dc5cad2c3dde01c",
"git_revision": "4c0a6d4e8501368db8e5d6029a41db985ef1252a",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "d7e928f87e2cc34121db52e65f2eeb7598a01412",
"revision": "61c5a1255e159b89caebf736d3c009a3778a5c42",
"repo_path": "integration/gaia-central"
}

Просмотреть файл

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="01ffe82cf088ca8fda9fe6783dc5cad2c3dde01c"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4c0a6d4e8501368db8e5d6029a41db985ef1252a"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>

Просмотреть файл

@ -18,7 +18,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="01ffe82cf088ca8fda9fe6783dc5cad2c3dde01c"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4c0a6d4e8501368db8e5d6029a41db985ef1252a"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="828317e64d28138f24d578ab340c2a0ff8552df0"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f004530b30a63c08a16d82536858600446b2abf5"/>

Просмотреть файл

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="01ffe82cf088ca8fda9fe6783dc5cad2c3dde01c"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4c0a6d4e8501368db8e5d6029a41db985ef1252a"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>

Просмотреть файл

@ -529,7 +529,8 @@ class Automation(object):
xrePath = None, certPath = None,
debuggerInfo = None, symbolsPath = None,
timeout = -1, maxTime = None, onLaunch = None,
detectShutdownLeaks = False, screenshotOnFail=False, testPath=None, bisectChunk=None):
detectShutdownLeaks = False, screenshotOnFail=False, testPath=None, bisectChunk=None,
valgrindPath=None, valgrindArgs=None, valgrindSuppFiles=None):
"""
Run the app, log the duration it took to execute, return the status code.
Kills the app if it runs for longer than |maxTime| seconds, or outputs nothing for |timeout| seconds.

Просмотреть файл

@ -191,15 +191,15 @@ public class FennecNativeActions implements Actions {
}
public void sendPreferencesGetEvent(int requestId, String[] prefNames) {
GeckoAppShell.sendEventToGecko(GeckoEvent.createPreferencesGetEvent(requestId, prefNames));
PrefsHelper.getPrefsById(requestId, prefNames, /* observe */ false);
}
public void sendPreferencesObserveEvent(int requestId, String[] prefNames) {
GeckoAppShell.sendEventToGecko(GeckoEvent.createPreferencesObserveEvent(requestId, prefNames));
PrefsHelper.getPrefsById(requestId, prefNames, /* observe */ true);
}
public void sendPreferencesRemoveObserversEvent(int requestId) {
GeckoAppShell.sendEventToGecko(GeckoEvent.createPreferencesRemoveObserversEvent(requestId));
PrefsHelper.removePrefsObserver(requestId);
}
class PaintExpecter implements RepeatedEventExpecter {

Просмотреть файл

@ -7641,7 +7641,6 @@ nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
aStatus == NS_ERROR_UNWANTED_URI ||
aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE ||
aStatus == NS_ERROR_REMOTE_XUL ||
aStatus == NS_ERROR_OFFLINE ||
aStatus == NS_ERROR_INTERCEPTION_FAILED ||
aStatus == NS_ERROR_OPAQUE_INTERCEPTION_DISABLED ||
aStatus == NS_ERROR_BAD_OPAQUE_INTERCEPTION_REQUEST_MODE ||

Просмотреть файл

@ -332,6 +332,13 @@ nsSHEntryShared::AttributeWillChange(nsIDocument* aDocument,
{
}
void
nsSHEntryShared::NativeAnonymousChildListChange(nsIDocument* aDocument,
nsIContent* aContent,
bool aIsRemove)
{
}
void
nsSHEntryShared::AttributeChanged(nsIDocument* aDocument,
dom::Element* aElement,

Просмотреть файл

@ -1631,6 +1631,9 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
}
nsNodeUtils::ParentChainChanged(this);
if (!hadParent && IsRootOfNativeAnonymousSubtree()) {
nsNodeUtils::NativeAnonymousChildListChange(this, false);
}
if (HasID()) {
AddToIdTable(DoGetID());
@ -1745,6 +1748,10 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent)
}
}
if (this->IsRootOfNativeAnonymousSubtree()) {
nsNodeUtils::NativeAnonymousChildListChange(this, true);
}
if (GetParent()) {
nsRefPtr<nsINode> p;
p.swap(mParent);

Просмотреть файл

@ -114,6 +114,38 @@ nsMutationReceiver::Disconnect(bool aRemoveFromObserver)
}
}
void
nsMutationReceiver::NativeAnonymousChildListChange(nsIDocument* aDocument,
nsIContent* aContent,
bool aIsRemove) {
if (!NativeAnonymousChildList()) {
return;
}
nsINode* parent = aContent->GetParentNode();
if (!parent ||
(!Subtree() && Target() != parent) ||
(Subtree() && RegisterTarget()->SubtreeRoot() != parent->SubtreeRoot())) {
return;
}
nsDOMMutationRecord* m =
Observer()->CurrentRecord(nsGkAtoms::nativeAnonymousChildList);
if (m->mTarget) {
return;
}
m->mTarget = parent;
if (aIsRemove) {
m->mRemovedNodes = new nsSimpleContentList(parent);
m->mRemovedNodes->AppendElement(aContent);
} else {
m->mAddedNodes = new nsSimpleContentList(parent);
m->mAddedNodes->AppendElement(aContent);
}
}
void
nsMutationReceiver::AttributeWillChange(nsIDocument* aDocument,
mozilla::dom::Element* aElement,
@ -586,6 +618,8 @@ nsDOMMutationObserver::Observe(nsINode& aTarget,
bool attributeOldValue =
aOptions.mAttributeOldValue.WasPassed() &&
aOptions.mAttributeOldValue.Value();
bool nativeAnonymousChildList = aOptions.mNativeAnonymousChildList &&
nsContentUtils::ThreadsafeIsCallerChrome();
bool characterDataOldValue =
aOptions.mCharacterDataOldValue.WasPassed() &&
aOptions.mCharacterDataOldValue.Value();
@ -605,7 +639,8 @@ nsDOMMutationObserver::Observe(nsINode& aTarget,
characterData = true;
}
if (!(childList || attributes || characterData || animations)) {
if (!(childList || attributes || characterData || animations ||
nativeAnonymousChildList)) {
aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
return;
}
@ -655,6 +690,7 @@ nsDOMMutationObserver::Observe(nsINode& aTarget,
r->SetSubtree(subtree);
r->SetAttributeOldValue(attributeOldValue);
r->SetCharacterDataOldValue(characterDataOldValue);
r->SetNativeAnonymousChildList(nativeAnonymousChildList);
r->SetAttributeFilter(filters);
r->SetAllAttributes(allAttrs);
r->SetAnimations(animations);
@ -715,6 +751,7 @@ nsDOMMutationObserver::GetObservingInfo(
info.mSubtree = mr->Subtree();
info.mAttributeOldValue.Construct(mr->AttributeOldValue());
info.mCharacterDataOldValue.Construct(mr->CharacterDataOldValue());
info.mNativeAnonymousChildList = mr->NativeAnonymousChildList();
info.mAnimations.Construct(mr->Animations());
nsCOMArray<nsIAtom>& filters = mr->AttributeFilter();
if (filters.Count()) {

Просмотреть файл

@ -172,6 +172,16 @@ public:
mCharacterDataOldValue = aOldValue;
}
bool NativeAnonymousChildList()
{
return mParent ? mParent->NativeAnonymousChildList() : mNativeAnonymousChildList;
}
void SetNativeAnonymousChildList(bool aOldValue)
{
NS_ASSERTION(!mParent, "Shouldn't have parent");
mNativeAnonymousChildList = aOldValue;
}
bool Attributes() { return mParent ? mParent->Attributes() : mAttributes; }
void SetAttributes(bool aAttributes)
{
@ -298,6 +308,7 @@ private:
bool mChildList;
bool mCharacterData;
bool mCharacterDataOldValue;
bool mNativeAnonymousChildList;
bool mAttributes;
bool mAllAttributes;
bool mAttributeOldValue;
@ -362,6 +373,7 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
NS_DECL_NSIMUTATIONOBSERVER_NATIVEANONYMOUSCHILDLISTCHANGE
NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATAWILLCHANGE
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED

Просмотреть файл

@ -514,6 +514,8 @@ nsGenericDOMDataNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
}
}
bool hadParent = !!GetParentNode();
// Set parent
if (aParent) {
if (!GetParent()) {
@ -548,6 +550,9 @@ nsGenericDOMDataNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
}
nsNodeUtils::ParentChainChanged(this);
if (!hadParent && IsRootOfNativeAnonymousSubtree()) {
nsNodeUtils::NativeAnonymousChildListChange(this, false);
}
UpdateEditableState(false);
@ -570,6 +575,9 @@ nsGenericDOMDataNode::UnbindFromTree(bool aDeep, bool aNullParent)
HasFlag(NODE_FORCE_XBL_BINDINGS) ? OwnerDoc() : GetComposedDoc();
if (aNullParent) {
if (this->IsRootOfNativeAnonymousSubtree()) {
nsNodeUtils::NativeAnonymousChildListChange(this, true);
}
if (GetParent()) {
NS_RELEASE(mParent);
} else {

Просмотреть файл

@ -633,6 +633,7 @@ GK_ATOM(_namespace, "namespace")
GK_ATOM(namespaceAlias, "namespace-alias")
GK_ATOM(namespaceUri, "namespace-uri")
GK_ATOM(NaN, "NaN")
GK_ATOM(nativeAnonymousChildList, "nativeAnonymousChildList")
GK_ATOM(nav, "nav")
GK_ATOM(negate, "negate")
GK_ATOM(never, "never")

Просмотреть файл

@ -22,8 +22,8 @@ class Element;
} // namespace mozilla
#define NS_IMUTATION_OBSERVER_IID \
{ 0xdd74f0cc, 0x2849, 0x4d05, \
{ 0x9c, 0xe3, 0xb0, 0x95, 0x3e, 0xc2, 0xfd, 0x44 } }
{ 0x6d674c17, 0x0fbc, 0x4633, \
{ 0x8f, 0x46, 0x73, 0x4e, 0x87, 0xeb, 0xf0, 0xc7 } }
/**
* Information details about a characterdata change. Basically, we
@ -200,6 +200,18 @@ public:
int32_t aModType,
const nsAttrValue* aOldValue) = 0;
/**
* Notification that the root of a native anonymous has been added
* or removed.
*
* @param aDocument Owner doc of aContent
* @param aContent Anonymous node that's been added or removed
* @param aIsRemove True if it's a removal, false if an addition
*/
virtual void NativeAnonymousChildListChange(nsIDocument* aDocument,
nsIContent* aContent,
bool aIsRemove) {}
/**
* Notification that an attribute of an element has been
* set to the value it already had.
@ -346,6 +358,11 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIMutationObserver, NS_IMUTATION_OBSERVER_IID)
int32_t aModType, \
const nsAttrValue* aNewValue) override;
#define NS_DECL_NSIMUTATIONOBSERVER_NATIVEANONYMOUSCHILDLISTCHANGE \
virtual void NativeAnonymousChildListChange(nsIDocument* aDocument, \
nsIContent* aContent, \
bool aIsRemove) override;
#define NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED \
virtual void AttributeChanged(nsIDocument* aDocument, \
mozilla::dom::Element* aElement, \
@ -383,6 +400,7 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIMutationObserver, NS_IMUTATION_OBSERVER_IID)
NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATAWILLCHANGE \
NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED \
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE \
NS_DECL_NSIMUTATIONOBSERVER_NATIVEANONYMOUSCHILDLISTCHANGE \
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED \
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED \
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED \
@ -419,6 +437,12 @@ _class::AttributeWillChange(nsIDocument* aDocument, \
{ \
} \
void \
_class::NativeAnonymousChildListChange(nsIDocument* aDocument, \
nsIContent* aContent, \
bool aIsRemove) \
{ \
} \
void \
_class::AttributeChanged(nsIDocument* aDocument, \
mozilla::dom::Element* aElement, \
int32_t aNameSpaceID, \

Просмотреть файл

@ -167,6 +167,15 @@ nsNodeUtils::ContentAppended(nsIContent* aContainer,
aNewIndexInContainer));
}
void
nsNodeUtils::NativeAnonymousChildListChange(nsIContent* aContent,
bool aIsRemove)
{
nsIDocument* doc = aContent->OwnerDoc();
IMPL_MUTATION_NOTIFICATION(NativeAnonymousChildListChange, aContent,
(doc, aContent, aIsRemove));
}
void
nsNodeUtils::ContentInserted(nsINode* aContainer,
nsIContent* aChild,

Просмотреть файл

@ -96,6 +96,15 @@ public:
nsIContent* aFirstNewContent,
int32_t aNewIndexInContainer);
/**
* Send NativeAnonymousChildList notifications to nsIMutationObservers
* @param aContent Anonymous node that's been added or removed
* @param aIsRemove True if it's a removal, false if an addition
* @see nsIMutationObserver::NativeAnonymousChildListChange
*/
static void NativeAnonymousChildListChange(nsIContent* aContent,
bool aIsRemove);
/**
* Send ContentInserted notifications to nsIMutationObservers
* @param aContainer Node into which new child was inserted

Просмотреть файл

@ -65,6 +65,7 @@ skip-if = buildapp == 'mulet'
[test_cpows.xul]
skip-if = buildapp == 'mulet'
[test_document_register.xul]
[test_mutationobserver_anonymous.html]
[test_registerElement_content.xul]
[test_registerElement_ep.xul]
[test_domparsing.xul]

Просмотреть файл

@ -0,0 +1,251 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1034110
-->
<head>
<title>Test for Bug 1034110</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1034110">Mozilla Bug 1034110</a>
<style type="text/css">
#pseudo.before::before { content: "before"; }
#pseudo.after::after { content: "after"; }
</style>
<div id="pseudo"></div>
<video id="video"></video>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript;version=1.7">
/** Test for Bug 1034110 **/
SimpleTest.waitForExplicitFinish();
const {Cc, Ci, Cu} = SpecialPowers;
function getWalker(node) {
let walker = Cc["@mozilla.org/inspector/deep-tree-walker;1"].createInstance(Ci.inIDeepTreeWalker);
walker.showAnonymousContent = true;
walker.init(node.ownerDocument, Ci.nsIDOMNodeFilter.SHOW_ALL);
walker.currentNode = node;
return walker;
}
function getFirstChild(parent) {
return SpecialPowers.unwrap(getWalker(parent).firstChild());
}
function getLastChild(parent) {
return SpecialPowers.unwrap(getWalker(parent).lastChild());
}
function assertSamePseudoElement(which, node1, node2) {
is(node1.nodeName, "_moz_generated_content_" + which,
"Correct pseudo element type");
is(node1, node2,
"Referencing the same ::after element");
}
window.onload = function () {
testOneAdded();
};
function testOneAdded() {
let parent = document.getElementById("pseudo");
var m = new MutationObserver(function(records, observer) {
is(records.length, 1, "Correct number of records");
is(records[0].type, "nativeAnonymousChildList", "Correct record type");
is(records[0].target, parent, "Correct target");
is(records[0].addedNodes.length, 1, "Should have got addedNodes");
assertSamePseudoElement("before", records[0].addedNodes[0], getFirstChild(parent));
is(records[0].removedNodes.length, 0, "Shouldn't have got removedNodes");
observer.disconnect();
testAddedAndRemoved();
});
m.observe(parent, { nativeAnonymousChildList: true});
parent.className = "before";
}
function testAddedAndRemoved() {
let parent = document.getElementById("pseudo");
let originalBeforeElement = getFirstChild(parent);
var m = new MutationObserver(function(records, observer) {
is(records.length, 2, "Correct number of records");
is(records[0].type, "nativeAnonymousChildList", "Correct record type (1)");
is(records[1].type, "nativeAnonymousChildList", "Correct record type (2)");
is(records[0].target, parent, "Correct target (1)");
is(records[1].target, parent, "Correct target (2)");
// Two records are sent - one for removed and one for added.
is(records[0].addedNodes.length, 0, "Shouldn't have got addedNodes");
is(records[0].removedNodes.length, 1, "Should have got removedNodes");
assertSamePseudoElement("before", records[0].removedNodes[0], originalBeforeElement);
is(records[1].addedNodes.length, 1, "Should have got addedNodes");
assertSamePseudoElement("after", records[1].addedNodes[0], getLastChild(parent));
is(records[1].removedNodes.length, 0, "Shouldn't have got removedNodes");
observer.disconnect();
testRemoved();
});
m.observe(parent, { nativeAnonymousChildList: true});
parent.className = "after";
}
function testRemoved() {
let parent = document.getElementById("pseudo");
let originalAfterElement = getLastChild(parent);
var m = new MutationObserver(function(records, observer) {
is(records.length, 1, "Correct number of records");
is(records[0].type, "nativeAnonymousChildList", "Correct record type");
is(records[0].target, parent, "Correct target");
is(records[0].addedNodes.length, 0, "Shouldn't have got addedNodes");
is(records[0].removedNodes.length, 1, "Should have got removedNodes");
assertSamePseudoElement("after", records[0].removedNodes[0], originalAfterElement);
observer.disconnect();
testMultipleAdded();
});
m.observe(parent, { nativeAnonymousChildList: true });
parent.className = "";
}
function testMultipleAdded() {
let parent = document.getElementById("pseudo");
var m = new MutationObserver(function(records, observer) {
is(records.length, 2, "Correct number of records");
is(records[0].type, "nativeAnonymousChildList", "Correct record type (1)");
is(records[1].type, "nativeAnonymousChildList", "Correct record type (2)");
is(records[0].target, parent, "Correct target (1)");
is(records[1].target, parent, "Correct target (2)");
is(records[0].addedNodes.length, 1, "Should have got addedNodes");
assertSamePseudoElement("before", records[0].addedNodes[0], getFirstChild(parent));
is(records[0].removedNodes.length, 0, "Shouldn't have got removedNodes");
is(records[1].addedNodes.length, 1, "Should have got addedNodes");
assertSamePseudoElement("after", records[1].addedNodes[0], getLastChild(parent));
is(records[1].removedNodes.length, 0, "Shouldn't have got removedNodes");
observer.disconnect();
testRemovedDueToDisplay();
});
m.observe(parent, { nativeAnonymousChildList: true });
parent.className = "before after";
}
function testRemovedDueToDisplay() {
let parent = document.getElementById("pseudo");
let originalBeforeElement = getFirstChild(parent);
let originalAfterElement = getLastChild(parent);
var m = new MutationObserver(function(records, observer) {
is(records.length, 2, "Correct number of records");
is(records[0].type, "nativeAnonymousChildList", "Correct record type (1)");
is(records[1].type, "nativeAnonymousChildList", "Correct record type (2)");
is(records[0].target, parent, "Correct target (1)");
is(records[1].target, parent, "Correct target (2)");
is(records[0].addedNodes.length, 0, "Shouldn't have got addedNodes");
is(records[0].removedNodes.length, 1, "Should have got removedNodes");
assertSamePseudoElement("before", records[0].removedNodes[0], originalBeforeElement);
is(records[1].addedNodes.length, 0, "Shouldn't have got addedNodes");
is(records[1].removedNodes.length, 1, "Should have got removedNodes");
assertSamePseudoElement("after", records[1].removedNodes[0], originalAfterElement);
observer.disconnect();
testAddedDueToDisplay();
});
m.observe(parent, { nativeAnonymousChildList: true });
parent.style.display = "none";
}
function testAddedDueToDisplay() {
let parent = document.getElementById("pseudo");
var m = new MutationObserver(function(records, observer) {
is(records.length, 2, "Correct number of records");
is(records[0].type, "nativeAnonymousChildList", "Correct record type (1)");
is(records[1].type, "nativeAnonymousChildList", "Correct record type (2)");
is(records[0].target, parent, "Correct target (1)");
is(records[1].target, parent, "Correct target (2)");
is(records[0].addedNodes.length, 1, "Should have got addedNodes");
assertSamePseudoElement("before", records[0].addedNodes[0], getFirstChild(parent));
is(records[0].removedNodes.length, 0, "Shouldn't have got removedNodes");
is(records[1].addedNodes.length, 1, "Should have got addedNodes");
assertSamePseudoElement("after", records[1].addedNodes[0], getLastChild(parent));
is(records[1].removedNodes.length, 0, "Shouldn't have got removedNodes");
observer.disconnect();
testDifferentTargetNoSubtree();
});
m.observe(parent, { nativeAnonymousChildList: true });
parent.style.display = "block";
}
function testDifferentTargetNoSubtree() {
let parent = document.getElementById("pseudo");
var m = new MutationObserver(function(records, observer) {
ok(false,
"No mutation should fire when observing on a parent without subtree option.");
});
m.observe(document, { nativeAnonymousChildList: true });
parent.style.display = "none";
// Wait for the actual mutation to come through, making sure that
// the original observer never fires.
var m2 = new MutationObserver(function(records, observer) {
ok(!getFirstChild(parent), "Pseudo element has been removed, but no mutation");
ok(!getLastChild(parent), "Pseudo element has been removed, but no mutation");
observer.disconnect();
testSubtree();
});
m2.observe(parent, { nativeAnonymousChildList: true });
}
function testSubtree() {
let parent = document.getElementById("pseudo");
var m = new MutationObserver(function(records, observer) {
is(records.length, 2, "Correct number of records");
is(records[0].type, "nativeAnonymousChildList", "Correct record type (1)");
is(records[1].type, "nativeAnonymousChildList", "Correct record type (2)");
is(records[0].target, parent, "Correct target (1)");
is(records[1].target, parent, "Correct target (2)");
is(records[0].addedNodes.length, 1, "Should have got addedNodes");
assertSamePseudoElement("before", records[0].addedNodes[0], getFirstChild(parent));
is(records[0].removedNodes.length, 0, "Shouldn't have got removedNodes");
is(records[1].addedNodes.length, 1, "Should have got addedNodes");
assertSamePseudoElement("after", records[1].addedNodes[0], getLastChild(parent));
is(records[1].removedNodes.length, 0, "Shouldn't have got removedNodes");
observer.disconnect();
SimpleTest.finish();
});
m.observe(document, { nativeAnonymousChildList: true, subtree: true });
parent.style.display = "block";
}
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -909,6 +909,19 @@ function testAttributeRecordMerging4() {
m.disconnect();
div.innerHTML = "";
div.removeAttribute("foo");
then(testChromeOnly);
}
function testChromeOnly() {
// Content can't access nativeAnonymousChildList
try {
var mo = new M(function(records, observer) { });
mo.observe(div, { nativeAnonymousChildList: true });
ok(false, "Should have thrown when trying to observe with chrome-only init");
} catch (e) {
ok(true, "Throws when trying to observe with chrome-only init");
}
then();
}

Просмотреть файл

@ -409,7 +409,7 @@ public:
NS_IMETHOD GetItemId(nsAString& aId) final override {
nsString id;
GetItemId(id);
aId.Assign(aId);
aId.Assign(id);
return NS_OK;
}
NS_IMETHOD SetItemId(const nsAString& aId) final override {

Просмотреть файл

@ -169,6 +169,10 @@ ScreenManagerParent::RecvScreenForBrowser(const TabId& aTabId,
bool
ScreenManagerParent::ExtractScreenDetails(nsIScreen* aScreen, ScreenDetails &aDetails)
{
if (!aScreen) {
return false;
}
uint32_t id;
nsresult rv = aScreen->GetId(&id);
NS_ENSURE_SUCCESS(rv, false);

Просмотреть файл

@ -9,11 +9,11 @@
#include <stdint.h>
#include "MP4Demuxer.h"
#include "mp4_demuxer/Index.h"
#include "mp4_demuxer/MoofParser.h"
#include "mp4_demuxer/MP4Metadata.h"
#include "mp4_demuxer/ResourceStream.h"
#include "mp4_demuxer/BufferStream.h"
#include "mp4_demuxer/Index.h"
// Used for telemetry
#include "mozilla/Telemetry.h"
@ -30,6 +30,51 @@ PRLogModuleInfo* GetDemuxerLog() {
namespace mozilla {
class MP4TrackDemuxer : public MediaTrackDemuxer
{
public:
MP4TrackDemuxer(MP4Demuxer* aParent,
UniquePtr<TrackInfo>&& aInfo,
const nsTArray<mp4_demuxer::Index::Indice>& indices);
virtual UniquePtr<TrackInfo> GetInfo() const override;
virtual nsRefPtr<SeekPromise> Seek(media::TimeUnit aTime) override;
virtual nsRefPtr<SamplesPromise> GetSamples(int32_t aNumSamples = 1) override;
virtual void Reset() override;
virtual nsresult GetNextRandomAccessPoint(media::TimeUnit* aTime) override;
nsRefPtr<SkipAccessPointPromise> SkipToNextRandomAccessPoint(media::TimeUnit aTimeThreshold) override;
virtual media::TimeIntervals GetBuffered() override;
virtual void BreakCycles() override;
private:
friend class MP4Demuxer;
void NotifyDataArrived();
void UpdateSamples(nsTArray<nsRefPtr<MediaRawData>>& aSamples);
void EnsureUpToDateIndex();
void SetNextKeyFrameTime();
nsRefPtr<MP4Demuxer> mParent;
nsRefPtr<mp4_demuxer::ResourceStream> mStream;
UniquePtr<TrackInfo> mInfo;
// We do not actually need a monitor, however MoofParser (in mIndex) will
// assert if a monitor isn't held.
Monitor mMonitor;
nsRefPtr<mp4_demuxer::Index> mIndex;
UniquePtr<mp4_demuxer::SampleIterator> mIterator;
Maybe<media::TimeUnit> mNextKeyframeTime;
// Queued samples extracted by the demuxer, but not yet returned.
nsRefPtr<MediaRawData> mQueuedSample;
bool mNeedReIndex;
bool mNeedSPSForTelemetry;
};
// Returns true if no SPS was found and search for it should continue.
bool
AccumulateSPSTelemetry(const MediaByteBuffer* aExtradata)
@ -120,8 +165,15 @@ MP4Demuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber)
if (mMetadata->GetNumberTracks(aType) <= aTrackNumber) {
return nullptr;
}
nsRefPtr<MP4TrackDemuxer> e =
new MP4TrackDemuxer(this, aType, aTrackNumber);
UniquePtr<TrackInfo> info = mMetadata->GetTrackInfo(aType, aTrackNumber);
if (!info) {
return nullptr;
}
FallibleTArray<mp4_demuxer::Index::Indice> indices;
if (!mMetadata->ReadTrackIndex(indices, info->mTrackId)) {
return nullptr;
}
nsRefPtr<MP4TrackDemuxer> e = new MP4TrackDemuxer(this, Move(info), indices);
mDemuxers.AppendElement(e);
return e.forget();
@ -174,27 +226,20 @@ MP4Demuxer::GetCrypto()
}
MP4TrackDemuxer::MP4TrackDemuxer(MP4Demuxer* aParent,
TrackInfo::TrackType aType,
uint32_t aTrackNumber)
UniquePtr<TrackInfo>&& aInfo,
const nsTArray<mp4_demuxer::Index::Indice>& indices)
: mParent(aParent)
, mStream(new mp4_demuxer::ResourceStream(mParent->mResource))
, mNeedReIndex(true)
, mInfo(Move(aInfo))
, mMonitor("MP4TrackDemuxer")
{
mInfo = mParent->mMetadata->GetTrackInfo(aType, aTrackNumber);
MOZ_ASSERT(mInfo);
FallibleTArray<mp4_demuxer::Index::Indice> indices;
if (!mParent->mMetadata->ReadTrackIndex(indices, mInfo->mTrackId)) {
MOZ_ASSERT(false);
}
mIndex = new mp4_demuxer::Index(indices,
, mIndex(new mp4_demuxer::Index(indices,
mStream,
mInfo->mTrackId,
mInfo->IsAudio(),
&mMonitor);
mIterator = MakeUnique<mp4_demuxer::SampleIterator>(mIndex);
&mMonitor))
, mIterator(MakeUnique<mp4_demuxer::SampleIterator>(mIndex))
, mNeedReIndex(true)
{
EnsureUpToDateIndex(); // Force update of index
// Collect telemetry from h264 AVCC SPS.

Просмотреть файл

@ -13,7 +13,6 @@
#include "MediaResource.h"
namespace mp4_demuxer {
class Index;
class MP4Metadata;
class ResourceStream;
class SampleIterator;
@ -54,51 +53,6 @@ private:
nsTArray<nsRefPtr<MP4TrackDemuxer>> mDemuxers;
};
class MP4TrackDemuxer : public MediaTrackDemuxer
{
public:
MP4TrackDemuxer(MP4Demuxer* aParent,
TrackInfo::TrackType aType,
uint32_t aTrackNumber);
virtual UniquePtr<TrackInfo> GetInfo() const override;
virtual nsRefPtr<SeekPromise> Seek(media::TimeUnit aTime) override;
virtual nsRefPtr<SamplesPromise> GetSamples(int32_t aNumSamples = 1) override;
virtual void Reset() override;
virtual nsresult GetNextRandomAccessPoint(media::TimeUnit* aTime) override;
nsRefPtr<SkipAccessPointPromise> SkipToNextRandomAccessPoint(media::TimeUnit aTimeThreshold) override;
virtual media::TimeIntervals GetBuffered() override;
virtual void BreakCycles() override;
private:
friend class MP4Demuxer;
void NotifyDataArrived();
void UpdateSamples(nsTArray<nsRefPtr<MediaRawData>>& aSamples);
void EnsureUpToDateIndex();
void SetNextKeyFrameTime();
nsRefPtr<MP4Demuxer> mParent;
nsRefPtr<mp4_demuxer::Index> mIndex;
UniquePtr<mp4_demuxer::SampleIterator> mIterator;
UniquePtr<TrackInfo> mInfo;
nsRefPtr<mp4_demuxer::ResourceStream> mStream;
Maybe<media::TimeUnit> mNextKeyframeTime;
// Queued samples extracted by the demuxer, but not yet returned.
nsRefPtr<MediaRawData> mQueuedSample;
bool mNeedReIndex;
bool mNeedSPSForTelemetry;
// We do not actually need a monitor, however MoofParser will assert
// if a monitor isn't held.
Monitor mMonitor;
};
} // namespace mozilla
#endif

Просмотреть файл

@ -172,10 +172,6 @@ skip-if = (toolkit == 'gonk' && !debug) || android_version == '10' || android_ve
[test_stereoPanningWithGain.html]
[test_waveDecoder.html]
[test_waveShaper.html]
skip-if = os == 'win' && debug #Bug 1202564
[test_waveShaperNoCurve.html]
skip-if = os == 'win' && debug #Bug 1202565
[test_waveShaperPassThrough.html]
skip-if = os == 'win' && debug #Bug 1196084
[test_waveShaperInvalidLengthCurve.html]
skip-if = os == 'win' && debug #Bug 1202567

Просмотреть файл

@ -971,7 +971,7 @@ NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char*
NPError err = pluginInstanceInit(instanceData);
if (err != NPERR_NO_ERROR) {
NPN_ReleaseObject(scriptableObject);
free(instanceData);
delete instanceData;
return err;
}

Просмотреть файл

@ -105,7 +105,7 @@ PresentationResponderLoadingCallback::Init(nsIDocShell* aDocShell)
return rv;
}
if ((busyFlags & nsIDocShell::BUSY_FLAGS_NONE) ||
if ((busyFlags == nsIDocShell::BUSY_FLAGS_NONE) ||
(busyFlags & nsIDocShell::BUSY_FLAGS_PAGE_LOADING)) {
// The docshell has finished loading or is receiving data (|STATE_TRANSFERRING|
// has already been fired), so the page is ready for presentation use.

Просмотреть файл

@ -301,15 +301,15 @@ private:
// List of scopes having data, for optimization purposes only
nsTHashtable<nsCStringHashKey> mScopesHavingData;
StatementCache mWorkerStatements;
StatementCache mReaderStatements;
// Connection used by the worker thread for all read and write ops
nsCOMPtr<mozIStorageConnection> mWorkerConnection;
// Connection used only on the main thread for sync read operations
nsCOMPtr<mozIStorageConnection> mReaderConnection;
StatementCache mWorkerStatements;
StatementCache mReaderStatements;
// Time the first pending operation has been added to the pending operations
// list
PRIntervalTime mDirtyEpoch;

Просмотреть файл

@ -61,6 +61,8 @@ dictionary MutationObserverInit {
boolean attributeOldValue;
boolean characterDataOldValue;
// [ChromeOnly]
boolean nativeAnonymousChildList = false;
// [ChromeOnly]
boolean animations;
sequence<DOMString> attributeFilter;
};

Просмотреть файл

@ -2873,7 +2873,7 @@ nsHTMLEditRules::MoveBlock(nsIDOMNode *aLeftBlock, nsIDOMNode *aRightBlock, int3
// GetNodesFromPoint is the workhorse that figures out what we wnat to move.
nsresult res = GetNodesFromPoint(::DOMPoint(aRightBlock,aRightOffset),
EditAction::makeList, arrayOfNodes,
TouchContent::no);
TouchContent::yes);
NS_ENSURE_SUCCESS(res, res);
for (auto& curNode : arrayOfNodes) {
// get the node to act on
@ -5614,6 +5614,27 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode* aNode,
if (mHTMLEditor->IsVisBreak(nextNode->AsDOMNode())) {
break;
}
// Check for newlines in pre-formatted text nodes.
bool isPRE;
mHTMLEditor->IsPreformatted(nextNode->AsDOMNode(), &isPRE);
if (isPRE) {
nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(nextNode);
if (textNode) {
nsAutoString tempString;
textNode->GetData(tempString);
int32_t newlinePos = tempString.FindChar(nsCRT::LF);
if (newlinePos >= 0) {
if ((uint32_t)newlinePos + 1 == tempString.Length()) {
// No need for special processing if the newline is at the end.
break;
}
*outNode = nextNode->AsDOMNode();
*outOffset = newlinePos + 1;
return;
}
}
}
NS_ENSURE_TRUE(mHTMLEditor, /* void */);
nextNode = mHTMLEditor->GetNextHTMLNode(node, offset, true);
}
@ -5781,6 +5802,38 @@ nsHTMLEditRules::GetNodesForOperation(nsTArray<nsRefPtr<nsRange>>& aArrayOfRange
int32_t rangeCount = aArrayOfRanges.Length();
nsresult res = NS_OK;
if (aTouchContent == TouchContent::yes) {
// Split text nodes. This is necessary, since GetPromotedPoint() may return a
// range ending in a text node in case where part of a pre-formatted
// elements needs to be moved.
for (int32_t i = 0; i < rangeCount; i++) {
nsRefPtr<nsRange> r = aArrayOfRanges[i];
nsCOMPtr<nsIContent> endParent = do_QueryInterface(r->GetEndParent());
if (!mHTMLEditor->IsTextNode(endParent)) {
continue;
}
nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(endParent);
if (textNode) {
int32_t offset = r->EndOffset();
nsAutoString tempString;
textNode->GetData(tempString);
if (0 < offset && offset < (int32_t)(tempString.Length())) {
// Split the text node.
nsCOMPtr<nsIDOMNode> tempNode;
res = mHTMLEditor->SplitNode(endParent->AsDOMNode(), offset,
getter_AddRefs(tempNode));
NS_ENSURE_SUCCESS(res, res);
// Correct the range.
// The new end parent becomes the parent node of the text.
nsCOMPtr<nsIContent> newParent = endParent->GetParent();
r->SetEnd(newParent, newParent->IndexOf(endParent));
}
}
}
}
// Bust up any inlines that cross our range endpoints, but only if we are
// allowed to touch content.

Просмотреть файл

@ -130,6 +130,7 @@ skip-if = toolkit == 'android' || e10s
[test_bug757371.html]
[test_bug757771.html]
[test_bug767684.html]
[test_bug772796.html]
[test_bug773262.html]
[test_bug780035.html]
[test_bug787432.html]

Просмотреть файл

@ -0,0 +1,223 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=772796
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 772796</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<style> .pre { white-space: pre } </style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=772796">Mozilla Bug 772796</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<div id="editable" contenteditable></div>
<pre id="test">
<script type="application/javascript">
var tests = [
/*00*/[ "<div>test</div><pre>foobar\nbaz</pre>", "<div>testfoobar\n</div><pre>baz</pre>" ],
/*01*/[ "<div>test</div><pre><b>foobar\nbaz</b></pre>", "<div>test<b>foobar\n</b></div><pre><b>baz</b></pre>" ],
/*02*/[ "<div>test</div><pre><b>foo</b>bar\nbaz</pre>", "<div>test<b>foo</b>bar\n</div><pre>baz</pre>" ],
/*03*/[ "<div>test</div><pre><b>foo</b>\nbar</pre>", "<div>test<b>foo</b>\n</div><pre>bar</pre>" ],
/*04*/[ "<div>test</div><pre><b>foo\n</b>bar\nbaz</pre>", "<div>test<b>foo\n</b></div><pre>bar\nbaz</pre>" ],
/* The <br> after the foobar is unfortunate but is behaviour that hasn't changed in bug 772796. */
/*05*/[ "<div>test</div><pre>foobar<br>baz</pre>", "<div>testfoobar<br></div><pre>baz</pre>" ],
/*06*/[ "<div>test</div><pre><b>foobar<br>baz</b></pre>", "<div>test<b>foobar</b><br></div><pre><b>baz</b></pre>" ],
/*
* Some tests with block elements.
* Tests 07, 09 and 11 don't use "MoveBlock", they use "JoinNodesSmart".
* Test 11 is a pain: <div>foo\bar</div> is be joined to "test", losing the visible line break.
*/
/*07*/[ "<div>test</div><pre><div>foobar</div>baz</pre>", "<div>testfoobar</div><pre>baz</pre>" ],
/*08*/[ "<div>test</div><pre>foobar<div>baz</div></pre>", "<div>testfoobar</div><pre><div>baz</div></pre>" ],
/*09*/[ "<div>test</div><pre><div>foobar</div>baz\nfred</pre>", "<div>testfoobar</div><pre>baz\nfred</pre>" ],
/*10*/[ "<div>test</div><pre>foobar<div>baz</div>\nfred</pre>", "<div>testfoobar</div><pre><div>baz</div>\nfred</pre>" ],
/*11*/[ "<div>test</div><pre><div>foo\nbar</div>baz\nfred</pre>", "<div>testfoo\nbar</div><pre>baz\nfred</pre>" ], // BAD
/*12*/[ "<div>test</div><pre>foo<div>bar</div>baz\nfred</pre>", "<div>testfoo</div><pre><div>bar</div>baz\nfred</pre>" ],
/*
* Repeating all tests above with the <pre> on a new line.
* We know that backspace doesn't work (bug 1190161). Third argument shows the current outcome.
*/
/*13-00*/[ "<div>test</div>\n<pre>foobar\nbaz</pre>", "<div>testfoobar\n</div><pre>baz</pre>",
"<div>test</div>foobar\n<pre>baz</pre>" ],
/*14-01*/[ "<div>test</div>\n<pre><b>foobar\nbaz</b></pre>", "<div>test<b>foobar\n</b></div><pre><b>baz</b></pre>",
"<div>test</div><b>foobar\n</b><pre><b>baz</b></pre>" ],
/*15-02*/[ "<div>test</div>\n<pre><b>foo</b>bar\nbaz</pre>", "<div>test<b>foo</b>bar\n</div><pre>baz</pre>",
"<div>test</div><b>foo</b>bar\n<pre>baz</pre>" ],
/*16-03*/[ "<div>test</div>\n<pre><b>foo</b>\nbar</pre>", "<div>test<b>foo</b>\n</div><pre>bar</pre>",
"<div>test</div><b>foo</b>\n<pre>bar</pre>" ],
/*17-04*/[ "<div>test</div>\n<pre><b>foo\n</b>bar\nbaz</pre>", "<div>test<b>foo\n</b></div><pre>bar\nbaz</pre>",
"<div>test</div><b>foo\n</b><pre>bar\nbaz</pre>" ],
/*18-05*/[ "<div>test</div>\n<pre>foobar<br>baz</pre>", "<div>testfoobar<br></div><pre>baz</pre>",
"<div>test</div>foobar<br><pre>baz</pre>" ],
/*19-06*/[ "<div>test</div>\n<pre><b>foobar<br>baz</b></pre>", "<div>test<b>foobar</b><br></div><pre><b>baz</b></pre>",
"<div>test</div><b>foobar</b><br><pre><b>baz</b></pre>" ],
/*20-07*/[ "<div>test</div>\n<pre><div>foobar</div>baz</pre>", "<div>testfoobar</div><pre>baz</pre>",
"<div>test</div>foobar<pre>baz</pre>" ],
/*21-08*/[ "<div>test</div>\n<pre>foobar<div>baz</div></pre>", "<div>testfoobar</div><pre><div>baz</div></pre>",
"<div>test</div>foobar<pre><div>baz</div></pre>" ],
/*22-09*/[ "<div>test</div>\n<pre><div>foobar</div>baz\nfred</pre>", "<div>testfoobar</div><pre>baz\nfred</pre>",
"<div>test</div>foobar<pre>baz\nfred</pre>" ],
/*23-10*/[ "<div>test</div>\n<pre>foobar<div>baz</div>\nfred</pre>", "<div>testfoobar</div><pre><div>baz</div>\nfred</pre>",
"<div>test</div>foobar<pre><div>baz</div>\nfred</pre>" ],
/*24-11*/[ "<div>test</div>\n<pre><div>foo\nbar</div>baz\nfred</pre>", "<div>testfoo\nbar</div><pre>baz\nfred</pre>", // BAD
"<div>test</div>foo\n<pre><div>bar</div>baz\nfred</pre>" ],
/*25-12*/[ "<div>test</div>\n<pre>foo<div>bar</div>baz\nfred</pre>", "<div>testfoo</div><pre><div>bar</div>baz\nfred</pre>",
"<div>test</div>foo<pre><div>bar</div>baz\nfred</pre>" ],
/* Some tests without <div>. These exercise the MoveBlock "right in left" */
/*26-00*/[ "test<pre>foobar\nbaz</pre>", "testfoobar\n<pre>baz</pre>" ],
/*27-01*/[ "test<pre><b>foobar\nbaz</b></pre>", "test<b>foobar\n</b><pre><b>baz</b></pre>" ],
/*28-02*/[ "test<pre><b>foo</b>bar\nbaz</pre>", "test<b>foo</b>bar\n<pre>baz</pre>" ],
/*29-03*/[ "test<pre><b>foo</b>\nbar</pre>", "test<b>foo</b>\n<pre>bar</pre>" ],
/*30-04*/[ "test<pre><b>foo\n</b>bar\nbaz</pre>", "test<b>foo\n</b><pre>bar\nbaz</pre>" ],
/*31-05*/[ "test<pre>foobar<br>baz</pre>", "testfoobar<br><pre>baz</pre>" ],
/*32-06*/[ "test<pre><b>foobar<br>baz</b></pre>", "test<b>foobar</b><br><pre><b>baz</b></pre>" ],
/*33-07*/[ "test<pre><div>foobar</div>baz</pre>", "testfoobar<pre>baz</pre>" ],
/*34-08*/[ "test<pre>foobar<div>baz</div></pre>", "testfoobar<pre><div>baz</div></pre>" ],
/*35-09*/[ "test<pre><div>foobar</div>baz\nfred</pre>", "testfoobar<pre>baz\nfred</pre>" ],
/*36-10*/[ "test<pre>foobar<div>baz</div>\nfred</pre>", "testfoobar<pre><div>baz</div>\nfred</pre>" ],
/*37-11*/[ "test<pre><div>foo\nbar</div>baz\nfred</pre>", "testfoo\n<pre><div>bar</div>baz\nfred</pre>" ],
/*38-12*/[ "test<pre>foo<div>bar</div>baz\nfred</pre>", "testfoo<pre><div>bar</div>baz\nfred</pre>" ],
/*
* Some tests with <span class="pre">. Again 07, 09 and 11 use "JoinNodesSmart".
* All these exercise MoveBlock "left in right". The "right" is the surrounding "contenteditable" div.
*/
/*39-00*/[ "<div>test</div><span class=\"pre\">foobar\nbaz</span>", "<div>test<span class=\"pre\">foobar\n</span></div><span class=\"pre\">baz</span>" ],
/*40-01*/[ "<div>test</div><span class=\"pre\"><b>foobar\nbaz</b></span>", "<div>test<span class=\"pre\"><b>foobar\n</b></span></div><span class=\"pre\"><b>baz</b></span>" ],
/*41-02*/[ "<div>test</div><span class=\"pre\"><b>foo</b>bar\nbaz</span>", "<div>test<span class=\"pre\"><b>foo</b>bar\n</span></div><span class=\"pre\">baz</span>" ],
/*42-03*/[ "<div>test</div><span class=\"pre\"><b>foo</b>\nbar</span>", "<div>test<span class=\"pre\"><b>foo</b>\n</span></div><span class=\"pre\">bar</span>" ],
/*43-04*/[ "<div>test</div><span class=\"pre\"><b>foo\n</b>bar\nbaz</span>", "<div>test<span class=\"pre\"><b>foo\n</b></span></div><span class=\"pre\">bar\nbaz</span>" ],
/*44-05*/[ "<div>test</div><span class=\"pre\">foobar<br>baz</span>", "<div>test<span class=\"pre\">foobar</span><br><span class=\"pre\"></span></div><span class=\"pre\">baz</span>" ],
/*45-06*/[ "<div>test</div><span class=\"pre\"><b>foobar<br>baz</b></span>", "<div>test<span class=\"pre\"><b>foobar</b></span><br><span class=\"pre\"></span></div><span class=\"pre\"><b>baz</b></span>" ],
/*46-07*/[ "<div>test</div><span class=\"pre\"><div>foobar</div>baz</span>", "<div>testfoobar</div><span class=\"pre\">baz</span>" ],
/*47-08*/[ "<div>test</div><span class=\"pre\">foobar<div>baz</div></span>", "<div>test<span class=\"pre\">foobar</span></div><span class=\"pre\"><div>baz</div></span>" ],
/*48-09*/[ "<div>test</div><span class=\"pre\"><div>foobar</div>baz\nfred</span>", "<div>testfoobar</div><span class=\"pre\">baz\nfred</span>" ],
/*49-10*/[ "<div>test</div><span class=\"pre\">foobar<div>baz</div>\nfred</span>", "<div>test<span class=\"pre\">foobar</span></div><span class=\"pre\"><div>baz</div>\nfred</span>" ],
/*50-11*/[ "<div>test</div><span class=\"pre\"><div>foo\nbar</div>baz\nfred</span>", "<div>testfoo\nbar</div><span class=\"pre\">baz\nfred</span>" ], // BAD
/*51-12*/[ "<div>test</div><span class=\"pre\">foo<div>bar</div>baz\nfred</span>", "<div>test<span class=\"pre\">foo</span></div><span class=\"pre\"><div>bar</div>baz\nfred</span>" ],
/* Some tests with <div class="pre">. */
/*
* The results are pretty ugly, since joining two <divs> sadly carrys the properties of the right to the left,
* but not in all cases: 07, 09, 11 are actually right. All cases use "JoinNodesSmart".
* Here we merely document the ugly behaviour. See bug 1191875 for more information.
*/
/*52-00*/[ "<div>test</div><div class=\"pre\">foobar\nbaz</div>", "<div class=\"pre\">testfoobar\nbaz</div>" ],
/*53-01*/[ "<div>test</div><div class=\"pre\"><b>foobar\nbaz</b></div>", "<div class=\"pre\">test<b>foobar\nbaz</b></div>" ],
/*54-02*/[ "<div>test</div><div class=\"pre\"><b>foo</b>bar\nbaz</div>", "<div class=\"pre\">test<b>foo</b>bar\nbaz</div>" ],
/*55-03*/[ "<div>test</div><div class=\"pre\"><b>foo</b>\nbar</div>", "<div class=\"pre\">test<b>foo</b>\nbar</div>" ],
/*56-04*/[ "<div>test</div><div class=\"pre\"><b>foo\n</b>bar\nbaz</div>", "<div class=\"pre\">test<b>foo\n</b>bar\nbaz</div>" ],
/*57-05*/[ "<div>test</div><div class=\"pre\">foobar<br>baz</div>", "<div class=\"pre\">testfoobar<br>baz</div>" ],
/*58-06*/[ "<div>test</div><div class=\"pre\"><b>foobar<br>baz</b></div>", "<div class=\"pre\">test<b>foobar<br>baz</b></div>" ],
/*59-07*/[ "<div>test</div><div class=\"pre\"><div>foobar</div>baz</div>", "<div>testfoobar</div><div class=\"pre\">baz</div>" ],
/*60-08*/[ "<div>test</div><div class=\"pre\">foobar<div>baz</div></div>", "<div class=\"pre\">testfoobar<div>baz</div></div>" ],
/*61-09*/[ "<div>test</div><div class=\"pre\"><div>foobar</div>baz\nfred</div>", "<div>testfoobar</div><div class=\"pre\">baz\nfred</div>" ],
/*62-10*/[ "<div>test</div><div class=\"pre\">foobar<div>baz</div>\nfred</div>", "<div class=\"pre\">testfoobar<div>baz</div>\nfred</div>" ],
/*63-11*/[ "<div>test</div><div class=\"pre\"><div>foo\nbar</div>baz\nfred</div>", "<div>testfoo\nbar</div><div class=\"pre\">baz\nfred</div>" ], // BAD
/*64-12*/[ "<div>test</div><div class=\"pre\">foo<div>bar</div>baz\nfred</div>", "<div class=\"pre\">testfoo<div>bar</div>baz\nfred</div>" ],
/* Some tests with lists. These exercise the MoveBlock "left in right". */
/*65*/[ "<ul><pre><li>test</li>foobar\nbaz</pre></ul>", "<ul><pre><li>testfoobar\n</li>baz</pre></ul>" ],
/*66*/[ "<ul><pre><li>test</li><b>foobar\nbaz</b></pre></ul>", "<ul><pre><li>test<b>foobar\n</b></li><b>baz</b></pre></ul>" ],
/*67*/[ "<ul><pre><li>test</li><b>foo</b>bar\nbaz</pre></ul>", "<ul><pre><li>test<b>foo</b>bar\n</li>baz</pre></ul>" ],
/*68*/[ "<ul><pre><li>test</li><b>foo</b>\nbar</pre></ul>", "<ul><pre><li>test<b>foo</b>\n</li>bar</pre></ul>" ],
/*69*/[ "<ul><pre><li>test</li><b>foo\n</b>bar\nbaz</pre></ul>", "<ul><pre><li>test<b>foo\n</b></li>bar\nbaz</pre></ul>" ],
/* Last not least, some simple edge case tests. */
/*70*/[ "<div>test</div><pre>foobar\n</pre>baz", "<div>testfoobar\n</div>baz" ],
/*71*/[ "<div>test</div><pre>\nfoo\nbar</pre>", "<div>testfoo\n</div><pre>bar</pre>" ],
/*72*/[ "<div>test</div><pre>\n\nfoo\nbar</pre>", "<div>test\n</div><pre>foo\nbar</pre>" ],
];
/** Test for Bug 772796 **/
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
var sel = window.getSelection();
var theEdit = document.getElementById("editable");
var testName;
var theDiv;
for (i = 0; i < tests.length; i++) {
testName = "test" + i.toString();
dump (testName+"\n");
dump (tests[i][0]+"\n");
/* Set up the selection. */
theEdit.innerHTML = "<div id=\"" + testName + "\">" + tests[i][0] + "</div>";
theDiv = document.getElementById(testName);
theDiv.focus();
sel.collapse(theDiv, 0);
synthesizeMouse(theDiv, 100, 2, {}); /* click behind and down */
/** First round: Forward delete. **/
synthesizeKey("VK_DELETE", {});
is(theDiv.innerHTML, tests[i][1], "delete(collapsed): inner HTML for " + testName);
/* Set up the selection. */
theEdit.innerHTML = "<div id=\"" + testName + "\">" + tests[i][0] + "</div>";
theDiv = document.getElementById(testName);
theDiv.focus();
sel.collapse(theDiv, 0);
synthesizeMouse(theDiv, 100, 2, {}); /* click behind and down */
/** Second round: Backspace. **/
synthesizeKey("VK_RIGHT", {});
synthesizeKey("VK_BACK_SPACE", {});
if (tests[i].length == 2) {
is(theDiv.innerHTML, tests[i][1], "backspace: inner HTML for " + testName);
} else {
todo_is(theDiv.innerHTML, tests[i][1], "backspace(should be): inner HTML for " + testName);
is(theDiv.innerHTML, tests[i][2], "backspace(currently is): inner HTML for " + testName);
}
/* Set up the selection. */
theEdit.innerHTML = "<div id=\"" + testName + "\">" + tests[i][0] + "</div>";
theDiv = document.getElementById(testName);
theDiv.focus();
sel.collapse(theDiv, 0);
synthesizeMouse(theDiv, 100, 2, {}); /* click behind and down */
/** Third round: Delete with non-collapsed selection. **/
if (i == 72) {
// This test doesn't work, since we can't select only one newline using the right arrow key.
continue;
}
synthesizeKey("VK_LEFT", {});
/* Strangely enough we need to hit "right arrow" three times to select two characters. */
synthesizeKey("VK_RIGHT", {shiftKey:true});
synthesizeKey("VK_RIGHT", {shiftKey:true});
synthesizeKey("VK_RIGHT", {shiftKey:true});
synthesizeKey("VK_DELETE", {});
/* We always expect to the delete the "tf" in "testfoo". */
var expected = tests[i][1].replace("testfoo", "tesoo")
.replace("test<b>foo", "tes<b>oo")
.replace("test<span class=\"pre\">foo", "tes<span class=\"pre\">oo")
.replace("test<span class=\"pre\"><b>foo", "tes<span class=\"pre\"><b>oo");
is(theDiv.innerHTML, expected, "delete(non-collapsed): inner HTML for " + testName);
}
SimpleTest.finish();
});
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -16,6 +16,7 @@
#include "nsContentUtils.h"
#include "nsIScriptSecurityManager.h"
#include "nsJSPrincipals.h"
#include "nsIScriptError.h"
#include "jswrapper.h"
extern PRLogModuleInfo *MCD;
@ -106,7 +107,18 @@ nsresult EvaluateAdminConfigScript(const char *js_buffer, size_t length,
nsAutoCString script(js_buffer, length);
JS::RootedValue v(cx);
rv = xpc->EvalInSandboxObject(NS_ConvertUTF8toUTF16(script), filename, cx,
nsString convertedScript = NS_ConvertUTF8toUTF16(script);
if (convertedScript.Length() == 0) {
nsContentUtils::ReportToConsoleNonLocalized(
NS_LITERAL_STRING("Your AutoConfig file is ASCII. Please convert it to UTF-8."),
nsIScriptError::warningFlag,
NS_LITERAL_CSTRING("autoconfig"),
nullptr);
/* If the length is 0, the conversion failed. Fallback to ASCII */
convertedScript = NS_ConvertASCIItoUTF16(script);
}
rv = xpc->EvalInSandboxObject(convertedScript, filename, cx,
autoconfigSb, JSVERSION_LATEST, &v);
NS_ENSURE_SUCCESS(rv, rv);

Просмотреть файл

@ -7,7 +7,6 @@
include('/ipc/chromium/chromium-config.mozbuild')
SOURCES += [
'mozEnglishWordUtils.cpp',
'mozGenericWordUtils.cpp',
'mozInlineSpellChecker.cpp',
'mozInlineSpellWordUtil.cpp',
'mozPersonalDictionary.cpp',

Просмотреть файл

@ -1,40 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozGenericWordUtils.h"
NS_IMPL_ISUPPORTS(mozGenericWordUtils, mozISpellI18NUtil)
// do something sensible but generic ... eventually. For now whine.
mozGenericWordUtils::mozGenericWordUtils()
{
/* member initializers and constructor code */
}
mozGenericWordUtils::~mozGenericWordUtils()
{
/* destructor code */
}
NS_IMETHODIMP mozGenericWordUtils::GetLanguage(char16_t * *aLanguage)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP mozGenericWordUtils::GetRootForm(const char16_t *word, uint32_t type, char16_t ***words, uint32_t *count)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP mozGenericWordUtils::FromRootForm(const char16_t *word, const char16_t **iwords, uint32_t icount, char16_t ***owords, uint32_t *ocount)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP mozGenericWordUtils::FindNextWord(const char16_t *word, uint32_t length, uint32_t offset, int32_t *begin, int32_t *end)
{
return NS_ERROR_NOT_IMPLEMENTED;
}

Просмотреть файл

@ -1,24 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozGenericWordUtils_h__
#define mozGenericWordUtils_h__
#include "nsCOMPtr.h"
#include "mozISpellI18NUtil.h"
#include "nsCycleCollectionParticipant.h"
class mozGenericWordUtils : public mozISpellI18NUtil
{
protected:
virtual ~mozGenericWordUtils();
public:
NS_DECL_ISUPPORTS
NS_DECL_MOZISPELLI18NUTIL
mozGenericWordUtils();
};
#endif

Просмотреть файл

@ -5,36 +5,28 @@
#include "mozSpellI18NManager.h"
#include "mozEnglishWordUtils.h"
#include "mozGenericWordUtils.h"
#include "nsString.h"
#include "mozilla/nsRefPtr.h"
NS_IMPL_ISUPPORTS(mozSpellI18NManager, mozISpellI18NManager)
mozSpellI18NManager::mozSpellI18NManager()
{
/* member initializers and constructor code */
}
mozSpellI18NManager::~mozSpellI18NManager()
{
/* destructor code */
}
NS_IMETHODIMP mozSpellI18NManager::GetUtil(const char16_t *aLanguage, mozISpellI18NUtil **_retval)
{
if( nullptr == _retval) {
if (!_retval) {
return NS_ERROR_NULL_POINTER;
}
*_retval = nullptr;
nsAutoString lang;
lang.Assign(aLanguage);
if(lang.EqualsLiteral("en")){
*_retval = new mozEnglishWordUtils;
}
else{
*_retval = new mozEnglishWordUtils;
}
NS_IF_ADDREF(*_retval);
// XXX TODO Actually handle multiple languages.
nsRefPtr<mozEnglishWordUtils> utils = new mozEnglishWordUtils;
utils.forget(_retval);
return NS_OK;
}

80
gfx/2d/CriticalSection.h Normal file
Просмотреть файл

@ -0,0 +1,80 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_GFX_CRITICALSECTION_H_
#define MOZILLA_GFX_CRITICALSECTION_H_
#ifdef WIN32
#include <windows.h>
#else
#include <pthread.h>
#include "mozilla/DebugOnly.h"
#endif
namespace mozilla {
namespace gfx {
#ifdef WIN32
class CriticalSection {
public:
CriticalSection() { ::InitializeCriticalSection(&mCriticalSection); }
~CriticalSection() { ::DeleteCriticalSection(&mCriticalSection); }
void Enter() { ::EnterCriticalSection(&mCriticalSection); }
void Leave() { ::LeaveCriticalSection(&mCriticalSection); }
protected:
CRITICAL_SECTION mCriticalSection;
};
#else
// posix
class PosixCondvar;
class CriticalSection {
public:
CriticalSection() {
DebugOnly<int> err = pthread_mutex_init(&mMutex, nullptr);
MOZ_ASSERT(!err);
}
~CriticalSection() {
DebugOnly<int> err = pthread_mutex_destroy(&mMutex);
MOZ_ASSERT(!err);
}
void Enter() {
DebugOnly<int> err = pthread_mutex_lock(&mMutex);
MOZ_ASSERT(!err);
}
void Leave() {
DebugOnly<int> err = pthread_mutex_unlock(&mMutex);
MOZ_ASSERT(!err);
}
protected:
pthread_mutex_t mMutex;
friend class PosixCondVar;
};
#endif
/// RAII helper.
struct CriticalSectionAutoEnter {
explicit CriticalSectionAutoEnter(CriticalSection* aSection) : mSection(aSection) { mSection->Enter(); }
~CriticalSectionAutoEnter() { mSection->Leave(); }
protected:
CriticalSection* mSection;
};
} // namespace
} // namespace
#endif

Просмотреть файл

@ -6,6 +6,10 @@
#ifndef MOZILLA_GFX_DRAWCOMMAND_H_
#define MOZILLA_GFX_DRAWCOMMAND_H_
#define _USE_MATH_DEFINES
#include <math.h>
#include "2D.h"
#include "Filters.h"
#include <vector>
@ -31,7 +35,8 @@ enum class CommandType : int8_t {
PUSHCLIP,
PUSHCLIPRECT,
POPCLIP,
SETTRANSFORM
SETTRANSFORM,
FLUSH
};
class DrawingCommand
@ -39,7 +44,9 @@ class DrawingCommand
public:
virtual ~DrawingCommand() {}
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix& aTransform) = 0;
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix* aTransform = nullptr) const = 0;
virtual bool GetAffectedRect(Rect& aDeviceRect, const Matrix& aTransform) const { return false; }
protected:
explicit DrawingCommand(CommandType aType)
@ -130,7 +137,7 @@ public:
{
}
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
{
aDT->DrawSurface(mSurface, mDest, mSource, mSurfOptions, mOptions);
}
@ -154,7 +161,7 @@ public:
{
}
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
{
aDT->DrawFilter(mFilter, mSourceRect, mDestPoint, mOptions);
}
@ -175,7 +182,7 @@ public:
{
}
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
{
aDT->ClearRect(mRect);
}
@ -197,11 +204,13 @@ public:
{
}
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix& aTransform)
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix* aTransform) const
{
MOZ_ASSERT(!aTransform.HasNonIntegerTranslation());
MOZ_ASSERT(!aTransform || !aTransform->HasNonIntegerTranslation());
Point dest(Float(mDestination.x), Float(mDestination.y));
dest = aTransform * dest;
if (aTransform) {
dest = (*aTransform) * dest;
}
aDT->CopySurface(mSurface, mSourceRect, IntPoint(uint32_t(dest.x), uint32_t(dest.y)));
}
@ -224,11 +233,17 @@ public:
{
}
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
{
aDT->FillRect(mRect, mPattern, mOptions);
}
bool GetAffectedRect(Rect& aDeviceRect, const Matrix& aTransform) const
{
aDeviceRect = aTransform.TransformBounds(mRect);
return true;
}
private:
Rect mRect;
StoredPattern mPattern;
@ -248,9 +263,14 @@ public:
, mStrokeOptions(aStrokeOptions)
, mOptions(aOptions)
{
if (aStrokeOptions.mDashLength) {
mDashes.resize(aStrokeOptions.mDashLength);
mStrokeOptions.mDashPattern = &mDashes.front();
memcpy(&mDashes.front(), aStrokeOptions.mDashPattern, mStrokeOptions.mDashLength * sizeof(Float));
}
}
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
{
aDT->StrokeRect(mRect, mPattern, mStrokeOptions, mOptions);
}
@ -260,6 +280,7 @@ private:
StoredPattern mPattern;
StrokeOptions mStrokeOptions;
DrawOptions mOptions;
std::vector<Float> mDashes;
};
class StrokeLineCommand : public DrawingCommand
@ -279,7 +300,7 @@ public:
{
}
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
{
aDT->StrokeLine(mStart, mEnd, mPattern, mStrokeOptions, mOptions);
}
@ -305,17 +326,58 @@ public:
{
}
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
{
aDT->Fill(mPath, mPattern, mOptions);
}
bool GetAffectedRect(Rect& aDeviceRect, const Matrix& aTransform) const
{
aDeviceRect = mPath->GetBounds(aTransform);
return true;
}
private:
RefPtr<Path> mPath;
StoredPattern mPattern;
DrawOptions mOptions;
};
#ifndef M_SQRT2
#define M_SQRT2 1.41421356237309504880
#endif
#ifndef M_SQRT1_2
#define M_SQRT1_2 0.707106781186547524400844362104849039
#endif
// The logic for this comes from _cairo_stroke_style_max_distance_from_path
static Rect
PathExtentsToMaxStrokeExtents(const StrokeOptions &aStrokeOptions,
const Rect &aRect,
const Matrix &aTransform)
{
double styleExpansionFactor = 0.5f;
if (aStrokeOptions.mLineCap == CapStyle::SQUARE) {
styleExpansionFactor = M_SQRT1_2;
}
if (aStrokeOptions.mLineJoin == JoinStyle::MITER &&
styleExpansionFactor < M_SQRT2 * aStrokeOptions.mMiterLimit) {
styleExpansionFactor = M_SQRT2 * aStrokeOptions.mMiterLimit;
}
styleExpansionFactor *= aStrokeOptions.mLineWidth;
double dx = styleExpansionFactor * hypot(aTransform._11, aTransform._21);
double dy = styleExpansionFactor * hypot(aTransform._22, aTransform._12);
Rect result = aRect;
result.Inflate(dx, dy);
return result;
}
class StrokeCommand : public DrawingCommand
{
public:
@ -329,18 +391,30 @@ public:
, mStrokeOptions(aStrokeOptions)
, mOptions(aOptions)
{
if (aStrokeOptions.mDashLength) {
mDashes.resize(aStrokeOptions.mDashLength);
mStrokeOptions.mDashPattern = &mDashes.front();
memcpy(&mDashes.front(), aStrokeOptions.mDashPattern, mStrokeOptions.mDashLength * sizeof(Float));
}
}
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
{
aDT->Stroke(mPath, mPattern, mStrokeOptions, mOptions);
}
bool GetAffectedRect(Rect& aDeviceRect, const Matrix& aTransform) const
{
aDeviceRect = PathExtentsToMaxStrokeExtents(mStrokeOptions, mPath->GetBounds(aTransform), aTransform);
return true;
}
private:
RefPtr<Path> mPath;
StoredPattern mPattern;
StrokeOptions mStrokeOptions;
DrawOptions mOptions;
std::vector<Float> mDashes;
};
class FillGlyphsCommand : public DrawingCommand
@ -361,7 +435,7 @@ public:
memcpy(&mGlyphs.front(), aBuffer.mGlyphs, sizeof(Glyph) * aBuffer.mNumGlyphs);
}
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
{
GlyphBuffer buf;
buf.mNumGlyphs = mGlyphs.size();
@ -390,7 +464,7 @@ public:
{
}
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
{
aDT->Mask(mSource, mMask, mOptions);
}
@ -416,7 +490,7 @@ public:
{
}
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
{
aDT->MaskSurface(mSource, mMask, mOffset, mOptions);
}
@ -437,7 +511,7 @@ public:
{
}
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
{
aDT->PushClip(mPath);
}
@ -455,7 +529,7 @@ public:
{
}
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
{
aDT->PushClipRect(mRect);
}
@ -472,7 +546,7 @@ public:
{
}
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&)
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
{
aDT->PopClip();
}
@ -487,17 +561,33 @@ public:
{
}
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix& aMatrix)
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix* aMatrix) const
{
Matrix transform = mTransform;
transform *= aMatrix;
aDT->SetTransform(transform);
if (aMatrix) {
aDT->SetTransform(mTransform * (*aMatrix));
} else {
aDT->SetTransform(mTransform);
}
}
private:
Matrix mTransform;
};
class FlushCommand : public DrawingCommand
{
public:
explicit FlushCommand()
: DrawingCommand(CommandType::FLUSH)
{
}
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
{
aDT->Flush();
}
};
} // namespace gfx
} // namespace mozilla

Просмотреть файл

@ -188,7 +188,7 @@ DrawTargetCaptureImpl::ReplayToDrawTarget(DrawTarget* aDT, const Matrix& aTransf
uint8_t* current = start;
while (current < start + mDrawCommandStorage.size()) {
reinterpret_cast<DrawingCommand*>(current + sizeof(uint32_t))->ExecuteOnDT(aDT, aTransform);
reinterpret_cast<DrawingCommand*>(current + sizeof(uint32_t))->ExecuteOnDT(aDT, &aTransform);
current += *(uint32_t*)current;
}
}

120
gfx/2d/DrawingJob.cpp Normal file
Просмотреть файл

@ -0,0 +1,120 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DrawingJob.h"
#include "JobScheduler.h"
#include "mozilla/gfx/2D.h"
namespace mozilla {
namespace gfx {
DrawingJobBuilder::DrawingJobBuilder()
{}
DrawingJobBuilder::~DrawingJobBuilder()
{
MOZ_ASSERT(!mDrawTarget);
}
void
DrawingJob::Clear()
{
mCommandBuffer = nullptr;
mCursor = 0;
}
void
DrawingJobBuilder::BeginDrawingJob(DrawTarget* aTarget, IntPoint aOffset,
SyncObject* aStart)
{
MOZ_ASSERT(mCommandOffsets.empty());
MOZ_ASSERT(aTarget);
mDrawTarget = aTarget;
mOffset = aOffset;
mStart = aStart;
}
DrawingJob*
DrawingJobBuilder::EndDrawingJob(CommandBuffer* aCmdBuffer,
SyncObject* aCompletion,
WorkerThread* aPinToWorker)
{
MOZ_ASSERT(mDrawTarget);
DrawingJob* task = new DrawingJob(mDrawTarget, mOffset, mStart, aCompletion, aPinToWorker);
task->mCommandBuffer = aCmdBuffer;
task->mCommandOffsets = Move(mCommandOffsets);
mDrawTarget = nullptr;
mOffset = IntPoint();
mStart = nullptr;
return task;
}
DrawingJob::DrawingJob(DrawTarget* aTarget, IntPoint aOffset,
SyncObject* aStart, SyncObject* aCompletion,
WorkerThread* aPinToWorker)
: Job(aStart, aCompletion, aPinToWorker)
, mCommandBuffer(nullptr)
, mCursor(0)
, mDrawTarget(aTarget)
, mOffset(aOffset)
{
mCommandOffsets.reserve(64);
}
JobStatus
DrawingJob::Run()
{
while (mCursor < mCommandOffsets.size()) {
const DrawingCommand* cmd = mCommandBuffer->GetDrawingCommand(mCommandOffsets[mCursor]);
if (!cmd) {
return JobStatus::Error;
}
cmd->ExecuteOnDT(mDrawTarget);
++mCursor;
}
return JobStatus::Complete;
}
DrawingJob::~DrawingJob()
{
Clear();
}
const DrawingCommand*
CommandBuffer::GetDrawingCommand(ptrdiff_t aId)
{
return static_cast<DrawingCommand*>(mStorage.GetStorage(aId));
}
CommandBuffer::~CommandBuffer()
{
mStorage.ForEach([](void* item){
static_cast<DrawingCommand*>(item)->~DrawingCommand();
});
mStorage.Clear();
}
void
CommandBufferBuilder::BeginCommandBuffer(size_t aBufferSize)
{
MOZ_ASSERT(!mCommands);
mCommands = new CommandBuffer(aBufferSize);
}
already_AddRefed<CommandBuffer>
CommandBufferBuilder::EndCommandBuffer()
{
return mCommands.forget();
}
} // namespace
} // namespace

157
gfx/2d/DrawingJob.h Normal file
Просмотреть файл

@ -0,0 +1,157 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_GFX_COMMANDBUFFER_H_
#define MOZILLA_GFX_COMMANDBUFFER_H_
#include <stdint.h>
#include "mozilla/RefPtr.h"
#include "mozilla/Assertions.h"
#include "mozilla/gfx/Matrix.h"
#include "mozilla/gfx/JobScheduler.h"
#include "mozilla/gfx/IterableArena.h"
#include "DrawCommand.h"
namespace mozilla {
namespace gfx {
class DrawingCommand;
class PrintCommand;
class SignalCommand;
class DrawingJob;
class WaitCommand;
class SyncObject;
class MultiThreadedJobQueue;
class DrawTarget;
class DrawingJobBuilder;
class CommandBufferBuilder;
/// Contains a sequence of immutable drawing commands that are typically used by
/// several DrawingJobs.
///
/// CommandBuffer objects are built using CommandBufferBuilder.
class CommandBuffer : public external::AtomicRefCounted<CommandBuffer>
{
public:
MOZ_DECLARE_REFCOUNTED_TYPENAME(CommandBuffer)
~CommandBuffer();
const DrawingCommand* GetDrawingCommand(ptrdiff_t aId);
protected:
explicit CommandBuffer(size_t aSize = 256)
: mStorage(IterableArena::GROWABLE, aSize)
{}
IterableArena mStorage;
friend class CommandBufferBuilder;
};
/// Generates CommandBuffer objects.
///
/// The builder is a separate object to ensure that commands are not added to a
/// submitted CommandBuffer.
class CommandBufferBuilder
{
public:
void BeginCommandBuffer(size_t aBufferSize = 256);
already_AddRefed<CommandBuffer> EndCommandBuffer();
/// Build the CommandBuffer, command after command.
/// This must be used between BeginCommandBuffer and EndCommandBuffer.
template<typename T, typename... Args>
ptrdiff_t AddCommand(Args&&... aArgs)
{
static_assert(IsBaseOf<DrawingCommand, T>::value,
"T must derive from DrawingCommand");
return mCommands->mStorage.Alloc<T>(Forward<Args>(aArgs)...);
}
bool HasCommands() const { return !!mCommands; }
protected:
RefPtr<CommandBuffer> mCommands;
};
/// Stores multiple commands to be executed sequencially.
class DrawingJob : public Job {
public:
~DrawingJob();
virtual JobStatus Run() override;
protected:
DrawingJob(DrawTarget* aTarget,
IntPoint aOffset,
SyncObject* aStart,
SyncObject* aCompletion,
WorkerThread* aPinToWorker = nullptr);
/// Runs the tasks's destructors and resets the buffer.
void Clear();
std::vector<ptrdiff_t> mCommandOffsets;
RefPtr<CommandBuffer> mCommandBuffer;
uint32_t mCursor;
RefPtr<DrawTarget> mDrawTarget;
IntPoint mOffset;
friend class DrawingJobBuilder;
};
/// Generates DrawingJob objects.
///
/// The builder is a separate object to ensure that commands are not added to a
/// submitted DrawingJob.
class DrawingJobBuilder {
public:
DrawingJobBuilder();
~DrawingJobBuilder();
/// Allocates a DrawingJob.
///
/// call this method before starting to add commands.
void BeginDrawingJob(DrawTarget* aTarget, IntPoint aOffset,
SyncObject* aStart = nullptr);
/// Build the DrawingJob, command after command.
/// This must be used between BeginDrawingJob and EndDrawingJob.
void AddCommand(ptrdiff_t offset)
{
mCommandOffsets.push_back(offset);
}
/// Finalizes and returns the drawing task.
///
/// If aCompletion is not null, the sync object will be signaled after the
/// task buffer is destroyed (and after the destructor of the tasks have run).
/// In most cases this means after the completion of all tasks in the task buffer,
/// but also when the task buffer is destroyed due to an error.
DrawingJob* EndDrawingJob(CommandBuffer* aCmdBuffer,
SyncObject* aCompletion = nullptr,
WorkerThread* aPinToWorker = nullptr);
/// Returns true between BeginDrawingJob and EndDrawingJob, false otherwise.
bool HasDrawingJob() const { return !!mDrawTarget; }
protected:
std::vector<ptrdiff_t> mCommandOffsets;
RefPtr<DrawTarget> mDrawTarget;
IntPoint mOffset;
RefPtr<SyncObject> mStart;
};
} // namespace
} // namespace
#endif

193
gfx/2d/IterableArena.h Normal file
Просмотреть файл

@ -0,0 +1,193 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_GFX_ITERABLEARENA_H_
#define MOZILLA_GFX_ITERABLEARENA_H_
#include "mozilla/Move.h"
#include "mozilla/Assertions.h"
#include "mozilla/gfx/Logging.h"
#include <string.h>
#include <vector>
#include <stdint.h>
#include <stdio.h>
namespace mozilla {
namespace gfx {
/// A simple pool allocator for plain data structures.
///
/// Beware that the pool will not attempt to run the destructors. It is the
/// responsibility of the user of this class to either use objects with no
/// destructor or to manually call the allocated objects destructors.
/// If the pool is growable, its allocated objects must be safely moveable in
/// in memory (through memcpy).
class IterableArena {
protected:
struct Header
{
size_t mBlocSize;
};
public:
enum ArenaType {
FIXED_SIZE,
GROWABLE
};
IterableArena(ArenaType aType, size_t aStorageSize)
: mSize(aStorageSize)
, mCursor(0)
, mIsGrowable(aType == GROWABLE)
{
if (mSize == 0) {
mSize = 128;
}
mStorage = (uint8_t*)malloc(mSize);
if (mStorage == nullptr) {
gfxCriticalError() << "Not enough Memory allocate a memory pool of size " << aStorageSize;
MOZ_CRASH();
}
}
~IterableArena()
{
free(mStorage);
}
/// Constructs a new item in the pool and returns a positive offset in case of
/// success.
///
/// The offset never changes even if the storage is reallocated, so users
/// of this class should prefer storing offsets rather than direct pointers
/// to the allocated objects.
/// Alloc can cause the storage to be reallocated if the pool was initialized
/// with IterableArena::GROWABLE.
/// If for any reason the pool fails to allocate enough space for the new item
/// Alloc returns a negative offset and the object's constructor is not called.
template<typename T, typename... Args>
ptrdiff_t
Alloc(Args&&... aArgs)
{
void* storage = nullptr;
auto offset = AllocRaw(sizeof(T), &storage);
if (offset < 0) {
return offset;
}
new (storage) T(Forward<Args>(aArgs)...);
return offset;
}
ptrdiff_t AllocRaw(size_t aSize, void** aOutPtr = nullptr)
{
const size_t blocSize = AlignedSize(sizeof(Header) + aSize);
if (AlignedSize(mCursor + blocSize) > mSize) {
if (!mIsGrowable) {
return -1;
}
size_t newSize = mSize * 2;
while (AlignedSize(mCursor + blocSize) > newSize) {
newSize *= 2;
}
uint8_t* newStorage = (uint8_t*)realloc(mStorage, newSize);
if (!newStorage) {
gfxCriticalError() << "Not enough Memory to grow the memory pool, size: " << newSize;
return -1;
}
mStorage = newStorage;
mSize = newSize;
}
ptrdiff_t offset = mCursor;
GetHeader(offset)->mBlocSize = blocSize;
mCursor += blocSize;
if (aOutPtr) {
*aOutPtr = GetStorage(offset);
}
return offset;
}
/// Get access to an allocated item at a given offset (only use offsets returned
/// by Alloc or AllocRaw).
///
/// If the pool is growable, the returned pointer is only valid temporarily. The
/// underlying storage can be reallocated in Alloc or AllocRaw, so do not keep
/// these pointers around and store the offset instead.
void* GetStorage(ptrdiff_t offset = 0)
{
MOZ_ASSERT(offset >= 0);
MOZ_ASSERT(offset < mCursor);
return offset >= 0 ? mStorage + offset + sizeof(Header) : nullptr;
}
/// Clears the storage without running any destructor and without deallocating it.
void Clear()
{
mCursor = 0;
}
/// Iterate over the elements allocated in this pool.
///
/// Takes a lambda or function object accepting a void* as parameter.
template<typename Func>
void ForEach(Func cb)
{
Iterator it;
while (void* ptr = it.Next(this)) {
cb(ptr);
}
}
/// A simple iterator over an arena.
class Iterator {
public:
Iterator()
: mCursor(0)
{}
void* Next(IterableArena* aArena)
{
if (mCursor >= aArena->mCursor) {
return nullptr;
}
void* result = aArena->GetStorage(mCursor);
const size_t blocSize = aArena->GetHeader(mCursor)->mBlocSize;
MOZ_ASSERT(blocSize != 0);
mCursor += blocSize;
return result;
}
private:
ptrdiff_t mCursor;
};
protected:
Header* GetHeader(ptrdiff_t offset)
{
return (Header*) (mStorage + offset);
}
size_t AlignedSize(size_t aSize) const
{
const size_t alignment = sizeof(uintptr_t);
return aSize + (alignment - (aSize % alignment)) % alignment;
}
uint8_t* mStorage;
uint32_t mSize;
ptrdiff_t mCursor;
bool mIsGrowable;
friend class Iterator;
};
} // namespace
} // namespace
#endif

279
gfx/2d/JobScheduler.cpp Normal file
Просмотреть файл

@ -0,0 +1,279 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "JobScheduler.h"
namespace mozilla {
namespace gfx {
JobScheduler* JobScheduler::sSingleton = nullptr;
bool JobScheduler::Init(uint32_t aNumThreads, uint32_t aNumQueues)
{
MOZ_ASSERT(!sSingleton);
MOZ_ASSERT(aNumThreads >= aNumQueues);
sSingleton = new JobScheduler();
sSingleton->mNextQueue = 0;
for (uint32_t i = 0; i < aNumQueues; ++i) {
sSingleton->mDrawingQueues.push_back(new MultiThreadedJobQueue());
}
for (uint32_t i = 0; i < aNumThreads; ++i) {
sSingleton->mWorkerThreads.push_back(WorkerThread::Create(sSingleton->mDrawingQueues[i%aNumQueues]));
}
return true;
}
void JobScheduler::ShutDown()
{
MOZ_ASSERT(IsEnabled());
if (!IsEnabled()) {
return;
}
for (auto queue : sSingleton->mDrawingQueues) {
queue->ShutDown();
delete queue;
}
for (WorkerThread* thread : sSingleton->mWorkerThreads) {
// this will block until the thread is joined.
delete thread;
}
sSingleton->mWorkerThreads.clear();
delete sSingleton;
sSingleton = nullptr;
}
JobStatus
JobScheduler::ProcessJob(Job* aJob)
{
MOZ_ASSERT(aJob);
auto status = aJob->Run();
if (status == JobStatus::Error || status == JobStatus::Complete) {
delete aJob;
}
return status;
}
void
JobScheduler::SubmitJob(Job* aJob)
{
MOZ_ASSERT(aJob);
RefPtr<SyncObject> start = aJob->GetStartSync();
if (start && start->Register(aJob)) {
// The Job buffer starts with a non-signaled sync object, it
// is now registered in the list of task buffers waiting on the
// sync object, so we should not place it in the queue.
return;
}
GetQueueForJob(aJob)->SubmitJob(aJob);
}
MultiThreadedJobQueue*
JobScheduler::GetQueueForJob(Job* aJob)
{
return aJob->IsPinnedToAThread() ? aJob->GetWorkerThread()->GetJobQueue()
: GetDrawingQueue();
}
Job::Job(SyncObject* aStart, SyncObject* aCompletion, WorkerThread* aThread)
: mNextWaitingJob(nullptr)
, mStartSync(aStart)
, mCompletionSync(aCompletion)
, mPinToThread(aThread)
{
if (mStartSync) {
mStartSync->AddSubsequent(this);
}
if (mCompletionSync) {
mCompletionSync->AddPrerequisite(this);
}
}
Job::~Job()
{
if (mCompletionSync) {
//printf(" -- Job %p dtor completion %p\n", this, mCompletionSync);
mCompletionSync->Signal();
mCompletionSync = nullptr;
}
}
JobStatus
SetEventJob::Run()
{
mEvent->Set();
return JobStatus::Complete;
}
SetEventJob::SetEventJob(EventObject* aEvent,
SyncObject* aStart, SyncObject* aCompletion,
WorkerThread* aWorker)
: Job(aStart, aCompletion, aWorker)
, mEvent(aEvent)
{}
SetEventJob::~SetEventJob()
{}
SyncObject::SyncObject(uint32_t aNumPrerequisites)
: mSignals(aNumPrerequisites)
, mFirstWaitingJob(nullptr)
#ifdef DEBUG
, mNumPrerequisites(aNumPrerequisites)
, mAddedPrerequisites(0)
#endif
{}
SyncObject::~SyncObject()
{
MOZ_ASSERT(mFirstWaitingJob == nullptr);
}
bool
SyncObject::Register(Job* aJob)
{
MOZ_ASSERT(aJob);
// For now, ensure that when we schedule the first subsequent, we have already
// created all of the prerequisites. This is an arbitrary restriction because
// we specify the number of prerequisites in the constructor, but in the typical
// scenario, if the assertion FreezePrerequisite blows up here it probably means
// we got the initial nmber of prerequisites wrong. We can decide to remove
// this restriction if needed.
FreezePrerequisites();
int32_t signals = mSignals;
if (signals > 0) {
AddWaitingJob(aJob);
// Since Register and Signal can be called concurrently, it can happen that
// reading mSignals in Register happens before decrementing mSignals in Signal,
// but SubmitWaitingJobs happens before AddWaitingJob. This ordering means
// the SyncObject ends up in the signaled state with a task sitting in the
// waiting list. To prevent that we check mSignals a second time and submit
// again if signals reached zero in the mean time.
// We do this instead of holding a mutex around mSignals+mJobs to reduce
// lock contention.
int32_t signals2 = mSignals;
if (signals2 == 0) {
SubmitWaitingJobs();
}
return true;
}
return false;
}
void
SyncObject::Signal()
{
int32_t signals = --mSignals;
MOZ_ASSERT(signals >= 0);
if (signals == 0) {
SubmitWaitingJobs();
}
}
void
SyncObject::AddWaitingJob(Job* aJob)
{
// Push (using atomics) the task into the list of waiting tasks.
for (;;) {
Job* first = mFirstWaitingJob;
aJob->mNextWaitingJob = first;
if (mFirstWaitingJob.compareExchange(first, aJob)) {
break;
}
}
}
void SyncObject::SubmitWaitingJobs()
{
// Scheduling the tasks can cause code that modifies <this>'s reference
// count to run concurrently, and cause the caller of this function to
// be owned by another thread. We need to make sure the reference count
// does not reach 0 on another thread before the end of this method, so
// hold a strong ref to prevent that!
RefPtr<SyncObject> kungFuDeathGrip(this);
// First atomically swap mFirstWaitingJob and waitingJobs...
Job* waitingJobs = nullptr;
for (;;) {
waitingJobs = mFirstWaitingJob;
if (mFirstWaitingJob.compareExchange(waitingJobs, nullptr)) {
break;
}
}
// ... and submit all of the waiting tasks in waitingJob now that they belong
// to this thread.
while (waitingJobs) {
Job* next = waitingJobs->mNextWaitingJob;
waitingJobs->mNextWaitingJob = nullptr;
JobScheduler::GetQueueForJob(waitingJobs)->SubmitJob(waitingJobs);
waitingJobs = next;
}
}
bool
SyncObject::IsSignaled()
{
return mSignals == 0;
}
void
SyncObject::FreezePrerequisites()
{
MOZ_ASSERT(mAddedPrerequisites == mNumPrerequisites);
}
void
SyncObject::AddPrerequisite(Job* aJob)
{
MOZ_ASSERT(++mAddedPrerequisites <= mNumPrerequisites);
}
void
SyncObject::AddSubsequent(Job* aJob)
{
}
WorkerThread::WorkerThread(MultiThreadedJobQueue* aJobQueue)
: mQueue(aJobQueue)
{
aJobQueue->RegisterThread();
}
void
WorkerThread::Run()
{
SetName("gfx worker");
for (;;) {
Job* commands = nullptr;
if (!mQueue->WaitForJob(commands)) {
mQueue->UnregisterThread();
return;
}
JobStatus status = JobScheduler::ProcessJob(commands);
if (status == JobStatus::Error) {
// Don't try to handle errors for now, but that's open to discussions.
// I expect errors to be mostly OOM issues.
MOZ_CRASH();
}
}
}
} //namespace
} //namespace

250
gfx/2d/JobScheduler.h Normal file
Просмотреть файл

@ -0,0 +1,250 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_GFX_TASKSCHEDULER_H_
#define MOZILLA_GFX_TASKSCHEDULER_H_
#include "mozilla/RefPtr.h"
#include "mozilla/gfx/Types.h"
#ifdef WIN32
#include "mozilla/gfx/JobScheduler_win32.h"
#else
#include "mozilla/gfx/JobScheduler_posix.h"
#endif
#include <vector>
namespace mozilla {
namespace gfx {
class MultiThreadedJobQueue;
class SyncObject;
class WorkerThread;
class JobScheduler {
public:
/// Return one of the queues that the drawing worker threads pull from, chosen
/// pseudo-randomly.
static MultiThreadedJobQueue* GetDrawingQueue()
{
return sSingleton->mDrawingQueues[
sSingleton->mNextQueue++ % sSingleton->mDrawingQueues.size()
];
}
/// Return one of the queues that the drawing worker threads pull from with a
/// hash to choose the queue.
///
/// Calling this function several times with the same hash will yield the same queue.
static MultiThreadedJobQueue* GetDrawingQueue(uint32_t aHash)
{
return sSingleton->mDrawingQueues[
aHash % sSingleton->mDrawingQueues.size()
];
}
/// Return the task queue associated to the worker the task is pinned to if
/// the task is pinned to a worker, or a random queue.
static MultiThreadedJobQueue* GetQueueForJob(Job* aJob);
/// Initialize the task scheduler with aNumThreads worker threads for drawing
/// and aNumQueues task queues.
///
/// The number of threads must be superior or equal to the number of queues
/// (since for now a worker thread only pulls from one queue).
static bool Init(uint32_t aNumThreads, uint32_t aNumQueues);
/// Shut the scheduler down.
///
/// This will block until worker threads are joined and deleted.
static void ShutDown();
/// Returns true if there is a successfully initialized JobScheduler singleton.
static bool IsEnabled() { return !!sSingleton; }
/// Submit a task buffer to its associated queue.
///
/// The caller looses ownership of the task buffer.
static void SubmitJob(Job* aJobs);
/// Process commands until the command buffer needs to block on a sync object,
/// completes, yields, or encounters an error.
///
/// Can be used on any thread. Worker threads basically loop over this, but the
/// main thread can also dequeue pending task buffers and process them alongside
/// the worker threads if it is about to block until completion anyway.
///
/// The caller looses ownership of the task buffer.
static JobStatus ProcessJob(Job* aJobs);
protected:
static JobScheduler* sSingleton;
// queues of Job that are ready to be processed
std::vector<MultiThreadedJobQueue*> mDrawingQueues;
std::vector<WorkerThread*> mWorkerThreads;
Atomic<uint32_t> mNextQueue;
};
/// Jobs are not reference-counted because they don't have shared ownership.
/// The ownership of tasks can change when they are passed to certain methods
/// of JobScheduler and SyncObject. See the docuumentaion of these classes.
class Job {
public:
Job(SyncObject* aStart, SyncObject* aCompletion, WorkerThread* aThread = nullptr);
virtual ~Job();
virtual JobStatus Run() = 0;
/// For use in JobScheduler::SubmitJob. Don't use it anywhere else.
//already_AddRefed<SyncObject> GetAndResetStartSync();
SyncObject* GetStartSync() { return mStartSync; }
bool IsPinnedToAThread() const { return !!mPinToThread; }
WorkerThread* GetWorkerThread() { return mPinToThread; }
protected:
// An intrusive linked list of tasks waiting for a sync object to enter the
// signaled state. When the task is not waiting for a sync object, mNextWaitingJob
// should be null. This is only accessed from the thread that owns the task.
Job* mNextWaitingJob;
RefPtr<SyncObject> mStartSync;
RefPtr<SyncObject> mCompletionSync;
WorkerThread* mPinToThread;
friend class SyncObject;
};
class EventObject;
/// This task will set an EventObject.
///
/// Typically used as the final task, so that the main thread can block on the
/// corresponfing EventObject until all of the tasks are processed.
class SetEventJob : public Job
{
public:
explicit SetEventJob(EventObject* aEvent,
SyncObject* aStart, SyncObject* aCompletion = nullptr,
WorkerThread* aPinToWorker = nullptr);
~SetEventJob();
JobStatus Run() override;
EventObject* GetEvent() { return mEvent; }
protected:
RefPtr<EventObject> mEvent;
};
/// A synchronization object that can be used to express dependencies and ordering between
/// tasks.
///
/// Jobs can register to SyncObjects in order to asynchronously wait for a signal.
/// In practice, Job objects usually start with a sync object (startSyc) and end
/// with another one (completionSync).
/// a Job never gets processed before its startSync is in the signaled state, and
/// signals its completionSync as soon as it finishes. This is how dependencies
/// between tasks is expressed.
class SyncObject final : public external::AtomicRefCounted<SyncObject> {
public:
MOZ_DECLARE_REFCOUNTED_TYPENAME(SyncObject)
/// Create a synchronization object.
///
/// aNumPrerequisites represents the number of times the object must be signaled
/// before actually entering the signaled state (in other words, it means the
/// number of dependencies of this sync object).
///
/// Explicitly specifying the number of prerequisites when creating sync objects
/// makes it easy to start scheduling some of the prerequisite tasks while
/// creating the others, which is how we typically use the task scheduler.
/// Automatically determining the number of prerequisites using Job's constructor
/// brings the risk that the sync object enters the signaled state while we
/// are still adding prerequisites which is hard to fix without using muteces.
explicit SyncObject(uint32_t aNumPrerequisites = 1);
~SyncObject();
/// Attempt to register a task.
///
/// If the sync object is already in the signaled state, the buffer is *not*
/// registered and the sync object does not take ownership of the task.
/// If the object is not yet in the signaled state, it takes ownership of
/// the task and places it in a list of pending tasks.
/// Pending tasks will not be processed by the worker thread.
/// When the SyncObject reaches the signaled state, it places the pending
/// tasks back in the available buffer queue, so that they can be
/// scheduled again.
///
/// Returns true if the SyncOject is not already in the signaled state.
/// This means that if this method returns true, the SyncObject has taken
/// ownership of the Job.
bool Register(Job* aJob);
/// Signal the SyncObject.
///
/// This decrements an internal counter. The sync object reaches the signaled
/// state when the counter gets to zero.
void Signal();
/// Returns true if mSignals is equal to zero. In other words, returns true
/// if all prerequisite tasks have already signaled the sync object.
bool IsSignaled();
/// Asserts that the number of added prerequisites is equal to the number
/// specified in the constructor (does nothin in release builds).
void FreezePrerequisites();
private:
// Called by Job's constructor
void AddSubsequent(Job* aJob);
void AddPrerequisite(Job* aJob);
void AddWaitingJob(Job* aJob);
void SubmitWaitingJobs();
Atomic<int32_t> mSignals;
Atomic<Job*> mFirstWaitingJob;
#ifdef DEBUG
uint32_t mNumPrerequisites;
Atomic<uint32_t> mAddedPrerequisites;
#endif
friend class Job;
friend class JobScheduler;
};
/// Base class for worker threads.
class WorkerThread
{
public:
static WorkerThread* Create(MultiThreadedJobQueue* aJobQueue);
virtual ~WorkerThread() {}
void Run();
MultiThreadedJobQueue* GetJobQueue() { return mQueue; }
protected:
explicit WorkerThread(MultiThreadedJobQueue* aJobQueue);
virtual void SetName(const char* aName) {}
MultiThreadedJobQueue* mQueue;
};
} // namespace
} // namespace
#endif

Просмотреть файл

@ -0,0 +1,194 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "JobScheduler.h"
#include "mozilla/gfx/Logging.h"
using namespace std;
namespace mozilla {
namespace gfx {
void* ThreadCallback(void* threadData);
class WorkerThreadPosix : public WorkerThread {
public:
explicit WorkerThreadPosix(MultiThreadedJobQueue* aJobQueue)
: WorkerThread(aJobQueue)
{
pthread_create(&mThread, nullptr, ThreadCallback, static_cast<WorkerThread*>(this));
}
~WorkerThreadPosix()
{
pthread_join(mThread, nullptr);
}
virtual void SetName(const char*) override
{
// XXX - temporarily disabled, see bug 1209039
//
// // Call this from the thread itself because of Mac.
//#ifdef XP_MACOSX
// pthread_setname_np(aName);
//#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
// pthread_set_name_np(mThread, aName);
//#elif defined(__NetBSD__)
// pthread_setname_np(mThread, "%s", (void*)aName);
//#else
// pthread_setname_np(mThread, aName);
//#endif
}
protected:
pthread_t mThread;
};
void* ThreadCallback(void* threadData)
{
WorkerThread* thread = static_cast<WorkerThread*>(threadData);
thread->Run();
return nullptr;
}
WorkerThread*
WorkerThread::Create(MultiThreadedJobQueue* aJobQueue)
{
return new WorkerThreadPosix(aJobQueue);
}
MultiThreadedJobQueue::MultiThreadedJobQueue()
: mThreadsCount(0)
, mShuttingDown(false)
{}
MultiThreadedJobQueue::~MultiThreadedJobQueue()
{
MOZ_ASSERT(mJobs.empty());
}
bool
MultiThreadedJobQueue::WaitForJob(Job*& aOutJob)
{
return PopJob(aOutJob, BLOCKING);
}
bool
MultiThreadedJobQueue::PopJob(Job*& aOutJobs, AccessType aAccess)
{
for (;;) {
MutexAutoLock lock(&mMutex);
while (aAccess == BLOCKING && !mShuttingDown && mJobs.empty()) {
mAvailableCondvar.Wait(&mMutex);
}
if (mShuttingDown) {
return false;
}
if (mJobs.empty()) {
if (aAccess == NON_BLOCKING) {
return false;
}
continue;
}
Job* task = mJobs.front();
MOZ_ASSERT(task);
mJobs.pop_front();
aOutJobs = task;
return true;
}
}
void
MultiThreadedJobQueue::SubmitJob(Job* aJobs)
{
MOZ_ASSERT(aJobs);
MutexAutoLock lock(&mMutex);
mJobs.push_back(aJobs);
mAvailableCondvar.Broadcast();
}
size_t
MultiThreadedJobQueue::NumJobs()
{
MutexAutoLock lock(&mMutex);
return mJobs.size();
}
bool
MultiThreadedJobQueue::IsEmpty()
{
MutexAutoLock lock(&mMutex);
return mJobs.empty();
}
void
MultiThreadedJobQueue::ShutDown()
{
MutexAutoLock lock(&mMutex);
mShuttingDown = true;
while (mThreadsCount) {
mAvailableCondvar.Broadcast();
mShutdownCondvar.Wait(&mMutex);
}
}
void
MultiThreadedJobQueue::RegisterThread()
{
mThreadsCount += 1;
}
void
MultiThreadedJobQueue::UnregisterThread()
{
MutexAutoLock lock(&mMutex);
mThreadsCount -= 1;
if (mThreadsCount == 0) {
mShutdownCondvar.Broadcast();
}
}
EventObject::EventObject()
: mIsSet(false)
{}
EventObject::~EventObject()
{}
bool
EventObject::Peak()
{
MutexAutoLock lock(&mMutex);
return mIsSet;
}
void
EventObject::Set()
{
MutexAutoLock lock(&mMutex);
if (!mIsSet) {
mIsSet = true;
mCond.Broadcast();
}
}
void
EventObject::Wait()
{
MutexAutoLock lock(&mMutex);
if (mIsSet) {
return;
}
mCond.Wait(&mMutex);
}
} // namespce
} // namespce

144
gfx/2d/JobScheduler_posix.h Normal file
Просмотреть файл

@ -0,0 +1,144 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef WIN32
#ifndef MOZILLA_GFX_TASKSCHEDULER_POSIX_H_
#define MOZILLA_GFX_TASKSCHEDULER_POSIX_H_
#include <string>
#include <vector>
#include <list>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include "mozilla/RefPtr.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/gfx/CriticalSection.h"
namespace mozilla {
namespace gfx {
class Job;
class PosixCondVar;
class WorkerThread;
typedef mozilla::gfx::CriticalSection Mutex;
typedef mozilla::gfx::CriticalSectionAutoEnter MutexAutoLock;
// posix platforms only!
class PosixCondVar {
public:
PosixCondVar() {
DebugOnly<int> err = pthread_cond_init(&mCond, nullptr);
MOZ_ASSERT(!err);
}
~PosixCondVar() {
DebugOnly<int> err = pthread_cond_destroy(&mCond);
MOZ_ASSERT(!err);
}
void Wait(Mutex* aMutex) {
DebugOnly<int> err = pthread_cond_wait(&mCond, &aMutex->mMutex);
MOZ_ASSERT(!err);
}
void Broadcast() {
DebugOnly<int> err = pthread_cond_broadcast(&mCond);
MOZ_ASSERT(!err);
}
protected:
pthread_cond_t mCond;
};
/// A simple and naive multithreaded task queue
///
/// The public interface of this class must remain identical to its equivalent
/// in JobScheduler_win32.h
class MultiThreadedJobQueue {
public:
enum AccessType {
BLOCKING,
NON_BLOCKING
};
// Producer thread
MultiThreadedJobQueue();
// Producer thread
~MultiThreadedJobQueue();
// Worker threads
bool WaitForJob(Job*& aOutJob);
// Any thread
bool PopJob(Job*& aOutJob, AccessType aAccess);
// Any threads
void SubmitJob(Job* aJob);
// Producer thread
void ShutDown();
// Any thread
size_t NumJobs();
// Any thread
bool IsEmpty();
// Producer thread
void RegisterThread();
// Worker threads
void UnregisterThread();
protected:
std::list<Job*> mJobs;
Mutex mMutex;
PosixCondVar mAvailableCondvar;
PosixCondVar mShutdownCondvar;
int32_t mThreadsCount;
bool mShuttingDown;
friend class WorkerThread;
};
/// An object that a thread can synchronously wait on.
/// Usually set by a SetEventJob.
class EventObject : public external::AtomicRefCounted<EventObject>
{
public:
MOZ_DECLARE_REFCOUNTED_TYPENAME(EventObject)
EventObject();
~EventObject();
/// Synchronously wait until the event is set.
void Wait();
/// Return true if the event is set, without blocking.
bool Peak();
/// Set the event.
void Set();
protected:
Mutex mMutex;
PosixCondVar mCond;
bool mIsSet;
};
} // namespace
} // namespace
#include "JobScheduler.h"
#endif
#endif

Просмотреть файл

@ -0,0 +1,143 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "JobScheduler.h"
#include "mozilla/gfx/Logging.h"
using namespace std;
namespace mozilla {
namespace gfx {
DWORD __stdcall ThreadCallback(void* threadData);
class WorkerThreadWin32 : public WorkerThread {
public:
explicit WorkerThreadWin32(MultiThreadedJobQueue* aJobQueue)
: WorkerThread(aJobQueue)
{
mThread = ::CreateThread(nullptr, 0, ThreadCallback, static_cast<WorkerThread*>(this), 0, nullptr);
}
~WorkerThreadWin32()
{
::WaitForSingleObject(mThread, INFINITE);
::CloseHandle(mThread);
}
protected:
HANDLE mThread;
};
DWORD __stdcall ThreadCallback(void* threadData)
{
WorkerThread* thread = static_cast<WorkerThread*>(threadData);
thread->Run();
return 0;
}
WorkerThread*
WorkerThread::Create(MultiThreadedJobQueue* aJobQueue)
{
return new WorkerThreadWin32(aJobQueue);
}
bool
MultiThreadedJobQueue::PopJob(Job*& aOutJob, AccessType aAccess)
{
for (;;) {
while (aAccess == BLOCKING && mJobs.empty()) {
{
CriticalSectionAutoEnter lock(&mSection);
if (mShuttingDown) {
return false;
}
}
HANDLE handles[] = { mAvailableEvent, mShutdownEvent };
::WaitForMultipleObjects(2, handles, FALSE, INFINITE);
}
CriticalSectionAutoEnter lock(&mSection);
if (mShuttingDown) {
return false;
}
if (mJobs.empty()) {
if (aAccess == NON_BLOCKING) {
return false;
}
continue;
}
Job* task = mJobs.front();
MOZ_ASSERT(task);
mJobs.pop_front();
if (mJobs.empty()) {
::ResetEvent(mAvailableEvent);
}
aOutJob = task;
return true;
}
}
void
MultiThreadedJobQueue::SubmitJob(Job* aJob)
{
MOZ_ASSERT(aJob);
CriticalSectionAutoEnter lock(&mSection);
mJobs.push_back(aJob);
::SetEvent(mAvailableEvent);
}
void
MultiThreadedJobQueue::ShutDown()
{
{
CriticalSectionAutoEnter lock(&mSection);
mShuttingDown = true;
}
while (mThreadsCount) {
::SetEvent(mAvailableEvent);
::WaitForSingleObject(mShutdownEvent, INFINITE);
}
}
size_t
MultiThreadedJobQueue::NumJobs()
{
CriticalSectionAutoEnter lock(&mSection);
return mJobs.size();
}
bool
MultiThreadedJobQueue::IsEmpty()
{
CriticalSectionAutoEnter lock(&mSection);
return mJobs.empty();
}
void
MultiThreadedJobQueue::RegisterThread()
{
mThreadsCount += 1;
}
void
MultiThreadedJobQueue::UnregisterThread()
{
CriticalSectionAutoEnter lock(&mSection);
mThreadsCount -= 1;
if (mThreadsCount == 0) {
::SetEvent(mShutdownEvent);
}
}
} // namespace
} // namespace

Просмотреть файл

@ -0,0 +1,98 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifdef WIN32
#ifndef MOZILLA_GFX_TASKSCHEDULER_WIN32_H_
#define MOZILLA_GFX_TASKSCHEDULER_WIN32_H_
#include <windows.h>
#include <list>
#include "mozilla/RefPtr.h"
#include "mozilla/gfx/CriticalSection.h"
namespace mozilla {
namespace gfx {
class WorkerThread;
class Job;
// The public interface of this class must remain identical to its equivalent
// in JobScheduler_posix.h
class MultiThreadedJobQueue {
public:
enum AccessType {
BLOCKING,
NON_BLOCKING
};
MultiThreadedJobQueue()
: mThreadsCount(0)
, mShuttingDown(false)
{
mAvailableEvent = ::CreateEventW(nullptr, TRUE, FALSE, nullptr);
mShutdownEvent = ::CreateEventW(nullptr, TRUE, FALSE, nullptr);
}
~MultiThreadedJobQueue()
{
::CloseHandle(mAvailableEvent);
::CloseHandle(mShutdownEvent);
}
bool WaitForJob(Job*& aOutJob) { return PopJob(aOutJob, BLOCKING); }
bool PopJob(Job*& aOutJob, AccessType aAccess);
void SubmitJob(Job* aJob);
void ShutDown();
size_t NumJobs();
bool IsEmpty();
void RegisterThread();
void UnregisterThread();
protected:
std::list<Job*> mJobs;
CriticalSection mSection;
HANDLE mAvailableEvent;
HANDLE mShutdownEvent;
int32_t mThreadsCount;
bool mShuttingDown;
friend class WorkerThread;
};
// The public interface of this class must remain identical to its equivalent
// in JobScheduler_posix.h
class EventObject : public external::AtomicRefCounted<EventObject>
{
public:
MOZ_DECLARE_REFCOUNTED_TYPENAME(EventObject)
EventObject() { mEvent = ::CreateEventW(nullptr, TRUE, FALSE, nullptr); }
~EventObject() { ::CloseHandle(mEvent); }
void Wait() { ::WaitForSingleObject(mEvent, INFINITE); }
bool Peak() { return ::WaitForSingleObject(mEvent, 0) == WAIT_OBJECT_0; }
void Set() { ::SetEvent(mEvent); }
protected:
// TODO: it's expensive to create events so we should try to reuse them
HANDLE mEvent;
};
} // namespace
} // namespace
#endif
#endif

Просмотреть файл

@ -289,6 +289,13 @@ struct GradientStop
Color color;
};
enum class JobStatus {
Complete,
Wait,
Yield,
Error
};
} // namespace gfx
} // namespace mozilla

Просмотреть файл

@ -20,11 +20,16 @@ EXPORTS.mozilla.gfx += [
'Blur.h',
'BorrowedContext.h',
'Coord.h',
'CriticalSection.h',
'DataSurfaceHelpers.h',
'DrawTargetTiled.h',
'Filters.h',
'Helpers.h',
'HelpersCairo.h',
'IterableArena.h',
'JobScheduler.h',
'JobScheduler_posix.h',
'JobScheduler_win32.h',
'Logging.h',
'Matrix.h',
'NumericTools.h',
@ -60,6 +65,7 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
'DrawTargetD2D1.cpp',
'ExtendInputEffectD2D1.cpp',
'FilterNodeD2D1.cpp',
'JobScheduler_win32.cpp',
'PathD2D.cpp',
'RadialGradientEffectD2D1.cpp',
'ScaledFontDWrite.cpp',
@ -70,6 +76,11 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
]
DEFINES['WIN32'] = True
if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'windows':
SOURCES += [
'JobScheduler_posix.cpp',
]
if CONFIG['MOZ_ENABLE_SKIA']:
UNIFIED_SOURCES += [
'convolver.cpp',
@ -118,6 +129,7 @@ UNIFIED_SOURCES += [
'DataSourceSurface.cpp',
'DataSurfaceHelpers.cpp',
'DrawEventRecorder.cpp',
'DrawingJob.cpp',
'DrawTarget.cpp',
'DrawTargetCairo.cpp',
'DrawTargetCapture.cpp',
@ -129,6 +141,7 @@ UNIFIED_SOURCES += [
'FilterProcessing.cpp',
'FilterProcessingScalar.cpp',
'ImageScaling.cpp',
'JobScheduler.cpp',
'Matrix.cpp',
'Path.cpp',
'PathCairo.cpp',

Просмотреть файл

@ -7,16 +7,21 @@
#define GFX_LAYERSTYPES_H
#include <stdint.h> // for uint32_t
#include "mozilla/gfx/Point.h" // for IntPoint
#include "nsRegion.h"
#include "mozilla/TypedEnumBits.h"
#ifdef MOZ_WIDGET_GONK
#include <utils/RefBase.h>
#if ANDROID_VERSION >= 21
#include <utils/NativeHandle.h>
#endif
#endif
#include "mozilla/gfx/Point.h" // for IntPoint
#include "mozilla/TypedEnumBits.h"
#include "nsRegion.h"
#include <stdio.h> // FILE
#include "mozilla/Logging.h" // for PR_LOG
#ifndef MOZ_LAYERS_HAVE_LOG
# define MOZ_LAYERS_HAVE_LOG
#endif
@ -110,6 +115,14 @@ struct LayerRenderState {
void SetOverlayId(const int32_t& aId)
{ mOverlayId = aId; }
android::GraphicBuffer* GetGrallocBuffer() const
{ return mSurface.get(); }
#if ANDROID_VERSION >= 21
android::NativeHandle* GetSidebandStream() const
{ return mSidebandStream.get(); }
#endif
#endif
void SetOffset(const nsIntPoint& aOffset)
@ -133,6 +146,9 @@ struct LayerRenderState {
// size of mSurface
gfx::IntSize mSize;
TextureHost* mTexture;
#if ANDROID_VERSION >= 21
android::sp<android::NativeHandle> mSidebandStream;
#endif
#endif
};

Просмотреть файл

@ -417,13 +417,11 @@ ClientTiledPaintedLayer::RenderLayer()
}
if (!mContentClient) {
#if defined(MOZ_B2G) || defined(XP_MACOSX)
if (mCreationHint == LayerManager::NONE &&
SingleTiledContentClient::ClientSupportsLayerSize(layerSize, ClientManager())) {
SingleTiledContentClient::ClientSupportsLayerSize(layerSize, ClientManager()) &&
gfxPrefs::LayersSingleTileEnabled()) {
mContentClient = new SingleTiledContentClient(this, ClientManager());
} else
#endif
{
} else {
mContentClient = new MultiTiledContentClient(this, ClientManager());
}
@ -567,7 +565,6 @@ ClientTiledPaintedLayer::RenderLayer()
bool
ClientTiledPaintedLayer::IsOptimizedFor(LayerManager::PaintedLayerCreationHint aHint)
{
#if defined(MOZ_B2G) || defined(XP_MACOSX)
// The only creation hint is whether the layer is scrollable or not, and this
// is only respected on B2G and OSX, where it's used to determine whether to
// use a tiled content client or not.
@ -575,9 +572,6 @@ ClientTiledPaintedLayer::IsOptimizedFor(LayerManager::PaintedLayerCreationHint a
// large, scrollable layers, so we want the layer to be recreated in this
// situation.
return aHint == GetCreationHint();
#else
return true;
#endif
}
void

Просмотреть файл

@ -0,0 +1,188 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "mozilla/gfx/IterableArena.h"
#include <string>
using namespace mozilla;
using namespace mozilla::gfx;
#ifdef A
#undef A
#endif
#ifdef B
#undef B
#endif
// to avoid having symbols that collide easily like A and B in the global namespace
namespace test_arena {
class A;
class B;
class Base {
public:
virtual ~Base() {}
virtual A* AsA() { return nullptr; }
virtual B* AsB() { return nullptr; }
};
static int sDtorItemA = 0;
static int sDtorItemB = 0;
class A : public Base {
public:
virtual A* AsA() override { return this; }
explicit A(uint64_t val) : mVal(val) {}
~A() { ++sDtorItemA; }
uint64_t mVal;
};
class B : public Base {
public:
virtual B* AsB() override { return this; }
explicit B(const string& str) : mVal(str) {}
~B() { ++sDtorItemB; }
std::string mVal;
};
struct BigStruct {
uint64_t mVal;
uint8_t data[120];
explicit BigStruct(uint64_t val) : mVal(val) {}
};
void TestArenaAlloc(IterableArena::ArenaType aType)
{
sDtorItemA = 0;
sDtorItemB = 0;
IterableArena arena(aType, 256);
// An empty arena has no items to iterate over.
{
int iterations = 0;
arena.ForEach([&](void* item){
iterations++;
});
ASSERT_EQ(iterations, 0);
}
auto a1 = arena.Alloc<A>(42);
auto b1 = arena.Alloc<B>("Obladi oblada");
auto a2 = arena.Alloc<A>(1337);
auto b2 = arena.Alloc<B>("Yellow submarine");
auto b3 = arena.Alloc<B>("She's got a ticket to ride");
// Alloc returns a non-negative offset if the allocation succeeded.
ASSERT_TRUE(a1 >= 0);
ASSERT_TRUE(a2 >= 0);
ASSERT_TRUE(b1 >= 0);
ASSERT_TRUE(b2 >= 0);
ASSERT_TRUE(b3 >= 0);
ASSERT_TRUE(arena.GetStorage(a1) != nullptr);
ASSERT_TRUE(arena.GetStorage(a2) != nullptr);
ASSERT_TRUE(arena.GetStorage(b1) != nullptr);
ASSERT_TRUE(arena.GetStorage(b2) != nullptr);
ASSERT_TRUE(arena.GetStorage(b3) != nullptr);
ASSERT_TRUE(((Base*)arena.GetStorage(a1))->AsA() != nullptr);
ASSERT_TRUE(((Base*)arena.GetStorage(a2))->AsA() != nullptr);
ASSERT_TRUE(((Base*)arena.GetStorage(b1))->AsB() != nullptr);
ASSERT_TRUE(((Base*)arena.GetStorage(b2))->AsB() != nullptr);
ASSERT_TRUE(((Base*)arena.GetStorage(b3))->AsB() != nullptr);
ASSERT_EQ(((Base*)arena.GetStorage(a1))->AsA()->mVal, (uint64_t)42);
ASSERT_EQ(((Base*)arena.GetStorage(a2))->AsA()->mVal, (uint64_t)1337);
ASSERT_EQ(((Base*)arena.GetStorage(b1))->AsB()->mVal, std::string("Obladi oblada"));
ASSERT_EQ(((Base*)arena.GetStorage(b2))->AsB()->mVal, std::string("Yellow submarine"));
ASSERT_EQ(((Base*)arena.GetStorage(b3))->AsB()->mVal, std::string("She's got a ticket to ride"));
{
int iterations = 0;
arena.ForEach([&](void* item){
iterations++;
});
ASSERT_EQ(iterations, 5);
}
// Typically, running the destructors of the elements in the arena will is done
// manually like this:
arena.ForEach([](void* item){
((Base*)item)->~Base();
});
arena.Clear();
ASSERT_EQ(sDtorItemA, 2);
ASSERT_EQ(sDtorItemB, 3);
// An empty arena has no items to iterate over (we just cleared it).
{
int iterations = 0;
arena.ForEach([&](void* item){
iterations++;
});
ASSERT_EQ(iterations, 0);
}
}
void TestArenaLimit(IterableArena::ArenaType aType, bool aShouldReachLimit)
{
IterableArena arena(aType, 128);
// A non-growable arena should return a negative offset when running out
// of space, without crashing.
// We should not run out of space with a growable arena (unless the os is
// running out of memory but this isn't expected for this test).
bool reachedLimit = false;
for (int i = 0; i < 100; ++i) {
auto offset = arena.Alloc<A>(42);
if (offset < 0) {
reachedLimit = true;
break;
}
}
ASSERT_EQ(reachedLimit, aShouldReachLimit);
}
} // namespace test_arena
using namespace test_arena;
TEST(Moz2D, FixedArena) {
TestArenaAlloc(IterableArena::FIXED_SIZE);
TestArenaLimit(IterableArena::FIXED_SIZE, true);
}
TEST(Moz2D, GrowableArena) {
TestArenaAlloc(IterableArena::GROWABLE);
TestArenaLimit(IterableArena::GROWABLE, false);
IterableArena arena(IterableArena::GROWABLE, 16);
// sizeof(BigStruct) is more than twice the initial capacity, make sure that
// this doesn't blow everything up, since the arena doubles its storage size each
// time it grows (until it finds a size that fits).
auto a = arena.Alloc<BigStruct>(1);
auto b = arena.Alloc<BigStruct>(2);
auto c = arena.Alloc<BigStruct>(3);
// Offsets should also still point to the appropriate values after reallocation.
ASSERT_EQ(((BigStruct*)arena.GetStorage(a))->mVal, (uint64_t)1);
ASSERT_EQ(((BigStruct*)arena.GetStorage(b))->mVal, (uint64_t)2);
ASSERT_EQ(((BigStruct*)arena.GetStorage(c))->mVal, (uint64_t)3);
arena.Clear();
}

Просмотреть файл

@ -0,0 +1,247 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "mozilla/gfx/JobScheduler.h"
#ifndef WIN32
#include <pthread.h>
#include <sched.h>
#endif
#include <stdlib.h>
#include <time.h>
namespace test_scheduler {
using namespace mozilla::gfx;
using namespace mozilla;
// Artificially cause threads to yield randomly in an attempt to make racy
// things more apparent (if any).
void MaybeYieldThread()
{
#ifndef WIN32
if (rand() % 5 == 0) {
sched_yield();
}
#endif
}
/// Used by the TestCommand to check that tasks are processed in the right order.
struct SanityChecker {
std::vector<uint64_t> mAdvancements;
mozilla::gfx::CriticalSection mSection;
explicit SanityChecker(uint64_t aNumCmdBuffers)
{
for (uint32_t i = 0; i < aNumCmdBuffers; ++i) {
mAdvancements.push_back(0);
}
}
virtual void Check(uint64_t aJobId, uint64_t aCmdId)
{
MaybeYieldThread();
CriticalSectionAutoEnter lock(&mSection);
ASSERT_EQ(mAdvancements[aJobId], aCmdId-1);
mAdvancements[aJobId] = aCmdId;
}
};
/// Run checks that are specific to TestSchulerJoin.
struct JoinTestSanityCheck : public SanityChecker {
bool mSpecialJobHasRun;
explicit JoinTestSanityCheck(uint64_t aNumCmdBuffers)
: SanityChecker(aNumCmdBuffers)
, mSpecialJobHasRun(false)
{}
virtual void Check(uint64_t aJobId, uint64_t aCmdId) override
{
// Job 0 is the special task executed when everything is joined after task 1
if (aCmdId == 0) {
ASSERT_FALSE(mSpecialJobHasRun);
mSpecialJobHasRun = true;
for (auto advancement : mAdvancements) {
// Because of the synchronization point (beforeFilter), all
// task buffers should have run task 1 when task 0 is run.
ASSERT_EQ(advancement, (uint32_t)1);
}
} else {
// This check does not apply to task 0.
SanityChecker::Check(aJobId, aCmdId);
}
if (aCmdId == 2) {
ASSERT_TRUE(mSpecialJobHasRun);
}
}
};
class TestJob : public Job
{
public:
TestJob(uint64_t aCmdId, uint64_t aJobId, SanityChecker* aChecker,
SyncObject* aStart, SyncObject* aCompletion)
: Job(aStart, aCompletion, nullptr)
, mCmdId(aCmdId)
, mCmdBufferId(aJobId)
, mSanityChecker(aChecker)
{}
JobStatus Run()
{
MaybeYieldThread();
mSanityChecker->Check(mCmdBufferId, mCmdId);
MaybeYieldThread();
return JobStatus::Complete;
}
uint64_t mCmdId;
uint64_t mCmdBufferId;
SanityChecker* mSanityChecker;
};
/// This test creates aNumCmdBuffers task buffers with sync objects set up
/// so that all tasks will join after command 5 before a task buffer runs
/// a special task (task 0) after which all task buffers fork again.
/// This simulates the kind of scenario where all tiles must join at
/// a certain point to execute, say, a filter, and fork again after the filter
/// has been processed.
/// The main thread is only blocked when waiting for the completion of the entire
/// task stream (it doesn't have to wait at the filter's sync points to orchestrate it).
void TestSchedulerJoin(uint32_t aNumThreads, uint32_t aNumCmdBuffers)
{
JoinTestSanityCheck check(aNumCmdBuffers);
RefPtr<SyncObject> beforeFilter = new SyncObject(aNumCmdBuffers);
RefPtr<SyncObject> afterFilter = new SyncObject();
RefPtr<SyncObject> completion = new SyncObject(aNumCmdBuffers);
for (uint32_t i = 0; i < aNumCmdBuffers; ++i) {
Job* t1 = new TestJob(1, i, &check, nullptr, beforeFilter);
JobScheduler::SubmitJob(t1);
MaybeYieldThread();
}
beforeFilter->FreezePrerequisites();
// This task buffer is executed when all other tasks have joined after task 1
JobScheduler::SubmitJob(
new TestJob(0, 0, &check, beforeFilter, afterFilter)
);
afterFilter->FreezePrerequisites();
for (uint32_t i = 0; i < aNumCmdBuffers; ++i) {
Job* t2 = new TestJob(2, i, &check, afterFilter, completion);
JobScheduler::SubmitJob(t2);
MaybeYieldThread();
}
completion->FreezePrerequisites();
RefPtr<EventObject> waitForCompletion = new EventObject();
auto evtJob = new SetEventJob(waitForCompletion, completion);
JobScheduler::SubmitJob(evtJob);
MaybeYieldThread();
waitForCompletion->Wait();
MaybeYieldThread();
for (auto advancement : check.mAdvancements) {
ASSERT_TRUE(advancement == 2);
}
}
/// This test creates several chains of 10 task, tasks of a given chain are executed
/// sequentially, and chains are exectuted in parallel.
/// This simulates the typical scenario where we want to process sequences of drawing
/// commands for several tiles in parallel.
void TestSchedulerChain(uint32_t aNumThreads, uint32_t aNumCmdBuffers)
{
SanityChecker check(aNumCmdBuffers);
RefPtr<SyncObject> completion = new SyncObject(aNumCmdBuffers);
uint32_t numJobs = 10;
for (uint32_t i = 0; i < aNumCmdBuffers; ++i) {
std::vector<RefPtr<SyncObject>> syncs;
std::vector<Job*> tasks;
syncs.reserve(numJobs);
tasks.reserve(numJobs);
for (uint32_t t = 0; t < numJobs-1; ++t) {
syncs.push_back(new SyncObject());
tasks.push_back(new TestJob(t+1, i, &check, t == 0 ? nullptr
: syncs[t-1].get(),
syncs[t]));
syncs.back()->FreezePrerequisites();
}
tasks.push_back(new TestJob(numJobs, i, &check, syncs.back(), completion));
if (i % 2 == 0) {
// submit half of the tasks in order
for (Job* task : tasks) {
JobScheduler::SubmitJob(task);
MaybeYieldThread();
}
} else {
// ... and submit the other half in reverse order
for (int32_t reverse = numJobs-1; reverse >= 0; --reverse) {
JobScheduler::SubmitJob(tasks[reverse]);
MaybeYieldThread();
}
}
}
completion->FreezePrerequisites();
RefPtr<EventObject> waitForCompletion = new EventObject();
auto evtJob = new SetEventJob(waitForCompletion, completion);
JobScheduler::SubmitJob(evtJob);
MaybeYieldThread();
waitForCompletion->Wait();
for (auto advancement : check.mAdvancements) {
ASSERT_TRUE(advancement == numJobs);
}
}
} // namespace test_scheduler
TEST(Moz2D, JobScheduler_Join) {
srand(time(nullptr));
for (uint32_t threads = 1; threads < 8; ++threads) {
for (uint32_t queues = 1; queues < threads; ++queues) {
for (uint32_t buffers = 1; buffers < 100; buffers += 3) {
mozilla::gfx::JobScheduler::Init(threads, queues);
test_scheduler::TestSchedulerJoin(threads, buffers);
mozilla::gfx::JobScheduler::ShutDown();
}
}
}
}
TEST(Moz2D, JobScheduler_Chain) {
srand(time(nullptr));
for (uint32_t threads = 1; threads < 8; ++threads) {
for (uint32_t queues = 1; queues < threads; ++queues) {
for (uint32_t buffers = 1; buffers < 100; buffers += 3) {
mozilla::gfx::JobScheduler::Init(threads, queues);
test_scheduler::TestSchedulerChain(threads, buffers);
mozilla::gfx::JobScheduler::ShutDown();
}
}
}
}

Просмотреть файл

@ -8,11 +8,13 @@ UNIFIED_SOURCES += [
'gfxSurfaceRefCountTest.cpp',
# Disabled on suspicion of causing bug 904227
#'gfxWordCacheTest.cpp',
'TestArena.cpp',
'TestBufferRotation.cpp',
'TestColorNames.cpp',
'TestCompositor.cpp',
'TestGfxPrefs.cpp',
'TestGfxWidgets.cpp',
'TestJobScheduler.cpp',
'TestLayers.cpp',
'TestMoz2D.cpp',
'TestQcms.cpp',

Просмотреть файл

@ -4,5 +4,5 @@ fuzzy-if(winWidget,175,443) == 611498-1.html 611498-ref.html
skip-if(B2G) fuzzy-if(Android&&AndroidVersion>=15,8,1000) == 709477-1.html 709477-1-ref.html # bug 773482
skip-if(!asyncPan) == 1086723.html 1086723-ref.html
== 853889-1.html 853889-1-ref.html
== 1143303-1.svg pass.svg
fuzzy(100,30) == 1149923.html 1149923-ref.html # use fuzzy due to few distorted pixels caused by border-radius
skip-if(Android) == 1143303-1.svg pass.svg
fuzzy(100,30) == 1149923.html 1149923-ref.html # use fuzzy due to few distorted pixels caused by border-radius

Просмотреть файл

@ -100,13 +100,7 @@ gfxAndroidPlatform::gfxAndroidPlatform()
RegisterStrongMemoryReporter(new FreetypeReporter());
nsCOMPtr<nsIScreenManager> screenMgr = do_GetService("@mozilla.org/gfx/screenmanager;1");
nsCOMPtr<nsIScreen> screen;
screenMgr->GetPrimaryScreen(getter_AddRefs(screen));
mScreenDepth = 24;
screen->GetColorDepth(&mScreenDepth);
mOffscreenFormat = mScreenDepth == 16
mOffscreenFormat = GetScreenDepth() == 16
? gfxImageFormat::RGB16_565
: gfxImageFormat::RGB24;
@ -418,12 +412,6 @@ gfxAndroidPlatform::RequiresLinearZoom()
return gfxPlatform::RequiresLinearZoom();
}
int
gfxAndroidPlatform::GetScreenDepth() const
{
return mScreenDepth;
}
bool
gfxAndroidPlatform::UseAcceleratedSkiaCanvas()
{

Просмотреть файл

@ -80,8 +80,6 @@ public:
FT_Library GetFTLibrary();
virtual int GetScreenDepth() const;
virtual bool CanRenderContentToDataSurface() const override {
return true;
}

Просмотреть файл

@ -52,6 +52,7 @@
#include "gfxGraphiteShaper.h"
#include "gfx2DGlue.h"
#include "gfxGradientCache.h"
#include "gfxUtils.h" // for NextPowerOfTwo
#include "nsUnicodeRange.h"
#include "nsServiceManagerUtils.h"
@ -394,6 +395,7 @@ gfxPlatform::gfxPlatform()
, mAzureCanvasBackendCollector(this, &gfxPlatform::GetAzureBackendInfo)
, mApzSupportCollector(this, &gfxPlatform::GetApzSupportInfo)
, mCompositorBackend(layers::LayersBackend::LAYERS_NONE)
, mScreenDepth(0)
{
mAllowDownloadableFonts = UNINITIALIZED_VALUE;
mFallbackUsesCmaps = UNINITIALIZED_VALUE;
@ -514,6 +516,7 @@ gfxPlatform::Init()
InitLayersAccelerationPrefs();
InitLayersIPC();
gPlatform->PopulateScreenInfo();
gPlatform->ComputeTileSize();
nsresult rv;
@ -1024,45 +1027,49 @@ gfxPlatform::ComputeTileSize()
int32_t w = gfxPrefs::LayersTileWidth();
int32_t h = gfxPrefs::LayersTileHeight();
// TODO We may want to take the screen size into consideration here.
if (gfxPrefs::LayersTilesAdjust()) {
gfx::IntSize screenSize = GetScreenSize();
if (screenSize.width > 0) {
// FIXME: we should probably make sure this is within the max texture size,
// but I think everything should at least support 1024
w = h = std::max(std::min(NextPowerOfTwo(screenSize.width) / 2, 1024), 256);
}
#ifdef MOZ_WIDGET_GONK
int32_t format = android::PIXEL_FORMAT_RGBA_8888;
android::sp<android::GraphicBuffer> alloc =
new android::GraphicBuffer(gfxPrefs::LayersTileWidth(), gfxPrefs::LayersTileHeight(),
format,
android::GraphicBuffer::USAGE_SW_READ_OFTEN |
android::GraphicBuffer::USAGE_SW_WRITE_OFTEN |
android::GraphicBuffer::USAGE_HW_TEXTURE);
new android::GraphicBuffer(w, h, android::PIXEL_FORMAT_RGBA_8888,
android::GraphicBuffer::USAGE_SW_READ_OFTEN |
android::GraphicBuffer::USAGE_SW_WRITE_OFTEN |
android::GraphicBuffer::USAGE_HW_TEXTURE);
if (alloc.get()) {
w = alloc->getStride(); // We want the tiles to be gralloc stride aligned.
// No need to adjust the height here.
}
#endif
}
#ifdef XP_MACOSX
// Use double sized tiles for HiDPI screens.
nsCOMPtr<nsIScreenManager> screenManager =
do_GetService("@mozilla.org/gfx/screenmanager;1");
if (screenManager) {
nsCOMPtr<nsIScreen> primaryScreen;
screenManager->GetPrimaryScreen(getter_AddRefs(primaryScreen));
double scaleFactor = 1.0;
if (primaryScreen) {
primaryScreen->GetContentsScaleFactor(&scaleFactor);
}
if (scaleFactor > 1.0) {
w *= 2;
h *= 2;
}
}
#endif
SetTileSize(w, h);
}
void
gfxPlatform::PopulateScreenInfo()
{
nsCOMPtr<nsIScreenManager> manager = do_GetService("@mozilla.org/gfx/screenmanager;1");
MOZ_ASSERT(manager, "failed to get nsIScreenManager");
nsCOMPtr<nsIScreen> screen;
manager->GetPrimaryScreen(getter_AddRefs(screen));
if (!screen) {
// This can happen in xpcshell, for instance
return;
}
screen->GetColorDepth(&mScreenDepth);
int left, top;
screen->GetRect(&left, &top, &mScreenSize.width, &mScreenSize.height);
}
bool
gfxPlatform::SupportsAzureContentForDrawTarget(DrawTarget* aTarget)
{
@ -2093,13 +2100,6 @@ gfxPlatform::GetLog(eGfxLog aWhichLog)
return nullptr;
}
int
gfxPlatform::GetScreenDepth() const
{
NS_WARNING("GetScreenDepth not implemented on this platform -- returning 0!");
return 0;
}
mozilla::gfx::SurfaceFormat
gfxPlatform::Optimal2DFormatForContent(gfxContentType aContent)
{

Просмотреть файл

@ -568,7 +568,8 @@ public:
*/
static PRLogModuleInfo* GetLog(eGfxLog aWhichLog);
virtual int GetScreenDepth() const;
int GetScreenDepth() const { return mScreenDepth; }
mozilla::gfx::IntSize GetScreenSize() const { return mScreenSize; }
/**
* Return the layer debugging options to use browser-wide.
@ -777,6 +778,11 @@ private:
*/
void ComputeTileSize();
/**
* This uses nsIScreenManager to determine the screen size and color depth
*/
void PopulateScreenInfo();
nsRefPtr<gfxASurface> mScreenReferenceSurface;
nsTArray<uint32_t> mCJKPrefLangs;
nsCOMPtr<nsIObserver> mSRGBOverrideObserver;
@ -804,6 +810,9 @@ private:
// Backend that we are compositing with. NONE, if no compositor has been
// created yet.
mozilla::layers::LayersBackend mCompositorBackend;
int32_t mScreenDepth;
mozilla::gfx::IntSize mScreenSize;
};
#endif /* GFX_PLATFORM_H */

Просмотреть файл

@ -363,24 +363,6 @@ gfxPlatformGtk::GetOffscreenFormat()
return gfxImageFormat::RGB24;
}
static int sDepth = 0;
int
gfxPlatformGtk::GetScreenDepth() const
{
if (!sDepth) {
GdkScreen *screen = gdk_screen_get_default();
if (screen) {
sDepth = gdk_visual_get_depth(gdk_visual_get_system());
} else {
sDepth = 24;
}
}
return sDepth;
}
void
gfxPlatformGtk::GetPlatformCMSOutputProfile(void *&mem, size_t &size)
{

Просмотреть файл

@ -122,8 +122,6 @@ public:
virtual gfxImageFormat GetOffscreenFormat() override;
virtual int GetScreenDepth() const override;
bool SupportsApzWheelInput() const override {
return true;
}

Просмотреть файл

@ -355,6 +355,7 @@ private:
DECL_GFX_PREF(Live, "layers.transaction.warning-ms", LayerTransactionWarning, uint32_t, 200);
DECL_GFX_PREF(Once, "layers.uniformity-info", UniformityInfo, bool, false);
DECL_GFX_PREF(Once, "layers.use-image-offscreen-surfaces", UseImageOffscreenSurfaces, bool, false);
DECL_GFX_PREF(Live, "layers.single-tile.enabled", LayersSingleTileEnabled, bool, true);
DECL_GFX_PREF(Live, "layout.css.scroll-behavior.damping-ratio", ScrollBehaviorDampingRatio, float, 1.0f);
DECL_GFX_PREF(Live, "layout.css.scroll-behavior.enabled", ScrollBehaviorEnabled, bool, false);

Просмотреть файл

@ -53,8 +53,8 @@ gfxQtPlatform::gfxQtPlatform()
if (!sFontconfigUtils)
sFontconfigUtils = gfxFontconfigUtils::GetFontconfigUtils();
mScreenDepth = qApp->primaryScreen()->depth();
if (mScreenDepth == 16) {
int32_t depth = GetScreenDepth();
if (depth == 16) {
sOffscreenFormat = gfxImageFormat::RGB16_565;
}
uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO) | BackendTypeBit(BackendType::SKIA);
@ -195,12 +195,6 @@ gfxQtPlatform::GetOffscreenFormat()
return sOffscreenFormat;
}
int
gfxQtPlatform::GetScreenDepth() const
{
return mScreenDepth;
}
already_AddRefed<ScaledFont>
gfxQtPlatform::GetScaledFontForFont(DrawTarget* aTarget, gfxFont* aFont)
{

Просмотреть файл

@ -82,8 +82,6 @@ public:
static Screen* GetXScreen(QWindow* aWindow = 0);
#endif
virtual int GetScreenDepth() const override;
bool AccelerateLayersByDefault() override {
return true;
}

Просмотреть файл

@ -1592,26 +1592,6 @@ gfxWindowsPlatform::IsOptimus()
return knowIsOptimus;
}
int
gfxWindowsPlatform::GetScreenDepth() const
{
// if the system doesn't have all displays with the same
// pixel format, just return 24 and move on with life.
if (!GetSystemMetrics(SM_SAMEDISPLAYFORMAT))
return 24;
HDC hdc = GetDC(nullptr);
if (!hdc)
return 24;
int depth = GetDeviceCaps(hdc, BITSPIXEL) *
GetDeviceCaps(hdc, PLANES);
ReleaseDC(nullptr, hdc);
return depth;
}
IDXGIAdapter1*
gfxWindowsPlatform::GetDXGIAdapter()
{

Просмотреть файл

@ -140,8 +140,6 @@ public:
RENDER_MODE_MAX
};
int GetScreenDepth() const;
RenderMode GetRenderMode() { return mRenderMode; }
void SetRenderMode(RenderMode rmode) { mRenderMode = rmode; }

Просмотреть файл

@ -5140,9 +5140,9 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
+ self.invokeRecvHandler(md, implicit=0)
+ [ Whitespace.NL ]
+ saveIdStmts
+ self.dtorEpilogue(md, md.actorDecl().var())
+ [ Whitespace.NL ]
+ self.makeReply(md, errfnRecv, routingId=idvar)
+ [ Whitespace.NL ]
+ self.dtorEpilogue(md, md.actorDecl().var())
+ [ Whitespace.NL,
StmtReturn(_Result.Processed) ])

Просмотреть файл

@ -0,0 +1,8 @@
try {
evalInWorker(`
function f() { f(); }
try { f(); } catch(e) {}
`);
} catch(e) {
assertEq(e.toString().includes("--no-threads"), true);
}

Просмотреть файл

@ -2165,6 +2165,19 @@ IonBuilder::inlineIsPossiblyWrappedTypedArray(CallInfo& callInfo)
return inlineIsTypedArrayHelper(callInfo, AllowWrappedTypedArrays);
}
static bool
IsTypedArrayObject(CompilerConstraintList* constraints, MDefinition* def)
{
MOZ_ASSERT(def->type() == MIRType_Object);
TemporaryTypeSet* types = def->resultTypeSet();
if (!types)
return false;
return types->forAllClasses(constraints, IsTypedArrayClass) ==
TemporaryTypeSet::ForAllResult::ALL_TRUE;
}
IonBuilder::InliningStatus
IonBuilder::inlineTypedArrayLength(CallInfo& callInfo)
{
@ -2175,8 +2188,10 @@ IonBuilder::inlineTypedArrayLength(CallInfo& callInfo)
if (getInlineReturnType() != MIRType_Int32)
return InliningStatus_NotInlined;
// We assume that when calling this function we always
// have a TypedArray. The native asserts that as well.
// Note that the argument we see here is not necessarily a typed array.
// If it's not, this call should be unreachable though.
if (!IsTypedArrayObject(constraints(), callInfo.getArg(0)))
return InliningStatus_NotInlined;
MInstruction* length = addTypedArrayLength(callInfo.getArg(0));
current->push(length);
@ -2210,19 +2225,10 @@ IonBuilder::inlineSetDisjointTypedElements(CallInfo& callInfo)
// Only attempt to optimize if |target| and |sourceTypedArray| are both
// definitely typed arrays. (The former always is. The latter is not,
// necessarily, because of wrappers.)
MDefinition* arrays[] = { target, sourceTypedArray };
for (MDefinition* def : arrays) {
TemporaryTypeSet* types = def->resultTypeSet();
if (!types)
return InliningStatus_NotInlined;
if (types->forAllClasses(constraints(), IsTypedArrayClass) !=
TemporaryTypeSet::ForAllResult::ALL_TRUE)
{
return InliningStatus_NotInlined;
}
if (!IsTypedArrayObject(constraints(), target) ||
!IsTypedArrayObject(constraints(), sourceTypedArray))
{
return InliningStatus_NotInlined;
}
auto sets = MSetDisjointTypedElements::New(alloc(), target, targetOffset, sourceTypedArray);

Просмотреть файл

@ -153,7 +153,7 @@ MacroAssembler::guardObjectType(Register obj, const TypeSet* types,
{
MOZ_ASSERT(!types->unknown());
MOZ_ASSERT(!types->hasType(TypeSet::AnyObjectType()));
MOZ_ASSERT(scratch != InvalidReg);
MOZ_ASSERT_IF(types->getObjectCount() > 0, scratch != InvalidReg);
// Note: this method elides read barriers on values read from type sets, as
// this may be called off the main thread during Ion compilation. This is
@ -215,7 +215,6 @@ MacroAssembler::guardObjectType(Register obj, const TypeSet* types,
lastBranch.emit(*this);
bind(&matched);
return;
}
template void MacroAssembler::guardTypeSet(const Address& address, const TypeSet* types,

Просмотреть файл

@ -2604,7 +2604,8 @@ EvalInWorker(JSContext* cx, unsigned argc, Value* vp)
return false;
PRThread* thread = PR_CreateThread(PR_USER_THREAD, WorkerMain, input,
PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD,
gMaxStackSize + 128 * 1024);
if (!thread || !workerThreads.append(thread))
return false;
@ -6129,6 +6130,8 @@ SetWorkerRuntimeOptions(JSRuntime* rt)
if (*gZealStr)
rt->gc.parseAndSetZeal(gZealStr);
#endif
JS_SetNativeStackQuota(rt, gMaxStackSize);
}
static int

Просмотреть файл

@ -335,7 +335,7 @@ TemporaryTypeSet::TemporaryTypeSet(LifoAlloc* alloc, Type type)
}
bool
TypeSet::mightBeMIRType(jit::MIRType type)
TypeSet::mightBeMIRType(jit::MIRType type) const
{
if (unknown())
return true;

Просмотреть файл

@ -475,7 +475,7 @@ class TypeSet
}
/* Whether any values in this set might have the specified type. */
bool mightBeMIRType(jit::MIRType type);
bool mightBeMIRType(jit::MIRType type) const;
/*
* Get whether this type set is known to be a subset of other.

Просмотреть файл

@ -2094,9 +2094,6 @@ ContainerState::GetLayerCreationHint(const nsIFrame* aAnimatedGeometryRoot)
{
// Check whether the layer will be scrollable. This is used as a hint to
// influence whether tiled layers are used or not.
if (mParameters.mInLowPrecisionDisplayPort) {
return LayerManager::SCROLLABLE;
}
// Check whether there's any active scroll frame on the animated geometry
// root chain.

Просмотреть файл

@ -69,7 +69,7 @@ fuzzy(3,7860) fuzzy-if(cocoaWidget,5,89041) fuzzy-if(azureSkiaGL,4,90000) == rad
== radial-position-1a.html radial-position-1-ref.html
fuzzy-if(cocoaWidget,1,28) fuzzy-if(winWidget,1,18) == radial-position-1b.html radial-position-1-ref.html
fuzzy-if(cocoaWidget,4,22317) fuzzy-if(Android,8,771) == radial-shape-closest-corner-1a.html radial-shape-closest-corner-1-ref.html
fuzzy(1,238) fuzzy-if(cocoaWidget,4,22608) fuzzy-if((/^Windows\x20NT\x2010\.0/.test(http.oscpu)||/^Windows\x20NT\x206\./.test(http.oscpu))&&d2d,1,336) fuzzy-if(Android,8,787) == radial-shape-closest-corner-1b.html radial-shape-closest-corner-1-ref.html
fuzzy(1,238) fuzzy-if(cocoaWidget,4,22608) fuzzy-if((/^Windows\x20NT\x2010\.0/.test(http.oscpu)||/^Windows\x20NT\x206\./.test(http.oscpu))&&d2d,1,336) fuzzy-if(Android,8,787) fuzzy-if(B2G,1,242) == radial-shape-closest-corner-1b.html radial-shape-closest-corner-1-ref.html
fuzzy-if(azureQuartz,2,41171) fuzzy-if(Android,8,771) == radial-shape-closest-corner-1c.html radial-shape-closest-corner-1-ref.html
fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)||/^Windows\x20NT\x206\.2/.test(http.oscpu),1,5) fuzzy-if(Android,17,3880) == radial-shape-closest-side-1a.html radial-shape-closest-side-1-ref.html
fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)||/^Windows\x20NT\x206\.2/.test(http.oscpu),1,5) fuzzy-if(Android,17,3880) == radial-shape-closest-side-1b.html radial-shape-closest-side-1-ref.html

Просмотреть файл

@ -33,7 +33,7 @@ include svg-integration/reftest.list
== clip-02a.svg clip-02-ref.svg
== clip-02b.svg clip-02-ref.svg
== clipPath-advanced-01.svg pass.svg
fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)||/^Windows\x20NT\x206\.[12]/.test(http.oscpu),1,5) fuzzy-if(azureQuartz,1,6) fuzzy-if(OSX,1,2) == clipPath-and-shape-rendering-01.svg clipPath-and-shape-rendering-01-ref.svg # bug 614840
fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)||/^Windows\x20NT\x206\.[12]/.test(http.oscpu),1,5) fuzzy-if(azureQuartz,1,6) fuzzy-if(OSX,1,6) == clipPath-and-shape-rendering-01.svg clipPath-and-shape-rendering-01-ref.svg # bug 614840
== clipPath-and-transform-01.svg pass.svg
== clipPath-basic-01.svg pass.svg
== clipPath-basic-02.svg pass.svg

Просмотреть файл

@ -4,7 +4,7 @@ fuzzy-if(Android,16,244) skip-if(B2G||Mulet) HTTP(..) == marker-basic.html marke
skip-if(B2G||Mulet) HTTP(..) == marker-string.html marker-string-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
skip-if(Android||B2G) HTTP(..) == bidi-simple.html bidi-simple-ref.html # Fails on Android due to anti-aliasing
skip-if(!gtkWidget) fuzzy-if(gtkWidget,2,289) HTTP(..) == bidi-simple-scrolled.html bidi-simple-scrolled-ref.html # Fails on Windows and OSX due to anti-aliasing
skip-if(B2G||Mulet) fuzzy-if(Android&&AndroidVersion<15,9,2545) fuzzy-if(Android&&AndroidVersion>=15,24,4000) fuzzy-if(cocoaWidget,1,40) fuzzy-if(asyncPan&&!layersGPUAccelerated,102,1770) HTTP(..) == scroll-rounding.html scroll-rounding-ref.html # bug 760264 # Initial mulet triage: parity with B2G/B2G Desktop
skip-if(B2G||Mulet) fuzzy-if(Android&&AndroidVersion<15,206,41) fuzzy-if(Android&&AndroidVersion>=15,24,4000) fuzzy-if(cocoaWidget,1,40) fuzzy-if(asyncPan&&!layersGPUAccelerated,102,1770) HTTP(..) == scroll-rounding.html scroll-rounding-ref.html # bug 760264 # Initial mulet triage: parity with B2G/B2G Desktop
fuzzy-if(OSX==1008,1,1) HTTP(..) == anonymous-block.html anonymous-block-ref.html
skip-if(B2G||Mulet) HTTP(..) == false-marker-overlap.html false-marker-overlap-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
HTTP(..) == visibility-hidden.html visibility-hidden-ref.html

Просмотреть файл

@ -262,6 +262,15 @@ nsCSSCompressedDataBlock::MapRuleInfoInto(nsRuleData *aRuleData) const
nsCSSValue* target = aRuleData->ValueFor(iProp);
if (target->GetUnit() == eCSSUnit_Null) {
const nsCSSValue *val = ValueAtIndex(i);
// In order for variable resolution to have the right information
// about the stylesheet level of a value, that level needs to be
// stored on the token stream. We can't do that at creation time
// because the CSS parser (which creates the object) has no idea
// about the stylesheet level, so we do it here instead, where
// the rule walking will have just updated aRuleData.
if (val->GetUnit() == eCSSUnit_TokenStream) {
val->GetTokenStreamValue()->mLevel = aRuleData->mLevel;
}
MapSinglePropertyInto(iProp, val, target, aRuleData);
}
}

Просмотреть файл

@ -23,6 +23,7 @@
#include "nsPresContext.h"
#include "nsStyleUtil.h"
#include "nsDeviceContext.h"
#include "nsStyleSet.h"
using namespace mozilla;
@ -2537,6 +2538,7 @@ nsCSSValueGradient::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) con
nsCSSValueTokenStream::nsCSSValueTokenStream()
: mPropertyID(eCSSProperty_UNKNOWN)
, mShorthandPropertyID(eCSSProperty_UNKNOWN)
, mLevel(nsStyleSet::eSheetTypeCount)
{
MOZ_COUNT_CTOR(nsCSSValueTokenStream);
}

Просмотреть файл

@ -1541,6 +1541,7 @@ public:
// mozilla::CSSStyleSheet* mSheet;
uint32_t mLineNumber;
uint32_t mLineOffset;
uint16_t mLevel; // an nsStyleSet::sheetType
private:
nsCSSValueTokenStream(const nsCSSValueTokenStream& aOther) = delete;

Просмотреть файл

@ -2180,6 +2180,12 @@ nsRuleNode::ResolveVariableReferences(const nsStyleStructID aSID,
&aContext->StyleVariables()->mVariables;
nsCSSValueTokenStream* tokenStream = value->GetTokenStreamValue();
MOZ_ASSERT(tokenStream->mLevel != nsStyleSet::eSheetTypeCount,
"Token stream should have a defined level");
AutoRestore<uint16_t> saveLevel(aRuleData->mLevel);
aRuleData->mLevel = tokenStream->mLevel;
// Note that ParsePropertyWithVariableReferences relies on the fact
// that the nsCSSValue in aRuleData for the property we are re-parsing
// is still the token stream value. When

Просмотреть файл

@ -260,6 +260,11 @@ class RemoteReftest(RefTest):
# Disable skia-gl: see bug 907351
prefs["gfx.canvas.azure.accelerated"] = False
# Debug reftests have problems with large tile size on pandaboards
if mozinfo.info['debug'] and self._devicemanager.shellCheckOutput(['getprop', 'ro.product.name']) == 'pandaboard':
prefs["layers.tiles.adjust"] = False
prefs["layers.single-tile.enabled"] = False
# Set the extra prefs.
profile.set_preferences(prefs)
@ -310,7 +315,8 @@ class RemoteReftest(RefTest):
def runApp(self, profile, binary, cmdargs, env,
timeout=None, debuggerInfo=None,
symbolsPath=None, options=None):
symbolsPath=None, options=None,
valgrindPath=None, valgrindArgs=None, valgrindSuppFiles=None):
status = self.automation.runApp(None, env,
binary,
profile.profile,

Просмотреть файл

@ -582,7 +582,8 @@ class RefTest(object):
def runApp(self, profile, binary, cmdargs, env,
timeout=None, debuggerInfo=None,
symbolsPath=None, options=None):
symbolsPath=None, options=None,
valgrindPath=None, valgrindArgs=None, valgrindSuppFiles=None):
def timeoutHandler():
self.handleTimeout(

Просмотреть файл

@ -311,7 +311,8 @@ class B2GRemoteReftest(RefTest):
def runApp(self, profile, binary, cmdargs, env,
timeout=None, debuggerInfo=None,
symbolsPath=None, options=None):
symbolsPath=None, options=None,
valgrindPath=None, valgrindArgs=None, valgrindSuppFiles=None):
status = self.automation.runApp(None, env,
binary,
profile.profile,

Просмотреть файл

@ -13,10 +13,64 @@
using namespace mozilla;
using namespace mp4_demuxer;
class TestStream : public Stream
{
public:
TestStream(const uint8_t* aBuffer, size_t aSize)
: mHighestSuccessfulEndOffset(0)
, mBuffer(aBuffer)
, mSize(aSize)
{
}
bool ReadAt(int64_t aOffset, void* aData, size_t aLength,
size_t* aBytesRead) override
{
if (aOffset < 0 || aOffset > static_cast<int64_t>(mSize)) {
return false;
}
// After the test, 0 <= aOffset <= mSize <= SIZE_MAX, so it's safe to cast to size_t.
size_t offset = static_cast<size_t>(aOffset);
// Don't read past the end (but it's not an error to try).
if (aLength > mSize - offset) {
aLength = mSize - offset;
}
// Now, 0 <= offset <= offset + aLength <= mSize <= SIZE_MAX.
*aBytesRead = aLength;
memcpy(aData, mBuffer + offset, aLength);
if (mHighestSuccessfulEndOffset < offset + aLength)
{
mHighestSuccessfulEndOffset = offset + aLength;
}
return true;
}
bool CachedReadAt(int64_t aOffset, void* aData, size_t aLength,
size_t* aBytesRead) override
{
return ReadAt(aOffset, aData, aLength, aBytesRead);
}
bool Length(int64_t* aLength) override
{
*aLength = mSize;
return true;
}
void DiscardBefore(int64_t aOffset) override
{
}
// Offset past the last character ever read. 0 when nothing read yet.
size_t mHighestSuccessfulEndOffset;
protected:
virtual ~TestStream()
{
}
const uint8_t* mBuffer;
size_t mSize;
};
TEST(stagefright_MP4Metadata, EmptyStream)
{
nsRefPtr<MediaByteBuffer> buffer = new MediaByteBuffer(0);
nsRefPtr<BufferStream> stream = new BufferStream(buffer);
nsRefPtr<Stream> stream = new TestStream(nullptr, 0);
EXPECT_FALSE(MP4Metadata::HasCompleteMetadata(stream));
nsRefPtr<MediaByteBuffer> metadataBuffer = MP4Metadata::Metadata(stream);
@ -40,8 +94,7 @@ TEST(stagefright_MP4Metadata, EmptyStream)
TEST(stagefright_MoofParser, EmptyStream)
{
nsRefPtr<MediaByteBuffer> buffer = new MediaByteBuffer(0);
nsRefPtr<BufferStream> stream = new BufferStream(buffer);
nsRefPtr<Stream> stream = new TestStream(nullptr, 0);
Monitor monitor("MP4Metadata::gtest");
MonitorAutoLock mon(monitor);
@ -64,344 +117,209 @@ TEST(stagefright_MoofParser, EmptyStream)
EXPECT_TRUE(parser.FirstCompleteMediaHeader().IsNull());
}
uint8_t test_case_mp4[] = {
0x00, 0x00, 0x00, 0x20, 'f', 't', 'y', 'p', 'i', 's', 'o', 'm',
0x00, 0x00, 0x02, 0x00, 'i', 's', 'o', 'm', 'i', 's', 'o', '2',
'a', 'v', 'c', '1', 'm', 'p', '4', '1', 0x00, 0x00, 0x00, 0x08,
'f', 'r', 'e', 'e', 0x00, 0x00, 0x07, 0xcc, 'm', 'd', 'a', 't',
0x00, 0x00, 0x02, 0xaf, 0x06, 0x05, 0xff, 0xff, 0xab, 0xdc, 0x45, 0xe9,
0xbd, 0xe6, 0xd9, 0x48, 0xb7, 0x96, 0x2c, 0xd8, 0x20, 0xd9, 0x23, 0xee,
0xef, 0x78, 0x32, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x63, 0x6f, 0x72, 0x65,
0x20, 0x31, 0x34, 0x36, 0x20, 0x72, 0x32, 0x35, 0x33, 0x38, 0x20, 0x31,
0x32, 0x31, 0x33, 0x39, 0x36, 0x63, 0x20, 0x2d, 0x20, 0x48, 0x2e, 0x32,
0x36, 0x34, 0x2f, 0x4d, 0x50, 0x45, 0x47, 0x2d, 0x34, 0x20, 0x41, 0x56,
0x43, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x63, 0x20, 0x2d, 0x20, 0x43, 0x6f,
0x70, 0x79, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x32, 0x30, 0x30, 0x33, 0x2d,
0x32, 0x30, 0x31, 0x35, 0x20, 0x2d, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a,
0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x6c,
0x61, 0x6e, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x78, 0x32, 0x36, 0x34, 0x2e,
0x68, 0x74, 0x6d, 0x6c, 0x20, 0x2d, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f,
0x6e, 0x73, 0x3a, 0x20, 0x63, 0x61, 0x62, 0x61, 0x63, 0x3d, 0x31, 0x20,
0x72, 0x65, 0x66, 0x3d, 0x33, 0x20, 0x64, 0x65, 0x62, 0x6c, 0x6f, 0x63,
0x6b, 0x3d, 0x31, 0x3a, 0x30, 0x3a, 0x30, 0x20, 0x61, 0x6e, 0x61, 0x6c,
0x79, 0x73, 0x65, 0x3d, 0x30, 0x78, 0x33, 0x3a, 0x30, 0x78, 0x31, 0x31,
0x33, 0x20, 0x6d, 0x65, 0x3d, 0x68, 0x65, 0x78, 0x20, 0x73, 0x75, 0x62,
0x6d, 0x65, 0x3d, 0x37, 0x20, 0x70, 0x73, 0x79, 0x3d, 0x31, 0x20, 0x70,
0x73, 0x79, 0x5f, 0x72, 0x64, 0x3d, 0x31, 0x2e, 0x30, 0x30, 0x3a, 0x30,
0x2e, 0x30, 0x30, 0x20, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x72, 0x65,
0x66, 0x3d, 0x31, 0x20, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65,
0x3d, 0x31, 0x36, 0x20, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x5f, 0x6d,
0x65, 0x3d, 0x31, 0x20, 0x74, 0x72, 0x65, 0x6c, 0x6c, 0x69, 0x73, 0x3d,
0x31, 0x20, 0x38, 0x78, 0x38, 0x64, 0x63, 0x74, 0x3d, 0x31, 0x20, 0x63,
0x71, 0x6d, 0x3d, 0x30, 0x20, 0x64, 0x65, 0x61, 0x64, 0x7a, 0x6f, 0x6e,
0x65, 0x3d, 0x32, 0x31, 0x2c, 0x31, 0x31, 0x20, 0x66, 0x61, 0x73, 0x74,
0x5f, 0x70, 0x73, 0x6b, 0x69, 0x70, 0x3d, 0x31, 0x20, 0x63, 0x68, 0x72,
0x6f, 0x6d, 0x61, 0x5f, 0x71, 0x70, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65,
0x74, 0x3d, 0x2d, 0x32, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73,
0x3d, 0x31, 0x32, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x61, 0x68, 0x65, 0x61,
0x64, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d, 0x31, 0x20,
0x73, 0x6c, 0x69, 0x63, 0x65, 0x64, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61,
0x64, 0x73, 0x3d, 0x30, 0x20, 0x6e, 0x72, 0x3d, 0x30, 0x20, 0x64, 0x65,
0x63, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x3d, 0x31, 0x20, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x6c, 0x61, 0x63, 0x65, 0x64, 0x3d, 0x30, 0x20, 0x62, 0x6c,
0x75, 0x72, 0x61, 0x79, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x3d,
0x30, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x65,
0x64, 0x5f, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x3d, 0x30, 0x20, 0x62, 0x66,
0x72, 0x61, 0x6d, 0x65, 0x73, 0x3d, 0x33, 0x20, 0x62, 0x5f, 0x70, 0x79,
0x72, 0x61, 0x6d, 0x69, 0x64, 0x3d, 0x32, 0x20, 0x62, 0x5f, 0x61, 0x64,
0x61, 0x70, 0x74, 0x3d, 0x31, 0x20, 0x62, 0x5f, 0x62, 0x69, 0x61, 0x73,
0x3d, 0x30, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x3d, 0x31, 0x20,
0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x62, 0x3d, 0x31, 0x20, 0x6f, 0x70,
0x65, 0x6e, 0x5f, 0x67, 0x6f, 0x70, 0x3d, 0x30, 0x20, 0x77, 0x65, 0x69,
0x67, 0x68, 0x74, 0x70, 0x3d, 0x32, 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e,
0x74, 0x3d, 0x32, 0x35, 0x30, 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74,
0x5f, 0x6d, 0x69, 0x6e, 0x3d, 0x32, 0x35, 0x20, 0x73, 0x63, 0x65, 0x6e,
0x65, 0x63, 0x75, 0x74, 0x3d, 0x34, 0x30, 0x20, 0x69, 0x6e, 0x74, 0x72,
0x61, 0x5f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x3d, 0x30, 0x20,
0x72, 0x63, 0x5f, 0x6c, 0x6f, 0x6f, 0x6b, 0x61, 0x68, 0x65, 0x61, 0x64,
0x3d, 0x34, 0x30, 0x20, 0x72, 0x63, 0x3d, 0x63, 0x72, 0x66, 0x20, 0x6d,
0x62, 0x74, 0x72, 0x65, 0x65, 0x3d, 0x31, 0x20, 0x63, 0x72, 0x66, 0x3d,
0x32, 0x33, 0x2e, 0x30, 0x20, 0x71, 0x63, 0x6f, 0x6d, 0x70, 0x3d, 0x30,
0x2e, 0x36, 0x30, 0x20, 0x71, 0x70, 0x6d, 0x69, 0x6e, 0x3d, 0x30, 0x20,
0x71, 0x70, 0x6d, 0x61, 0x78, 0x3d, 0x36, 0x39, 0x20, 0x71, 0x70, 0x73,
0x74, 0x65, 0x70, 0x3d, 0x34, 0x20, 0x69, 0x70, 0x5f, 0x72, 0x61, 0x74,
0x69, 0x6f, 0x3d, 0x31, 0x2e, 0x34, 0x30, 0x20, 0x61, 0x71, 0x3d, 0x31,
0x3a, 0x31, 0x2e, 0x30, 0x30, 0x00, 0x80, 0x00, 0x00, 0x04, 0xfb, 0x65,
0x88, 0x84, 0x00, 0x25, 0xff, 0xd7, 0x2b, 0x47, 0x53, 0x79, 0x61, 0xc8,
0xdf, 0x88, 0x8e, 0xdc, 0xef, 0xc0, 0x85, 0x00, 0x99, 0x10, 0x58, 0x46,
0xf1, 0x28, 0xf2, 0x0c, 0xfc, 0x41, 0x82, 0xa0, 0x45, 0x81, 0xb5, 0x7e,
0xcc, 0x27, 0x62, 0x2f, 0xac, 0x5c, 0xe8, 0xa9, 0xcf, 0x0e, 0x54, 0x17,
0x68, 0x13, 0x48, 0x0d, 0xa1, 0x5d, 0xd1, 0x8b, 0xdf, 0x6e, 0xd0, 0xc8,
0x8f, 0xb3, 0xc2, 0xaf, 0xd1, 0xdc, 0x98, 0x74, 0x7e, 0x0f, 0x69, 0xbe,
0x54, 0xa2, 0x83, 0x4a, 0xc4, 0xf1, 0x79, 0x35, 0x60, 0x10, 0xb4, 0xfb,
0xa7, 0x52, 0x3a, 0x99, 0x5e, 0x39, 0x0d, 0x04, 0xad, 0x24, 0x49, 0xfd,
0xbe, 0x5f, 0x49, 0xdf, 0x36, 0x91, 0x1f, 0x62, 0x7f, 0x00, 0xa1, 0xba,
0x3c, 0xe9, 0xf3, 0x38, 0x08, 0xb7, 0x87, 0x9a, 0x11, 0xae, 0x5f, 0xf2,
0x66, 0x14, 0xc3, 0xa9, 0xf0, 0x76, 0x69, 0x5f, 0x1d, 0x20, 0x19, 0xc0,
0x50, 0x94, 0xb4, 0x7b, 0x44, 0xaf, 0x21, 0x24, 0x80, 0x3f, 0x2a, 0x81,
0x1b, 0xe4, 0xae, 0xfe, 0xb0, 0x99, 0xd3, 0xa5, 0x1d, 0x3f, 0x27, 0x0c,
0x61, 0x54, 0x4d, 0x39, 0x8b, 0x1d, 0x12, 0x65, 0xe1, 0xb3, 0x4d, 0xc1,
0x9d, 0xef, 0xfe, 0x48, 0xc6, 0xc9, 0xf4, 0x9a, 0x09, 0xc1, 0x3c, 0x5c,
0xe3, 0xa1, 0x16, 0xec, 0x83, 0xb6, 0xb9, 0xc2, 0x94, 0x64, 0xc9, 0xf2,
0x91, 0x51, 0x4c, 0xaf, 0xe5, 0xc3, 0x8a, 0x99, 0x1b, 0x22, 0x86, 0x55,
0x70, 0xbf, 0xc7, 0x99, 0xc3, 0x07, 0x15, 0xd6, 0xdb, 0x27, 0x46, 0x08,
0x19, 0x7e, 0x0a, 0x9b, 0x82, 0xe0, 0x15, 0x1d, 0x57, 0x8a, 0x2c, 0xc4,
0x05, 0xf8, 0x09, 0xfb, 0x3c, 0x47, 0x4f, 0x55, 0xc6, 0x3e, 0x0b, 0x9d,
0xe5, 0x03, 0x3e, 0xc9, 0x4c, 0xeb, 0xa2, 0x1b, 0x04, 0xfa, 0x99, 0x44,
0x29, 0x06, 0x2e, 0x57, 0x61, 0xa3, 0x63, 0x00, 0x53, 0x08, 0x20, 0xcb,
0x15, 0x50, 0x95, 0x13, 0x24, 0x63, 0xc9, 0x4f, 0xc0, 0xb0, 0x88, 0xe6,
0x38, 0xef, 0x96, 0x99, 0xd2, 0x96, 0x47, 0xc7, 0xc8, 0x61, 0xac, 0xcc,
0x24, 0x0a, 0x1f, 0x79, 0xce, 0x91, 0x88, 0xf5, 0xe9, 0xe2, 0x2d, 0x88,
0xaf, 0x37, 0xca, 0xa3, 0x90, 0x5b, 0x83, 0x1a, 0x9c, 0x92, 0xda, 0x33,
0x69, 0xe6, 0x7c, 0x14, 0xa6, 0x85, 0x00, 0x42, 0x46, 0x51, 0xcf, 0x50,
0xed, 0x34, 0xe8, 0x8e, 0xb5, 0x78, 0xeb, 0x09, 0x37, 0x6e, 0x82, 0xea,
0x7b, 0xe1, 0xfd, 0x45, 0x61, 0x77, 0x14, 0x6a, 0x75, 0xbf, 0xb8, 0x13,
0x10, 0xc0, 0x08, 0xca, 0x1c, 0x51, 0xc7, 0x3a, 0x0a, 0x2b, 0x89, 0x5d,
0x3c, 0x8d, 0xf4, 0x11, 0xc0, 0x3a, 0x90, 0x02, 0xc0, 0x0c, 0xe8, 0x59,
0x11, 0xac, 0xe0, 0x5d, 0xe2, 0x95, 0x78, 0x09, 0xd5, 0xfa, 0x26, 0xf5,
0x23, 0x30, 0x0a, 0xfd, 0x54, 0x0b, 0x73, 0x1c, 0x81, 0x22, 0x48, 0x8b,
0x79, 0xe0, 0xf9, 0xe8, 0xde, 0x7a, 0x79, 0xba, 0xef, 0xdd, 0x5a, 0xd6,
0x44, 0xb7, 0xa8, 0x41, 0xd0, 0xa1, 0xeb, 0x45, 0xf8, 0x4d, 0x7f, 0x97,
0x48, 0x82, 0xd2, 0x81, 0x61, 0x08, 0x15, 0x9e, 0xfe, 0x78, 0xba, 0xdf,
0xb0, 0x13, 0x92, 0x59, 0x4e, 0x4d, 0xfd, 0xff, 0x5d, 0x66, 0x14, 0xd4,
0x0b, 0x43, 0x9f, 0xd8, 0x62, 0x4e, 0x54, 0x83, 0xf9, 0x59, 0x48, 0x2f,
0x26, 0x21, 0xd4, 0xf0, 0x98, 0x6b, 0x14, 0x61, 0x4b, 0xf6, 0x00, 0xcf,
0xe3, 0x24, 0x4b, 0x2f, 0xd4, 0x5e, 0x6d, 0x40, 0x4b, 0x52, 0xea, 0xa5,
0x89, 0x94, 0x0f, 0xd2, 0xeb, 0x02, 0x54, 0x68, 0x26, 0xcf, 0x2d, 0x83,
0x0f, 0x62, 0x1b, 0x9e, 0x75, 0x81, 0x65, 0x30, 0xdd, 0x03, 0xb0, 0xc1,
0xda, 0x4a, 0xc9, 0xd8, 0x64, 0x29, 0x6a, 0xe2, 0x83, 0xf1, 0xb8, 0x4e,
0xc5, 0xaf, 0x8e, 0xf0, 0x8c, 0xbb, 0xda, 0xea, 0x8a, 0xbc, 0xcc, 0xa3,
0xf2, 0x19, 0xe8, 0xeb, 0x7e, 0x18, 0x64, 0x91, 0x1f, 0xdd, 0xc8, 0xc8,
0xf8, 0xad, 0xc8, 0x3b, 0xf9, 0x92, 0x74, 0x03, 0x9b, 0x90, 0x51, 0xb7,
0xb5, 0xe3, 0x80, 0x4f, 0x8a, 0x84, 0xfa, 0xa4, 0xd6, 0x9c, 0x53, 0x0b,
0x81, 0xc3, 0xee, 0x9e, 0x70, 0xa3, 0xbd, 0x3f, 0xbb, 0x60, 0x2d, 0x97,
0x65, 0xa7, 0x69, 0x2f, 0x22, 0x49, 0x65, 0xf4, 0xb0, 0x33, 0xbc, 0xd2,
0x80, 0x1b, 0x2c, 0x3a, 0xe3, 0x04, 0x8c, 0x49, 0x42, 0x25, 0xa0, 0x6d,
0x3c, 0xfe, 0xfa, 0x70, 0x90, 0x6a, 0x30, 0xf4, 0x0c, 0xe4, 0x3f, 0x78,
0xf9, 0xba, 0x55, 0xb9, 0xfa, 0xd7, 0xce, 0x05, 0xbc, 0xe9, 0xc9, 0xad,
0x4a, 0x37, 0xa0, 0xf7, 0x8d, 0x96, 0x22, 0xf6, 0x38, 0x8d, 0xf4, 0xf6,
0xe6, 0x8b, 0x45, 0xac, 0x13, 0xc5, 0xe6, 0x05, 0x1e, 0x09, 0xd7, 0x98,
0xb3, 0xb6, 0x59, 0xe4, 0x3b, 0x47, 0x16, 0x6e, 0xdf, 0xac, 0x7f, 0x38,
0x6e, 0xf9, 0xcf, 0xaa, 0x68, 0x98, 0xdb, 0x22, 0x89, 0x6e, 0xad, 0xbe,
0xed, 0xb1, 0x82, 0xa0, 0xc2, 0x9b, 0xd5, 0x79, 0x89, 0x96, 0xf6, 0xd9,
0x8f, 0x58, 0x77, 0x15, 0x2c, 0x73, 0xeb, 0x89, 0xcc, 0xf0, 0x37, 0x4d,
0x41, 0x70, 0xc5, 0x58, 0xae, 0x77, 0xab, 0x30, 0xcf, 0x6c, 0x7c, 0x1c,
0x52, 0x9a, 0x62, 0xf6, 0xf8, 0x0a, 0x65, 0x92, 0x83, 0x01, 0xc3, 0x60,
0xed, 0xfd, 0x4d, 0x9a, 0x4b, 0xd4, 0xa5, 0xe1, 0xc4, 0xe2, 0xe1, 0x8c,
0x64, 0xce, 0x54, 0x9c, 0xa9, 0x7f, 0xb9, 0x34, 0x88, 0xb1, 0x17, 0xde,
0x85, 0x27, 0x43, 0x81, 0x3b, 0x37, 0x27, 0x25, 0xbf, 0xba, 0x6c, 0x69,
0xb4, 0xce, 0xcd, 0xef, 0x7c, 0xee, 0x48, 0xb9, 0x8a, 0x09, 0x1f, 0x42,
0x8e, 0xc3, 0x14, 0xe4, 0xfd, 0xda, 0xa0, 0xfa, 0x7d, 0x0b, 0x68, 0x6a,
0x81, 0xb1, 0x96, 0xf9, 0x07, 0xf2, 0xed, 0x32, 0xf3, 0x52, 0x15, 0x00,
0x2f, 0x5f, 0x6e, 0x55, 0xc6, 0x85, 0x4b, 0xdc, 0x3d, 0x7c, 0xa6, 0xa7,
0xeb, 0x80, 0xab, 0xf3, 0xfe, 0x21, 0xe6, 0x1d, 0xbd, 0xca, 0x33, 0x29,
0x9c, 0x94, 0xa4, 0x7f, 0xec, 0x67, 0x6c, 0xc0, 0x0e, 0xea, 0x5f, 0x9a,
0x30, 0x54, 0x9c, 0xf2, 0x8f, 0xaa, 0x5f, 0xc3, 0x3e, 0x61, 0x54, 0x41,
0x8a, 0xbf, 0x7f, 0xff, 0x8a, 0xfb, 0x7f, 0x8c, 0x40, 0xe4, 0x5a, 0xbe,
0xe5, 0x39, 0xa4, 0xdd, 0x9b, 0xa6, 0xab, 0xd7, 0xee, 0x15, 0x32, 0x04,
0xd1, 0xbd, 0x7c, 0xe4, 0x98, 0x3f, 0x3f, 0x40, 0x87, 0xbc, 0x01, 0x36,
0xe7, 0xcd, 0x7b, 0xcf, 0xf7, 0xe9, 0x60, 0x1d, 0xea, 0xe2, 0x6e, 0x13,
0x3c, 0xd0, 0x28, 0x2d, 0xa8, 0x9d, 0x25, 0x06, 0x99, 0xf8, 0xdc, 0x9f,
0x9f, 0xc5, 0x5a, 0xfd, 0x8f, 0x31, 0xcf, 0xc3, 0xe5, 0xe6, 0xc9, 0x99,
0xae, 0x56, 0x72, 0xe2, 0x2f, 0xdf, 0x2c, 0x0e, 0x7d, 0x51, 0xc8, 0x35,
0x40, 0x23, 0x9e, 0x52, 0x44, 0x6f, 0x17, 0x40, 0x01, 0xd1, 0x85, 0x07,
0x55, 0xef, 0x10, 0xc9, 0xe5, 0xb9, 0xef, 0xbf, 0xf4, 0xe5, 0x38, 0xd3,
0x1f, 0x2b, 0x91, 0x51, 0x60, 0x75, 0xc8, 0x95, 0xcc, 0x9d, 0x5a, 0xfa,
0x69, 0xae, 0x9a, 0x16, 0x41, 0x07, 0x9c, 0x94, 0x40, 0xb7, 0xa9, 0xdd,
0x8d, 0xcb, 0xce, 0xc5, 0xf2, 0x5e, 0x59, 0x67, 0x69, 0x07, 0xd3, 0x04,
0xa4, 0x99, 0x56, 0xd9, 0xdc, 0x04, 0x9a, 0x66, 0x62, 0x7b, 0x67, 0xc8,
0xd0, 0x34, 0x69, 0x5b, 0x4a, 0xce, 0x6e, 0x53, 0x0e, 0x62, 0xf7, 0x85,
0xe0, 0xd7, 0xb7, 0x27, 0x55, 0x3a, 0x4d, 0x36, 0x47, 0xf2, 0x74, 0x02,
0xb3, 0x66, 0x2c, 0xda, 0xc3, 0xb3, 0x38, 0x94, 0x67, 0x82, 0x44, 0xf4,
0x12, 0xe4, 0x1c, 0x8f, 0x22, 0x4d, 0x32, 0x35, 0xf0, 0xe3, 0x41, 0x0a,
0x7d, 0xe4, 0xb4, 0x6e, 0x10, 0x4f, 0xa9, 0x46, 0xd5, 0xab, 0x90, 0x4c,
0xad, 0x2c, 0x30, 0xd0, 0x9e, 0x68, 0x2c, 0xc4, 0x3c, 0xf7, 0x05, 0xdf,
0x22, 0xaa, 0xb0, 0x82, 0xbb, 0x2c, 0x67, 0x8c, 0xfd, 0x1b, 0x04, 0x41,
0xf1, 0x4f, 0x77, 0xa4, 0xdb, 0xfb, 0xca, 0x1d, 0xd7, 0x61, 0x8a, 0x3e,
0x89, 0x40, 0x88, 0xf2, 0xda, 0x35, 0x2b, 0x9d, 0xbf, 0xd8, 0x98, 0x55,
0x4e, 0x60, 0xac, 0xc1, 0x1b, 0xd4, 0xe0, 0xb8, 0x6d, 0x13, 0xa0, 0xa3,
0x24, 0x80, 0xa0, 0xe6, 0x12, 0xad, 0x27, 0x36, 0xee, 0xd7, 0x55, 0x4b,
0xb4, 0x1a, 0xd2, 0x87, 0x31, 0x1a, 0x00, 0x53, 0xe9, 0x0f, 0xb7, 0x50,
0xeb, 0xdb, 0x63, 0xfe, 0xc3, 0xd0, 0xb1, 0x25, 0xdc, 0x63, 0x66, 0xcc,
0xe6, 0x99, 0xa3, 0x34, 0x0b, 0x1d, 0xdd, 0x84, 0x88, 0x3c, 0xfc, 0x79,
0xf5, 0x13, 0x0a, 0xe0, 0xca, 0x9e, 0x02, 0xeb, 0x06, 0xab, 0x6d, 0x80,
0xeb, 0x06, 0x3d, 0x9a, 0xbb, 0x97, 0xd5, 0xd2, 0x23, 0x22, 0x17, 0xca,
0x7a, 0x34, 0x09, 0xfe, 0x53, 0xfa, 0xc1, 0x34, 0x2a, 0x2c, 0xcb, 0x07,
0xd3, 0x92, 0x86, 0x9c, 0x7b, 0xd6, 0xdc, 0xe9, 0x5d, 0xa9, 0xcd, 0xb3,
0x72, 0xc1, 0x5d, 0xcd, 0x3f, 0xc2, 0x9b, 0xcf, 0x5a, 0x54, 0xbe, 0x50,
0x84, 0xbc, 0xe1, 0x33, 0xbd, 0xfd, 0xb6, 0x59, 0x49, 0x11, 0x25, 0xc9,
0x01, 0x57, 0x78, 0x8a, 0xef, 0x16, 0x7e, 0x15, 0x2d, 0x9d, 0x30, 0x4b,
0x68, 0xa4, 0x3b, 0x99, 0xbf, 0x11, 0x70, 0x0f, 0x17, 0x33, 0xd5, 0x6e,
0x31, 0x86, 0x7a, 0xea, 0x12, 0xdb, 0xb9, 0xbc, 0x67, 0x4e, 0x79, 0x58,
0x2f, 0x81, 0x00, 0x00, 0x00, 0x0e, 0x41, 0x9a, 0x21, 0x6c, 0x42, 0xdf,
0x18, 0xd3, 0x2d, 0x01, 0x43, 0x7f, 0x24, 0x38, 0x00, 0x00, 0x03, 0x1f,
'm', 'o', 'o', 'v', 0x00, 0x00, 0x00, 0x6c, 'm', 'v', 'h', 'd',
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x50, 0x00, 0x01, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x49, 't', 'r', 'a', 'k',
0x00, 0x00, 0x00, 0x5c, 't', 'k', 'h', 'd', 0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x00, 0xa0, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24,
'e', 'd', 't', 's', 0x00, 0x00, 0x00, 0x1c, 'e', 'l', 's', 't',
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc1,
'm', 'd', 'i', 'a', 0x00, 0x00, 0x00, 0x20, 'm', 'd', 'h', 'd',
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x04, 0x00, 0x15, 0xc7, 0x00, 0x00,
0x00, 0x00, 0x00, 0x2d, 0x68, 0x64, 0x6c, 0x72, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x76, 0x69, 0x64, 0x65, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x69, 0x64, 0x65,
0x6f, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00, 0x00, 0x00, 0x01,
0x6c, 0x6d, 0x69, 0x6e, 0x66, 0x00, 0x00, 0x00, 0x14, 0x76, 0x6d, 0x68,
0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x24, 0x64, 0x69, 0x6e, 0x66, 0x00, 0x00, 0x00,
0x1c, 0x64, 0x72, 0x65, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0c, 0x75, 0x72, 0x6c, 0x20, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x01, 0x2c, 0x73, 0x74, 0x62, 0x6c, 0x00, 0x00, 0x00,
0xac, 0x73, 0x74, 0x73, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x9c, 0x61, 0x76, 0x63, 0x31, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00,
0x5a, 0x00, 0x48, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0xff, 0xff, 0x00, 0x00, 0x00, 0x36, 0x61, 0x76, 0x63, 0x43, 0x01,
0x64, 0x00, 0x0a, 0xff, 0xe1, 0x00, 0x1d, 0x67, 0x64, 0x00, 0x0a, 0xac,
0xd9, 0x42, 0x0d, 0xf9, 0x3f, 0xf0, 0x00, 0x50, 0x00, 0x41, 0x00, 0x00,
0x03, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x32, 0x0f, 0x12, 0x25, 0x96,
0x01, 0x00, 0x06, 0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0, 0x00, 0x00, 0x00,
0x10, 0x70, 0x61, 0x73, 0x70, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x18, 0x73, 0x74, 0x74, 0x73, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x14, 0x73, 0x74, 0x73, 0x73, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x1c, 0x73, 0x74, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x1c, 0x73, 0x74, 0x73, 0x7a, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x07,
0xb2, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x73, 0x74, 0x63,
0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x30, 0x00, 0x00, 0x00, 0x62, 0x75, 0x64, 0x74, 0x61, 0x00, 0x00, 0x00,
0x5a, 0x6d, 0x65, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x21, 0x68, 0x64, 0x6c, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x6d, 0x64, 0x69, 0x72, 0x61, 0x70, 0x70, 0x6c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x69, 0x6c,
0x73, 0x74, 0x00, 0x00, 0x00, 0x25, 0xa9, 0x74, 0x6f, 0x6f, 0x00, 0x00,
0x00, 0x1d, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x4c, 0x61, 0x76, 0x66, 0x35, 0x36, 0x2e, 0x33, 0x33, 0x2e,
0x31, 0x30, 0x31
nsTArray<uint8_t>
ReadTestFile(const char* aFilename)
{
if (!aFilename) {
return {};
}
FILE* f = fopen(aFilename, "rb");
if (!f) {
return {};
}
if (fseek(f, 0, SEEK_END) != 0) {
fclose(f);
return {};
}
long position = ftell(f);
// I know EOF==-1, so this test is made obsolete by '<0', but I don't want
// the code to rely on that.
if (position == 0 || position == EOF || position < 0) {
fclose(f);
return {};
}
if (fseek(f, 0, SEEK_SET) != 0) {
fclose(f);
return {};
}
size_t len = static_cast<size_t>(position);
nsTArray<uint8_t> buffer(len);
buffer.SetLength(len);
size_t read = fread(buffer.Elements(), 1, len, f);
fclose(f);
if (read != len) {
return {};
}
return buffer;
}
struct TestFileData
{
const char* mFilename;
uint32_t mNumberVideoTracks;
int32_t mWidth;
int32_t mHeight;
};
static const TestFileData testFiles[] = {
{ "test_case_1187067.mp4", 1, 160, 90 },
{ "test_case_1200326.mp4", 0, 0, 0 }
};
static const size_t test_case_mp4_len = ArrayLength(test_case_mp4);
TEST(stagefright_MPEG4Metadata, test_case_mp4)
{
nsRefPtr<MediaByteBuffer> buffer = new MediaByteBuffer(test_case_mp4_len);
buffer->AppendElements(test_case_mp4, test_case_mp4_len);
nsRefPtr<BufferStream> stream = new BufferStream(buffer);
for (size_t test = 0; test < ArrayLength(testFiles); ++test) {
nsTArray<uint8_t> buffer = ReadTestFile(testFiles[test].mFilename);
ASSERT_FALSE(buffer.IsEmpty());
nsRefPtr<Stream> stream = new TestStream(buffer.Elements(), buffer.Length());
EXPECT_TRUE(MP4Metadata::HasCompleteMetadata(stream));
nsRefPtr<MediaByteBuffer> metadataBuffer = MP4Metadata::Metadata(stream);
EXPECT_TRUE(metadataBuffer);
EXPECT_TRUE(MP4Metadata::HasCompleteMetadata(stream));
nsRefPtr<MediaByteBuffer> metadataBuffer = MP4Metadata::Metadata(stream);
EXPECT_TRUE(metadataBuffer);
MP4Metadata metadata(stream);
EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kUndefinedTrack));
EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kAudioTrack));
EXPECT_EQ(1u, metadata.GetNumberTracks(TrackInfo::kVideoTrack));
EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kTextTrack));
EXPECT_EQ(0u, metadata.GetNumberTracks(static_cast<TrackInfo::TrackType>(-1)));
EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kUndefinedTrack, 0));
EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kAudioTrack, 0));
UniquePtr<TrackInfo> track = metadata.GetTrackInfo(TrackInfo::kVideoTrack, 0);
EXPECT_TRUE(!!track);
EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kTextTrack, 0));
EXPECT_FALSE(metadata.GetTrackInfo(static_cast<TrackInfo::TrackType>(-1), 0));
// We can see anywhere in any MPEG4.
EXPECT_TRUE(metadata.CanSeek());
EXPECT_FALSE(metadata.Crypto().valid);
MP4Metadata metadata(stream);
EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kUndefinedTrack));
EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kAudioTrack));
EXPECT_EQ(testFiles[test].mNumberVideoTracks,
metadata.GetNumberTracks(TrackInfo::kVideoTrack));
EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kTextTrack));
EXPECT_EQ(0u, metadata.GetNumberTracks(static_cast<TrackInfo::TrackType>(-1)));
EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kUndefinedTrack, 0));
EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kAudioTrack, 0));
UniquePtr<TrackInfo> trackInfo = metadata.GetTrackInfo(TrackInfo::kVideoTrack, 0);
if (testFiles[test].mNumberVideoTracks == 0) {
EXPECT_TRUE(!trackInfo);
} else {
EXPECT_TRUE(!!trackInfo);
if (trackInfo) {
const VideoInfo* videoInfo = trackInfo->GetAsVideoInfo();
EXPECT_TRUE(!!videoInfo);
if (videoInfo) {
EXPECT_TRUE(videoInfo->IsValid());
EXPECT_TRUE(videoInfo->IsVideo());
EXPECT_EQ(testFiles[test].mWidth, videoInfo->mDisplay.width);
EXPECT_EQ(testFiles[test].mHeight, videoInfo->mDisplay.height);
FallibleTArray<mp4_demuxer::Index::Indice> indices;
EXPECT_TRUE(metadata.ReadTrackIndex(indices, videoInfo->mTrackId));
}
}
}
EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kTextTrack, 0));
EXPECT_FALSE(metadata.GetTrackInfo(static_cast<TrackInfo::TrackType>(-1), 0));
// We can see anywhere in any MPEG4.
EXPECT_TRUE(metadata.CanSeek());
EXPECT_FALSE(metadata.Crypto().valid);
}
}
TEST(stagefright_MPEG4Metadata, test_case_mp4_skimming)
TEST(stagefright_MPEG4Metadata, test_case_mp4_subsets)
{
static const size_t step = 4u;
nsRefPtr<MediaByteBuffer> buffer = new MediaByteBuffer(test_case_mp4_len);
buffer->AppendElements(test_case_mp4, test_case_mp4_len);
for (size_t offset = 0; offset < test_case_mp4_len - step; offset += step) {
nsRefPtr<BufferStream> stream = new BufferStream(buffer);
static const size_t step = 1u;
for (size_t test = 0; test < ArrayLength(testFiles); ++test) {
nsTArray<uint8_t> buffer = ReadTestFile(testFiles[test].mFilename);
ASSERT_FALSE(buffer.IsEmpty());
ASSERT_LE(step, buffer.Length());
// Just exercizing the parser starting at different points through the file,
// making sure it doesn't crash.
// No checks because results would differ for each position.
MP4Metadata::HasCompleteMetadata(stream);
nsRefPtr<MediaByteBuffer> metadataBuffer = MP4Metadata::Metadata(stream);
MP4Metadata metadata(stream);
for (size_t offset = 0; offset < buffer.Length() - step; offset += step) {
size_t size = buffer.Length() - offset;
while (size > 0) {
nsRefPtr<TestStream> stream =
new TestStream(buffer.Elements() + offset, size);
buffer->RemoveElementsAt(0, step);
MP4Metadata::HasCompleteMetadata(stream);
nsRefPtr<MediaByteBuffer> metadataBuffer = MP4Metadata::Metadata(stream);
MP4Metadata metadata(stream);
if (stream->mHighestSuccessfulEndOffset <= 0) {
// No successful reads -> Cutting down the size won't change anything.
break;
}
if (stream->mHighestSuccessfulEndOffset < size) {
// Read up to a point before the end -> Resize down to that point.
size = stream->mHighestSuccessfulEndOffset;
} else {
// Read up to the end (or after?!) -> Just cut 1 byte.
size -= 1;
}
}
}
}
}
TEST(stagefright_MoofParser, test_case_mp4)
{
nsRefPtr<MediaByteBuffer> buffer = new MediaByteBuffer(test_case_mp4_len);
buffer->AppendElements(test_case_mp4, test_case_mp4_len);
nsRefPtr<BufferStream> stream = new BufferStream(buffer);
for (size_t test = 0; test < ArrayLength(testFiles); ++test) {
nsTArray<uint8_t> buffer = ReadTestFile(testFiles[test].mFilename);
ASSERT_FALSE(buffer.IsEmpty());
nsRefPtr<Stream> stream = new TestStream(buffer.Elements(), buffer.Length());
Monitor monitor("MP4Metadata::HasCompleteMetadata");
MonitorAutoLock mon(monitor);
MoofParser parser(stream, 0, false, &monitor);
EXPECT_EQ(0u, parser.mOffset);
EXPECT_FALSE(parser.ReachedEnd());
nsTArray<MediaByteRange> byteRanges;
byteRanges.AppendElement(MediaByteRange(0, 0));
EXPECT_FALSE(parser.RebuildFragmentedIndex(byteRanges));
EXPECT_TRUE(parser.GetCompositionRange(byteRanges).IsNull());
EXPECT_TRUE(parser.mInitRange.IsNull());
EXPECT_EQ(0u, parser.mOffset);
EXPECT_FALSE(parser.ReachedEnd());
EXPECT_TRUE(parser.HasMetadata());
nsRefPtr<MediaByteBuffer> metadataBuffer = parser.Metadata();
EXPECT_TRUE(metadataBuffer);
EXPECT_TRUE(parser.FirstCompleteMediaSegment().IsNull());
EXPECT_TRUE(parser.FirstCompleteMediaHeader().IsNull());
}
TEST(stagefright_MoofParser, test_case_mp4_skimming)
{
const size_t step = 4u;
nsRefPtr<MediaByteBuffer> buffer = new MediaByteBuffer(test_case_mp4_len);
buffer->AppendElements(test_case_mp4, test_case_mp4_len);
Monitor monitor("MP4Metadata::HasCompleteMetadata");
MonitorAutoLock mon(monitor);
for (size_t offset = 0; offset < test_case_mp4_len - step; offset += step) {
nsRefPtr<BufferStream> stream = new BufferStream(buffer);
// Just exercizing the parser starting at different points through the file,
// making sure it doesn't crash.
// No checks because results would differ for each position.
Monitor monitor("MP4Metadata::HasCompleteMetadata");
MonitorAutoLock mon(monitor);
MoofParser parser(stream, 0, false, &monitor);
EXPECT_EQ(0u, parser.mOffset);
EXPECT_FALSE(parser.ReachedEnd());
nsTArray<MediaByteRange> byteRanges;
byteRanges.AppendElement(MediaByteRange(0, 0));
EXPECT_FALSE(parser.RebuildFragmentedIndex(byteRanges));
parser.GetCompositionRange(byteRanges);
parser.HasMetadata();
nsRefPtr<MediaByteBuffer> metadataBuffer = parser.Metadata();
parser.FirstCompleteMediaSegment();
parser.FirstCompleteMediaHeader();
buffer->RemoveElementsAt(0, step);
EXPECT_TRUE(parser.GetCompositionRange(byteRanges).IsNull());
EXPECT_TRUE(parser.mInitRange.IsNull());
EXPECT_EQ(0u, parser.mOffset);
EXPECT_FALSE(parser.ReachedEnd());
EXPECT_TRUE(parser.HasMetadata());
nsRefPtr<MediaByteBuffer> metadataBuffer = parser.Metadata();
EXPECT_TRUE(metadataBuffer);
EXPECT_TRUE(parser.FirstCompleteMediaSegment().IsNull());
EXPECT_TRUE(parser.FirstCompleteMediaHeader().IsNull());
}
}
TEST(stagefright_MoofParser, test_case_mp4_subsets)
{
const size_t step = 1u;
for (size_t test = 0; test < ArrayLength(testFiles); ++test) {
nsTArray<uint8_t> buffer = ReadTestFile(testFiles[test].mFilename);
ASSERT_FALSE(buffer.IsEmpty());
ASSERT_LE(step, buffer.Length());
Monitor monitor("MP4Metadata::HasCompleteMetadata");
MonitorAutoLock mon(monitor);
// Just exercizing the parser starting at different points through the file,
// making sure it doesn't crash.
// No checks because results would differ for each position.
for (size_t offset = 0; offset < buffer.Length() - step; offset += step) {
size_t size = buffer.Length() - offset;
while (size > 0) {
nsRefPtr<TestStream> stream =
new TestStream(buffer.Elements() + offset, size);
MoofParser parser(stream, 0, false, &monitor);
nsTArray<MediaByteRange> byteRanges;
byteRanges.AppendElement(MediaByteRange(0, 0));
EXPECT_FALSE(parser.RebuildFragmentedIndex(byteRanges));
parser.GetCompositionRange(byteRanges);
parser.HasMetadata();
nsRefPtr<MediaByteBuffer> metadataBuffer = parser.Metadata();
parser.FirstCompleteMediaSegment();
parser.FirstCompleteMediaHeader();
if (stream->mHighestSuccessfulEndOffset <= 0) {
// No successful reads -> Cutting down the size won't change anything.
break;
}
if (stream->mHighestSuccessfulEndOffset < size) {
// Read up to a point before the end -> Resize down to that point.
size = stream->mHighestSuccessfulEndOffset;
} else {
// Read up to the end (or after?!) -> Just cut 1 byte.
size -= 1;
}
}
}
}
}

Просмотреть файл

@ -11,6 +11,11 @@ SOURCES += [
'TestParser.cpp',
]
TEST_HARNESS_FILES.gtest += [
'test_case_1187067.mp4',
'test_case_1200326.mp4',
]
if CONFIG['MOZ_RUST']:
UNIFIED_SOURCES += ['TestMP4Rust.cpp',]
TEST_HARNESS_FILES.gtest += [

Двоичные данные
media/libstagefright/gtest/test_case_1187067.mp4 Normal file

Двоичный файл не отображается.

Двоичные данные
media/libstagefright/gtest/test_case_1200326.mp4 Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -84,6 +84,7 @@ import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
import android.hardware.Sensor;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
@ -110,6 +111,7 @@ import android.util.Base64;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.Surface;
import android.view.SurfaceView;
@ -2740,4 +2742,10 @@ public class GeckoAppShell
}
return 0;
}
@WrapForJNI
static Rect getScreenSize() {
Display disp = getGeckoInterface().getActivity().getWindowManager().getDefaultDisplay();
return new Rect(0, 0, disp.getWidth(), disp.getHeight());
}
}

Просмотреть файл

@ -97,9 +97,6 @@ public class GeckoEvent {
LOW_MEMORY(35),
NETWORK_LINK_CHANGE(36),
TELEMETRY_HISTOGRAM_ADD(37),
PREFERENCES_OBSERVE(39),
PREFERENCES_GET(40),
PREFERENCES_REMOVE_OBSERVERS(41),
TELEMETRY_UI_SESSION_START(42),
TELEMETRY_UI_SESSION_STOP(43),
TELEMETRY_UI_EVENT(44),
@ -225,8 +222,6 @@ public class GeckoEvent {
private float mGamepadButtonValue;
private float[] mGamepadValues;
private String[] mPrefNames;
private Object mObject;
private GeckoEvent(NativeGeckoEvent event) {
@ -767,29 +762,6 @@ public class GeckoEvent {
return event;
}
@RobocopTarget
public static GeckoEvent createPreferencesObserveEvent(int requestId, String[] prefNames) {
GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.PREFERENCES_OBSERVE);
event.mCount = requestId;
event.mPrefNames = prefNames;
return event;
}
@RobocopTarget
public static GeckoEvent createPreferencesGetEvent(int requestId, String[] prefNames) {
GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.PREFERENCES_GET);
event.mCount = requestId;
event.mPrefNames = prefNames;
return event;
}
@RobocopTarget
public static GeckoEvent createPreferencesRemoveObserversEvent(int requestId) {
GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.PREFERENCES_REMOVE_OBSERVERS);
event.mCount = requestId;
return event;
}
public static GeckoEvent createLowMemoryEvent(int level) {
GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.LOW_MEMORY);
event.mMetaState = level;

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше