mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to fx-team, a=merge
This commit is contained in:
commit
db3aaf2c33
@ -324,8 +324,14 @@ IDRefsIterator::GetElem(const nsDependentSubstring& aID)
|
||||
Accessible*
|
||||
IDRefsIterator::Next()
|
||||
{
|
||||
nsIContent* nextElm = NextElem();
|
||||
return nextElm ? mDoc->GetAccessible(nextElm) : nullptr;
|
||||
nsIContent* nextEl = nullptr;
|
||||
while ((nextEl = NextElem())) {
|
||||
Accessible* acc = mDoc->GetAccessible(nextEl);
|
||||
if (acc) {
|
||||
return acc;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
@ -295,12 +295,16 @@ EventQueue::CoalesceReorderEvents(AccEvent* aTailEvent)
|
||||
AccReorderEvent* thisReorder = downcast_accEvent(thisEvent);
|
||||
AccReorderEvent* tailReorder = downcast_accEvent(aTailEvent);
|
||||
uint32_t eventType = thisReorder->IsShowHideEventTarget(tailParent);
|
||||
if (eventType == nsIAccessibleEvent::EVENT_SHOW)
|
||||
if (eventType == nsIAccessibleEvent::EVENT_SHOW) {
|
||||
tailReorder->DoNotEmitAll();
|
||||
else if (eventType == nsIAccessibleEvent::EVENT_HIDE)
|
||||
}
|
||||
else if (eventType == nsIAccessibleEvent::EVENT_HIDE) {
|
||||
NS_ERROR("Accessible tree was modified after it was removed! Huh?");
|
||||
else
|
||||
}
|
||||
else {
|
||||
aTailEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
mEvents[index].swap(mEvents[count - 1]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NotificationController)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHangingChildDocuments)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContentInsertions)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvents)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRelocations)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(NotificationController, AddRef)
|
||||
@ -86,6 +87,7 @@ NotificationController::Shutdown()
|
||||
mContentInsertions.Clear();
|
||||
mNotifications.Clear();
|
||||
mEvents.Clear();
|
||||
mRelocations.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
@ -351,6 +353,16 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
|
||||
// modification are done.
|
||||
mDocument->ProcessInvalidationList();
|
||||
|
||||
// We cannot rely on DOM tree to keep aria-owns relations updated. Make
|
||||
// a validation to remove dead links.
|
||||
mDocument->ValidateARIAOwned();
|
||||
|
||||
// Process relocation list.
|
||||
for (uint32_t idx = 0; idx < mRelocations.Length(); idx++) {
|
||||
mDocument->DoARIAOwnsRelocation(mRelocations[idx]);
|
||||
}
|
||||
mRelocations.Clear();
|
||||
|
||||
// If a generic notification occurs after this point then we may be allowed to
|
||||
// process it synchronously. However we do not want to reenter if fireing
|
||||
// events causes script to run.
|
||||
|
@ -133,6 +133,16 @@ public:
|
||||
nsIContent* aStartChildNode,
|
||||
nsIContent* aEndChildNode);
|
||||
|
||||
/**
|
||||
* Pend an accessible subtree relocation.
|
||||
*/
|
||||
void ScheduleRelocation(Accessible* aOwner)
|
||||
{
|
||||
if (!mRelocations.Contains(aOwner) && mRelocations.AppendElement(aOwner)) {
|
||||
ScheduleProcessing();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start to observe refresh to make notifications and events processing after
|
||||
* layout.
|
||||
@ -303,6 +313,11 @@ private:
|
||||
* use SwapElements() on it.
|
||||
*/
|
||||
nsTArray<RefPtr<Notification> > mNotifications;
|
||||
|
||||
/**
|
||||
* Holds all scheduled relocations.
|
||||
*/
|
||||
nsTArray<RefPtr<Accessible> > mRelocations;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
|
@ -116,7 +116,7 @@ TreeWalker::Next(ChildrenIterator* aIter, Accessible** aAccesible,
|
||||
// Ignore the accessible and its subtree if it was repositioned by means of
|
||||
// aria-owns.
|
||||
if (accessible) {
|
||||
if (accessible->IsRepositioned()) {
|
||||
if (accessible->IsRelocated()) {
|
||||
*aSkipSubtree = true;
|
||||
} else {
|
||||
*aAccesible = accessible;
|
||||
|
@ -910,13 +910,13 @@ public:
|
||||
* Get/set repositioned bit indicating that the accessible was moved in
|
||||
* the accessible tree, i.e. the accessible tree structure differs from DOM.
|
||||
*/
|
||||
bool IsRepositioned() const { return mStateFlags & eRepositioned; }
|
||||
void SetRepositioned(bool aRepositioned)
|
||||
bool IsRelocated() const { return mStateFlags & eRelocated; }
|
||||
void SetRelocated(bool aRelocated)
|
||||
{
|
||||
if (aRepositioned)
|
||||
mStateFlags |= eRepositioned;
|
||||
if (aRelocated)
|
||||
mStateFlags |= eRelocated;
|
||||
else
|
||||
mStateFlags &= ~eRepositioned;
|
||||
mStateFlags &= ~eRelocated;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1009,9 +1009,9 @@ protected:
|
||||
eSubtreeMutating = 1 << 6, // subtree is being mutated
|
||||
eIgnoreDOMUIEvent = 1 << 7, // don't process DOM UI events for a11y events
|
||||
eSurvivingInUpdate = 1 << 8, // parent drops children to recollect them
|
||||
eRepositioned = 1 << 9, // accessible was moved in tree
|
||||
eRelocated = 1 << 9, // accessible was moved in tree
|
||||
|
||||
eLastStateFlag = eRepositioned
|
||||
eLastStateFlag = eRelocated
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -130,9 +130,13 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DocAccessible, Accessible)
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAccessibleCache)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchorJumpElm)
|
||||
for (uint32_t i = 0; i < tmp->mARIAOwnsInvalidationList.Length(); ++i) {
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mARIAOwnsInvalidationList[i].mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mARIAOwnsInvalidationList[i].mChild)
|
||||
for (auto it = tmp->mARIAOwnsHash.ConstIter(); !it.Done(); it.Next()) {
|
||||
nsTArray<RefPtr<Accessible> >* ar = it.UserData();
|
||||
for (uint32_t i = 0; i < ar->Length(); i++) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
|
||||
"mARIAOwnsHash entry item");
|
||||
cb.NoteXPCOMChild(ar->ElementAt(i));
|
||||
}
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
@ -144,10 +148,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DocAccessible, Accessible)
|
||||
tmp->mNodeToAccessibleMap.Clear();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAccessibleCache)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnchorJumpElm)
|
||||
for (uint32_t i = 0; i < tmp->mARIAOwnsInvalidationList.Length(); ++i) {
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mARIAOwnsInvalidationList[i].mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mARIAOwnsInvalidationList[i].mChild)
|
||||
}
|
||||
tmp->mARIAOwnsHash.Clear();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DocAccessible)
|
||||
@ -731,6 +732,10 @@ DocAccessible::AttributeWillChange(nsIDocument* aDocument,
|
||||
if (aModType != nsIDOMMutationEvent::ADDITION)
|
||||
RemoveDependentIDsFor(accessible, aAttribute);
|
||||
|
||||
if (aAttribute == nsGkAtoms::id) {
|
||||
RelocateARIAOwnedIfNeeded(aElement);
|
||||
}
|
||||
|
||||
// Store the ARIA attribute old value so that it can be used after
|
||||
// attribute change. Note, we assume there's no nested ARIA attribute
|
||||
// changes. If this happens then we should end up with keeping a stack of
|
||||
@ -906,6 +911,10 @@ DocAccessible::AttributeChangedImpl(Accessible* aAccessible,
|
||||
return;
|
||||
}
|
||||
|
||||
if (aAttribute == nsGkAtoms::id) {
|
||||
RelocateARIAOwnedIfNeeded(elm);
|
||||
}
|
||||
|
||||
// ARIA or XUL selection
|
||||
if ((aAccessible->GetContent()->IsXULElement() &&
|
||||
aAttribute == nsGkAtoms::selected) ||
|
||||
@ -1040,6 +1049,10 @@ DocAccessible::ARIAAttributeChanged(Accessible* aAccessible, nsIAtom* aAttribute
|
||||
FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, aAccessible);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aAttribute == nsGkAtoms::aria_owns) {
|
||||
mNotificationController->ScheduleRelocation(aAccessible);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -1271,6 +1284,15 @@ DocAccessible::BindToDocument(Accessible* aAccessible,
|
||||
aAccessible->SetRoleMapEntry(aRoleMapEntry);
|
||||
|
||||
AddDependentIDsFor(aAccessible);
|
||||
|
||||
if (aAccessible->HasOwnContent()) {
|
||||
nsIContent* el = aAccessible->GetContent();
|
||||
if (el->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_owns)) {
|
||||
mNotificationController->ScheduleRelocation(aAccessible);
|
||||
}
|
||||
|
||||
RelocateARIAOwnedIfNeeded(el);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -1365,67 +1387,6 @@ DocAccessible::ProcessInvalidationList()
|
||||
}
|
||||
|
||||
mInvalidationList.Clear();
|
||||
|
||||
// Alter the tree according to aria-owns (seize the trees).
|
||||
for (uint32_t idx = 0; idx < mARIAOwnsInvalidationList.Length(); idx++) {
|
||||
Accessible* owner = mARIAOwnsInvalidationList[idx].mOwner;
|
||||
if (!owner->IsInDocument()) { // eventually died before we've got here
|
||||
continue;
|
||||
}
|
||||
|
||||
Accessible* child = GetAccessible(mARIAOwnsInvalidationList[idx].mChild);
|
||||
if (!child || !child->IsInDocument()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Accessible* oldParent = child->Parent();
|
||||
if (!oldParent) {
|
||||
NS_ERROR("The accessible is in document but doesn't have a parent");
|
||||
continue;
|
||||
}
|
||||
int32_t idxInParent = child->IndexInParent();
|
||||
|
||||
// XXX: update context flags
|
||||
{
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(oldParent);
|
||||
RefPtr<AccMutationEvent> hideEvent =
|
||||
new AccHideEvent(child, child->GetContent(), false);
|
||||
FireDelayedEvent(hideEvent);
|
||||
reorderEvent->AddSubMutationEvent(hideEvent);
|
||||
|
||||
AutoTreeMutation mut(oldParent);
|
||||
oldParent->RemoveChild(child);
|
||||
|
||||
MaybeNotifyOfValueChange(oldParent);
|
||||
FireDelayedEvent(reorderEvent);
|
||||
}
|
||||
|
||||
bool isReinserted = false;
|
||||
{
|
||||
AutoTreeMutation mut(owner);
|
||||
isReinserted = owner->AppendChild(child);
|
||||
}
|
||||
|
||||
Accessible* newParent = owner;
|
||||
if (!isReinserted) {
|
||||
AutoTreeMutation mut(oldParent);
|
||||
oldParent->InsertChildAt(idxInParent, child);
|
||||
newParent = oldParent;
|
||||
}
|
||||
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(newParent);
|
||||
RefPtr<AccMutationEvent> showEvent =
|
||||
new AccShowEvent(child, child->GetContent());
|
||||
FireDelayedEvent(showEvent);
|
||||
reorderEvent->AddSubMutationEvent(showEvent);
|
||||
|
||||
MaybeNotifyOfValueChange(newParent);
|
||||
FireDelayedEvent(reorderEvent);
|
||||
|
||||
child->SetRepositioned(isReinserted);
|
||||
}
|
||||
|
||||
mARIAOwnsInvalidationList.Clear();
|
||||
}
|
||||
|
||||
Accessible*
|
||||
@ -1635,31 +1596,6 @@ DocAccessible::AddDependentIDsFor(Accessible* aRelProvider, nsIAtom* aRelAttr)
|
||||
if (!HasAccessible(dependentContent)) {
|
||||
mInvalidationList.AppendElement(dependentContent);
|
||||
}
|
||||
|
||||
// Update ARIA owns cache.
|
||||
if (relAttr == nsGkAtoms::aria_owns) {
|
||||
// ARIA owns cannot refer to itself or a parent. Ignore
|
||||
// the element if so.
|
||||
nsIContent* parentEl = relProviderEl;
|
||||
while (parentEl && parentEl != dependentContent) {
|
||||
parentEl = parentEl->GetParent();
|
||||
}
|
||||
|
||||
if (!parentEl) {
|
||||
// ARIA owns element cannot refer to an element in parents chain
|
||||
// of other ARIA owns element (including that ARIA owns element)
|
||||
// if it's inside of a dependent element subtree of that
|
||||
// ARIA owns element. Applied recursively.
|
||||
if (!IsInARIAOwnsLoop(relProviderEl, dependentContent)) {
|
||||
nsTArray<nsIContent*>* list =
|
||||
mARIAOwnsHash.LookupOrAdd(aRelProvider);
|
||||
list->AppendElement(dependentContent);
|
||||
|
||||
mARIAOwnsInvalidationList.AppendElement(
|
||||
ARIAOwnsPair(aRelProvider, dependentContent));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1709,59 +1645,6 @@ DocAccessible::RemoveDependentIDsFor(Accessible* aRelProvider,
|
||||
}
|
||||
}
|
||||
|
||||
// aria-owns has gone, put the children back.
|
||||
if (relAttr == nsGkAtoms::aria_owns) {
|
||||
nsTArray<nsIContent*>* children = mARIAOwnsHash.Get(aRelProvider);
|
||||
if (children) {
|
||||
nsTArray<Accessible*> containers;
|
||||
|
||||
// Remove ARIA owned elements from where they belonged.
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(aRelProvider);
|
||||
{
|
||||
AutoTreeMutation mut(aRelProvider);
|
||||
for (uint32_t idx = 0; idx < children->Length(); idx++) {
|
||||
nsIContent* childEl = children->ElementAt(idx);
|
||||
Accessible* child = GetAccessible(childEl);
|
||||
if (child && child->IsRepositioned()) {
|
||||
{
|
||||
RefPtr<AccMutationEvent> hideEvent =
|
||||
new AccHideEvent(child, childEl, false);
|
||||
FireDelayedEvent(hideEvent);
|
||||
reorderEvent->AddSubMutationEvent(hideEvent);
|
||||
|
||||
aRelProvider->RemoveChild(child);
|
||||
}
|
||||
|
||||
// Collect DOM-order containers to update their trees.
|
||||
child->SetRepositioned(false);
|
||||
Accessible* container = GetContainerAccessible(childEl);
|
||||
if (!containers.Contains(container)) {
|
||||
containers.AppendElement(container);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mARIAOwnsHash.Remove(aRelProvider);
|
||||
for (uint32_t idx = 0; idx < mARIAOwnsInvalidationList.Length();) {
|
||||
if (mARIAOwnsInvalidationList[idx].mOwner == aRelProvider) {
|
||||
mARIAOwnsInvalidationList.RemoveElementAt(idx);
|
||||
continue;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
|
||||
MaybeNotifyOfValueChange(aRelProvider);
|
||||
FireDelayedEvent(reorderEvent);
|
||||
|
||||
// Reinserted previously ARIA owned elements into the tree
|
||||
// (restore a DOM-like order).
|
||||
for (uint32_t idx = 0; idx < containers.Length(); idx++) {
|
||||
UpdateTreeOnInsertion(containers[idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the relation attribute is given then we don't have anything else to
|
||||
// check.
|
||||
if (aRelAttr)
|
||||
@ -1769,45 +1652,6 @@ DocAccessible::RemoveDependentIDsFor(Accessible* aRelProvider,
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
DocAccessible::IsInARIAOwnsLoop(nsIContent* aOwnerEl, nsIContent* aDependentEl)
|
||||
{
|
||||
// ARIA owns element cannot refer to an element in parents chain of other ARIA
|
||||
// owns element (including that ARIA owns element) if it's inside of
|
||||
// a dependent element subtree of that ARIA owns element.
|
||||
for (auto it = mARIAOwnsHash.Iter(); !it.Done(); it.Next()) {
|
||||
Accessible* otherOwner = it.Key();
|
||||
nsIContent* parentEl = otherOwner->GetContent();
|
||||
while (parentEl && parentEl != aDependentEl) {
|
||||
parentEl = parentEl->GetParent();
|
||||
}
|
||||
|
||||
// The dependent element of this ARIA owns element contains some other ARIA
|
||||
// owns element, make sure this ARIA owns element is not in a subtree of
|
||||
// a dependent element of that other ARIA owns element. If not then
|
||||
// continue a check recursively.
|
||||
if (parentEl) {
|
||||
nsTArray<nsIContent*>* childEls = it.UserData();
|
||||
for (uint32_t idx = 0; idx < childEls->Length(); idx++) {
|
||||
nsIContent* childEl = childEls->ElementAt(idx);
|
||||
nsIContent* parentEl = aOwnerEl;
|
||||
while (parentEl && parentEl != childEl) {
|
||||
parentEl = parentEl->GetParent();
|
||||
}
|
||||
if (parentEl) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IsInARIAOwnsLoop(aOwnerEl, childEl)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
|
||||
nsIAtom* aAttribute)
|
||||
@ -2023,11 +1867,6 @@ DocAccessible::UpdateTreeOnRemoval(Accessible* aContainer, nsIContent* aChildNod
|
||||
}
|
||||
}
|
||||
|
||||
// We may not have an integral DOM tree to remove all aria-owns relations
|
||||
// from the tree. Validate all relations after timeout to workaround that.
|
||||
mNotificationController->ScheduleNotification<DocAccessible>
|
||||
(this, &DocAccessible::ValidateARIAOwned);
|
||||
|
||||
// Content insertion/removal is not cause of accessible tree change.
|
||||
if (updateFlags == eNoAccessible)
|
||||
return;
|
||||
@ -2111,23 +1950,244 @@ DocAccessible::UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
|
||||
return updateFlags;
|
||||
}
|
||||
|
||||
void
|
||||
DocAccessible::RelocateARIAOwnedIfNeeded(nsIContent* aElement)
|
||||
{
|
||||
if (!aElement->HasID())
|
||||
return;
|
||||
|
||||
AttrRelProviderArray* list =
|
||||
mDependentIDsHash.Get(nsDependentAtomString(aElement->GetID()));
|
||||
if (list) {
|
||||
for (uint32_t idx = 0; idx < list->Length(); idx++) {
|
||||
if (list->ElementAt(idx)->mRelAttr == nsGkAtoms::aria_owns) {
|
||||
Accessible* owner = GetAccessible(list->ElementAt(idx)->mContent);
|
||||
if (owner) {
|
||||
mNotificationController->ScheduleRelocation(owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DocAccessible::ValidateARIAOwned()
|
||||
{
|
||||
for (auto it = mARIAOwnsHash.Iter(); !it.Done(); it.Next()) {
|
||||
nsTArray<nsIContent*>* childEls = it.UserData();
|
||||
for (uint32_t idx = 0; idx < childEls->Length(); idx++) {
|
||||
nsIContent* childEl = childEls->ElementAt(idx);
|
||||
Accessible* child = GetAccessible(childEl);
|
||||
if (child && child->IsInDocument() && !child->GetFrame()) {
|
||||
if (!child->Parent()) {
|
||||
NS_ERROR("An element in the document doesn't have a parent?");
|
||||
Accessible* owner = it.Key();
|
||||
nsTArray<RefPtr<Accessible> >* children = it.UserData();
|
||||
|
||||
// Owner is about to die, put children back if applicable.
|
||||
if (!owner->IsInDocument()) {
|
||||
PutChildrenBack(children, 0);
|
||||
it.Remove();
|
||||
continue;
|
||||
}
|
||||
UpdateTreeOnRemoval(child->Parent(), childEl);
|
||||
|
||||
for (uint32_t idx = 0; idx < children->Length(); idx++) {
|
||||
Accessible* child = children->ElementAt(idx);
|
||||
if (!child->IsInDocument()) {
|
||||
children->RemoveElementAt(idx);
|
||||
idx--;
|
||||
continue;
|
||||
}
|
||||
|
||||
NS_ASSERTION(child->Parent(), "No parent for ARIA owned?");
|
||||
|
||||
// If DOM node doesn't have a frame anymore then shutdown its accessible.
|
||||
if (child->Parent() && !child->GetFrame()) {
|
||||
UpdateTreeOnRemoval(child->Parent(), child->GetContent());
|
||||
children->RemoveElementAt(idx);
|
||||
idx--;
|
||||
continue;
|
||||
}
|
||||
|
||||
NS_ASSERTION(child->Parent() == owner,
|
||||
"Illigally stolen ARIA owned child!");
|
||||
}
|
||||
|
||||
if (children->Length() == 0) {
|
||||
it.Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DocAccessible::DoARIAOwnsRelocation(Accessible* aOwner)
|
||||
{
|
||||
nsTArray<RefPtr<Accessible> >* children = mARIAOwnsHash.LookupOrAdd(aOwner);
|
||||
|
||||
IDRefsIterator iter(this, aOwner->Elm(), nsGkAtoms::aria_owns);
|
||||
Accessible* child = nullptr;
|
||||
|
||||
uint32_t arrayIdx = 0, insertIdx = aOwner->ChildCount() - children->Length();
|
||||
while ((child = iter.Next())) {
|
||||
// Same child on same position, no change.
|
||||
if (child->Parent() == aOwner &&
|
||||
child->IndexInParent() == static_cast<int32_t>(insertIdx)) {
|
||||
NS_ASSERTION(child == children->ElementAt(arrayIdx), "Not in sync!");
|
||||
insertIdx++; arrayIdx++;
|
||||
continue;
|
||||
}
|
||||
|
||||
NS_ASSERTION(children->SafeElementAt(arrayIdx) != child, "Already in place!");
|
||||
|
||||
nsTArray<RefPtr<Accessible> >::index_type idx = children->IndexOf(child);
|
||||
if (idx < arrayIdx) {
|
||||
continue; // ignore second entry of same ID
|
||||
}
|
||||
|
||||
// A new child is found, check for loops.
|
||||
if (child->Parent() != aOwner) {
|
||||
Accessible* parent = aOwner;
|
||||
while (parent && parent != child && !parent->IsDoc()) {
|
||||
parent = parent->Parent();
|
||||
}
|
||||
// A referred child cannot be a parent of the owner.
|
||||
if (parent == child) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (child->Parent() == aOwner) {
|
||||
MoveChild(child, insertIdx - 1);
|
||||
children->InsertElementAt(arrayIdx, child);
|
||||
arrayIdx++;
|
||||
|
||||
} else if (SeizeChild(aOwner, child, insertIdx)) {
|
||||
children->InsertElementAt(arrayIdx, child);
|
||||
insertIdx++; arrayIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
// Put back children that are not seized anymore.
|
||||
PutChildrenBack(children, arrayIdx);
|
||||
if (children->Length() == 0) {
|
||||
mARIAOwnsHash.Remove(aOwner);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
DocAccessible::SeizeChild(Accessible* aNewParent, Accessible* aChild,
|
||||
int32_t aIdxInParent)
|
||||
{
|
||||
Accessible* oldParent = aChild->Parent();
|
||||
NS_PRECONDITION(oldParent, "No parent?");
|
||||
|
||||
int32_t oldIdxInParent = aChild->IndexInParent();
|
||||
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(oldParent);
|
||||
RefPtr<AccMutationEvent> hideEvent =
|
||||
new AccHideEvent(aChild, aChild->GetContent(), false);
|
||||
reorderEvent->AddSubMutationEvent(hideEvent);
|
||||
|
||||
{
|
||||
AutoTreeMutation mut(oldParent);
|
||||
oldParent->RemoveChild(aChild);
|
||||
}
|
||||
|
||||
bool isReinserted = false;
|
||||
{
|
||||
AutoTreeMutation mut(aNewParent);
|
||||
isReinserted = aNewParent->InsertChildAt(aIdxInParent, aChild);
|
||||
}
|
||||
|
||||
if (!isReinserted) {
|
||||
AutoTreeMutation mut(oldParent);
|
||||
oldParent->InsertChildAt(oldIdxInParent, aChild);
|
||||
return false;
|
||||
}
|
||||
|
||||
// The child may be stolen from other ARIA owns element.
|
||||
if (aChild->IsRelocated()) {
|
||||
nsTArray<RefPtr<Accessible> >* children = mARIAOwnsHash.Get(oldParent);
|
||||
children->RemoveElement(aChild);
|
||||
}
|
||||
|
||||
FireDelayedEvent(hideEvent);
|
||||
MaybeNotifyOfValueChange(oldParent);
|
||||
FireDelayedEvent(reorderEvent);
|
||||
|
||||
reorderEvent = new AccReorderEvent(aNewParent);
|
||||
RefPtr<AccMutationEvent> showEvent =
|
||||
new AccShowEvent(aChild, aChild->GetContent());
|
||||
reorderEvent->AddSubMutationEvent(showEvent);
|
||||
|
||||
FireDelayedEvent(showEvent);
|
||||
MaybeNotifyOfValueChange(aNewParent);
|
||||
FireDelayedEvent(reorderEvent);
|
||||
|
||||
aChild->SetRelocated(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
DocAccessible::MoveChild(Accessible* aChild, int32_t aIdxInParent)
|
||||
{
|
||||
NS_PRECONDITION(aChild->Parent(), "No parent?");
|
||||
|
||||
Accessible* parent = aChild->Parent();
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(parent);
|
||||
RefPtr<AccMutationEvent> hideEvent =
|
||||
new AccHideEvent(aChild, aChild->GetContent(), false);
|
||||
reorderEvent->AddSubMutationEvent(hideEvent);
|
||||
|
||||
AutoTreeMutation mut(parent);
|
||||
parent->RemoveChild(aChild);
|
||||
|
||||
parent->InsertChildAt(aIdxInParent, aChild);
|
||||
aChild->SetRelocated(true);
|
||||
|
||||
FireDelayedEvent(hideEvent);
|
||||
|
||||
RefPtr<AccMutationEvent> showEvent =
|
||||
new AccShowEvent(aChild, aChild->GetContent());
|
||||
reorderEvent->AddSubMutationEvent(showEvent);
|
||||
FireDelayedEvent(showEvent);
|
||||
|
||||
MaybeNotifyOfValueChange(parent);
|
||||
FireDelayedEvent(reorderEvent);
|
||||
}
|
||||
|
||||
void
|
||||
DocAccessible::PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
|
||||
uint32_t aStartIdx)
|
||||
{
|
||||
nsTArray<Accessible*> containers;
|
||||
for (auto idx = aStartIdx; idx < aChildren->Length(); idx++) {
|
||||
Accessible* child = aChildren->ElementAt(idx);
|
||||
|
||||
// If the child is in the tree then remove it from the owner.
|
||||
if (child->IsInDocument()) {
|
||||
Accessible* owner = child->Parent();
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(owner);
|
||||
RefPtr<AccMutationEvent> hideEvent =
|
||||
new AccHideEvent(child, child->GetContent(), false);
|
||||
reorderEvent->AddSubMutationEvent(hideEvent);
|
||||
|
||||
{
|
||||
AutoTreeMutation mut(owner);
|
||||
owner->RemoveChild(child);
|
||||
child->SetRelocated(false);
|
||||
}
|
||||
|
||||
FireDelayedEvent(hideEvent);
|
||||
MaybeNotifyOfValueChange(owner);
|
||||
FireDelayedEvent(reorderEvent);
|
||||
}
|
||||
|
||||
Accessible* container = GetContainerAccessible(child->GetContent());
|
||||
if (container &&
|
||||
containers.IndexOf(container) == nsTArray<Accessible*>::NoIndex) {
|
||||
containers.AppendElement(container);
|
||||
}
|
||||
}
|
||||
|
||||
// And put it back where it belongs to.
|
||||
aChildren->RemoveElementsAt(aStartIdx, aChildren->Length() - aStartIdx);
|
||||
for (uint32_t idx = 0; idx < containers.Length(); idx++) {
|
||||
UpdateTreeOnInsertion(containers[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -286,13 +286,9 @@ public:
|
||||
*/
|
||||
Accessible* ARIAOwnedAt(Accessible* aParent, uint32_t aIndex) const
|
||||
{
|
||||
nsTArray<nsIContent*>* childrenEl = mARIAOwnsHash.Get(aParent);
|
||||
if (childrenEl) {
|
||||
nsIContent* childEl = childrenEl->SafeElementAt(aIndex);
|
||||
Accessible* child = GetAccessible(childEl);
|
||||
if (child && child->IsRepositioned()) {
|
||||
return child;
|
||||
}
|
||||
nsTArray<RefPtr<Accessible> >* children = mARIAOwnsHash.Get(aParent);
|
||||
if (children) {
|
||||
return children->SafeElementAt(aIndex);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@ -436,12 +432,6 @@ protected:
|
||||
void RemoveDependentIDsFor(Accessible* aRelProvider,
|
||||
nsIAtom* aRelAttr = nullptr);
|
||||
|
||||
/**
|
||||
* Return true if given ARIA owner element and its referred content make
|
||||
* the loop closed.
|
||||
*/
|
||||
bool IsInARIAOwnsLoop(nsIContent* aOwnerEl, nsIContent* aDependentEl);
|
||||
|
||||
/**
|
||||
* Update or recreate an accessible depending on a changed attribute.
|
||||
*
|
||||
@ -513,11 +503,38 @@ protected:
|
||||
uint32_t UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
|
||||
AccReorderEvent* aReorderEvent);
|
||||
|
||||
/**
|
||||
* Schedule ARIA owned element relocation if needed.
|
||||
*/
|
||||
void RelocateARIAOwnedIfNeeded(nsIContent* aEl);
|
||||
|
||||
/**
|
||||
* Validates all aria-owns connections and updates the tree accordingly.
|
||||
*/
|
||||
void ValidateARIAOwned();
|
||||
|
||||
/**
|
||||
* Steals or puts back accessible subtrees.
|
||||
*/
|
||||
void DoARIAOwnsRelocation(Accessible* aOwner);
|
||||
|
||||
/**
|
||||
* Moves the child from old parent under new one.
|
||||
*/
|
||||
bool SeizeChild(Accessible* aNewParent, Accessible* aChild,
|
||||
int32_t aIdxInParent);
|
||||
|
||||
/**
|
||||
* Move the child under same parent.
|
||||
*/
|
||||
void MoveChild(Accessible* aChild, int32_t aIdxInParent);
|
||||
|
||||
/**
|
||||
* Moves children back under their original parents.
|
||||
*/
|
||||
void PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
|
||||
uint32_t aStartIdx);
|
||||
|
||||
/**
|
||||
* Create accessible tree.
|
||||
*
|
||||
@ -649,14 +666,12 @@ protected:
|
||||
AttrRelProvider& operator =(const AttrRelProvider&);
|
||||
};
|
||||
|
||||
typedef nsTArray<nsAutoPtr<AttrRelProvider> > AttrRelProviderArray;
|
||||
typedef nsClassHashtable<nsStringHashKey, AttrRelProviderArray>
|
||||
DependentIDsHashtable;
|
||||
|
||||
/**
|
||||
* The cache of IDs pointed by relation attributes.
|
||||
*/
|
||||
DependentIDsHashtable mDependentIDsHash;
|
||||
typedef nsTArray<nsAutoPtr<AttrRelProvider> > AttrRelProviderArray;
|
||||
nsClassHashtable<nsStringHashKey, AttrRelProviderArray>
|
||||
mDependentIDsHash;
|
||||
|
||||
friend class RelatedAccIterator;
|
||||
|
||||
@ -669,24 +684,11 @@ protected:
|
||||
nsTArray<nsIContent*> mInvalidationList;
|
||||
|
||||
/**
|
||||
* Holds a list of aria-owns relations.
|
||||
* Holds a list of aria-owns relocations.
|
||||
*/
|
||||
nsClassHashtable<nsPtrHashKey<Accessible>, nsTArray<nsIContent*> >
|
||||
nsClassHashtable<nsPtrHashKey<Accessible>, nsTArray<RefPtr<Accessible> > >
|
||||
mARIAOwnsHash;
|
||||
|
||||
struct ARIAOwnsPair {
|
||||
ARIAOwnsPair(Accessible* aOwner, nsIContent* aChild) :
|
||||
mOwner(aOwner), mChild(aChild) { }
|
||||
ARIAOwnsPair(const ARIAOwnsPair& aPair) :
|
||||
mOwner(aPair.mOwner), mChild(aPair.mChild) { }
|
||||
ARIAOwnsPair& operator =(const ARIAOwnsPair& aPair)
|
||||
{ mOwner = aPair.mOwner; mChild = aPair.mChild; return *this; }
|
||||
|
||||
RefPtr<Accessible> mOwner;
|
||||
nsCOMPtr<nsIContent> mChild;
|
||||
};
|
||||
nsTArray<ARIAOwnsPair> mARIAOwnsInvalidationList;
|
||||
|
||||
/**
|
||||
* Used to process notification from core and accessible events.
|
||||
*/
|
||||
|
@ -51,9 +51,11 @@
|
||||
{
|
||||
this.menuNode = getNode(aMenuID);
|
||||
|
||||
// Because of aria-owns processing we may have menupopup start fired before
|
||||
// related show event.
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_SHOW, this.menuNode),
|
||||
new invokerChecker(EVENT_MENUPOPUP_START, this.menuNode),
|
||||
new asyncInvokerChecker(EVENT_MENUPOPUP_START, this.menuNode),
|
||||
new invokerChecker(EVENT_REORDER, getNode(aParentMenuID))
|
||||
];
|
||||
|
||||
|
@ -28,7 +28,7 @@
|
||||
{
|
||||
var tree =
|
||||
{ SECTION: [ // t1_1
|
||||
{ SECTION: [ // t1_2
|
||||
{ HEADING: [ // t1_2
|
||||
// no kids, no loop
|
||||
] }
|
||||
] };
|
||||
@ -36,8 +36,8 @@
|
||||
|
||||
tree =
|
||||
{ SECTION: [ // t2_1
|
||||
{ SECTION: [ // t2_2
|
||||
{ SECTION: [ // t2_3
|
||||
{ GROUPING: [ // t2_2
|
||||
{ HEADING: [ // t2_3
|
||||
// no kids, no loop
|
||||
] }
|
||||
] }
|
||||
@ -46,9 +46,9 @@
|
||||
|
||||
tree =
|
||||
{ SECTION: [ // t3_3
|
||||
{ SECTION: [ // t3_1
|
||||
{ SECTION: [ // t3_2
|
||||
{ SECTION: [ // DOM child of t3_2
|
||||
{ GROUPING: [ // t3_1
|
||||
{ NOTE: [ // t3_2
|
||||
{ HEADING: [ // DOM child of t3_2
|
||||
// no kids, no loop
|
||||
] }
|
||||
] }
|
||||
@ -58,7 +58,7 @@
|
||||
|
||||
tree =
|
||||
{ SECTION: [ // t4_1
|
||||
{ SECTION: [ // DOM child of t4_1
|
||||
{ GROUPING: [ // DOM child of t4_1, aria-owns ignored
|
||||
// no kids, no loop
|
||||
] }
|
||||
] };
|
||||
@ -66,11 +66,11 @@
|
||||
|
||||
tree =
|
||||
{ SECTION: [ // t5_1
|
||||
{ SECTION: [ // DOM child of t5_1
|
||||
{ SECTION: [ // t5_2
|
||||
{ SECTION: [ // DOM child of t5_2
|
||||
{ SECTION: [ // t5_3
|
||||
{ SECTION: [ // DOM child of t5_3
|
||||
{ GROUPING: [ // DOM child of t5_1
|
||||
{ NOTE: [ // t5_2
|
||||
{ HEADING: [ // DOM child of t5_2
|
||||
{ FORM: [ // t5_3
|
||||
{ TOOLTIP: [ // DOM child of t5_3
|
||||
// no kids, no loop
|
||||
]}
|
||||
]}
|
||||
@ -80,6 +80,14 @@
|
||||
] };
|
||||
testAccessibleTree("t5_1", tree);
|
||||
|
||||
tree =
|
||||
{ SECTION: [ // t6_1
|
||||
{ RADIOBUTTON: [ ] },
|
||||
{ CHECKBUTTON: [ ] }, // t6_3, rearranged by aria-owns
|
||||
{ PUSHBUTTON: [ ] }, // t6_2, rearranged by aria-owns
|
||||
] };
|
||||
testAccessibleTree("t6_1", tree);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
@ -96,24 +104,37 @@
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<!-- simple loop -->
|
||||
<div id="t1_1" aria-owns="t1_2"></div>
|
||||
<div id="t1_2" aria-owns="t1_1"></div>
|
||||
<div id="t1_2" aria-owns="t1_1" role="heading"></div>
|
||||
|
||||
<div id="t2_2" aria-owns="t2_3"></div>
|
||||
<!-- loop -->
|
||||
<div id="t2_2" aria-owns="t2_3" role="group"></div>
|
||||
<div id="t2_1" aria-owns="t2_2"></div>
|
||||
<div id="t2_3" aria-owns="t2_1"></div>
|
||||
<div id="t2_3" aria-owns="t2_1" role="heading"></div>
|
||||
|
||||
<div id="t3_1" aria-owns="t3_2"></div>
|
||||
<div id="t3_2">
|
||||
<div aria-owns="t3_3"></div>
|
||||
<!-- loop #2 -->
|
||||
<div id="t3_1" aria-owns="t3_2" role="group"></div>
|
||||
<div id="t3_2" role="note">
|
||||
<div aria-owns="t3_3" role="heading"></div>
|
||||
</div>
|
||||
<div id="t3_3" aria-owns="t3_1"></div>
|
||||
|
||||
<div id="t4_1"><div aria-owns="t4_1"></div></div>
|
||||
<!-- self loop -->
|
||||
<div id="t4_1"><div aria-owns="t4_1" role="group"></div></div>
|
||||
|
||||
<!-- natural and aria-owns hierarchy -->
|
||||
<div id="t5_1"><div aria-owns="t5_2" role="group"></div></div>
|
||||
<div id="t5_2" role="note"><div aria-owns="t5_3" role="heading"></div></div>
|
||||
<div id="t5_3" role="form"><div aria-owns="t5_1" role="tooltip"></div></div>
|
||||
|
||||
<!-- rearrange children -->
|
||||
<div id="t6_1" aria-owns="t6_3 t6_2">
|
||||
<div id="t6_2" role="button"></div>
|
||||
<div id="t6_3" role="checkbox"></div>
|
||||
<div role="radio"></div>
|
||||
</div>
|
||||
|
||||
<div id="t5_1"><div aria-owns="t5_2"></div>
|
||||
<div id="t5_2"><div aria-owns="t5_3"></div></div>
|
||||
<div id="t5_3"><div aria-owns="t5_1"></div></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
@ -26,11 +26,11 @@
|
||||
function removeARIAOwns()
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getNode("t2_checkbox")),
|
||||
new invokerChecker(EVENT_HIDE, getNode("t2_button")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t2_button")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t2_checkbox")),
|
||||
new invokerChecker(EVENT_REORDER, getNode("container2"))
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_checkbox")),
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_button")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_button")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_checkbox")),
|
||||
new invokerChecker(EVENT_REORDER, getNode("t1_container"))
|
||||
];
|
||||
|
||||
this.invoke = function removeARIAOwns_invoke()
|
||||
@ -43,9 +43,9 @@
|
||||
] },
|
||||
{ PUSHBUTTON: [ ] }
|
||||
] };
|
||||
testAccessibleTree("container2", tree);
|
||||
testAccessibleTree("t1_container", tree);
|
||||
|
||||
getNode("container2").removeAttribute("aria-owns");
|
||||
getNode("t1_container").removeAttribute("aria-owns");
|
||||
}
|
||||
|
||||
this.finalCheck = function removeARIAOwns_finalCheck()
|
||||
@ -58,7 +58,7 @@
|
||||
{ SECTION: [] }
|
||||
] }
|
||||
] };
|
||||
testAccessibleTree("container2", tree);
|
||||
testAccessibleTree("t1_container", tree);
|
||||
}
|
||||
|
||||
this.getID = function removeARIAOwns_getID()
|
||||
@ -70,16 +70,17 @@
|
||||
function setARIAOwns()
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getNode("t2_button")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t2_button")),
|
||||
new invokerChecker(EVENT_HIDE, getNode("t2_subdiv")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t2_subdiv")),
|
||||
new invokerChecker(EVENT_REORDER, getNode("container2"))
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_button")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_button")),
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_subdiv")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_subdiv")),
|
||||
new invokerChecker(EVENT_REORDER, getNode("t1_container"))
|
||||
];
|
||||
|
||||
this.invoke = function setARIAOwns_invoke()
|
||||
{
|
||||
getNode("container2").setAttribute("aria-owns", "t2_button t2_subdiv");
|
||||
getNode("t1_container").
|
||||
setAttribute("aria-owns", "t1_button t1_subdiv");
|
||||
}
|
||||
|
||||
this.finalCheck = function setARIAOwns_finalCheck()
|
||||
@ -88,11 +89,11 @@
|
||||
// the children.
|
||||
var tree =
|
||||
{ SECTION: [
|
||||
{ CHECKBUTTON: [ ] }, // div
|
||||
{ PUSHBUTTON: [ ] }, // button
|
||||
{ SECTION: [ ] } // subdiv
|
||||
{ CHECKBUTTON: [ ] }, // checkbox
|
||||
{ PUSHBUTTON: [ ] }, // button, rearranged by ARIA own
|
||||
{ SECTION: [ ] } // subdiv from the subtree, ARIA owned
|
||||
] };
|
||||
testAccessibleTree("container2", tree);
|
||||
testAccessibleTree("t1_container", tree);
|
||||
}
|
||||
|
||||
this.getID = function setARIAOwns_getID()
|
||||
@ -101,19 +102,53 @@
|
||||
}
|
||||
}
|
||||
|
||||
function addIdToARIAOwns()
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_group")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_group")),
|
||||
new invokerChecker(EVENT_REORDER, document)
|
||||
];
|
||||
|
||||
this.invoke = function addIdToARIAOwns_invoke()
|
||||
{
|
||||
getNode("t1_container").
|
||||
setAttribute("aria-owns", "t1_button t1_subdiv t1_group");
|
||||
}
|
||||
|
||||
this.finalCheck = function addIdToARIAOwns_finalCheck()
|
||||
{
|
||||
// children are swapped again, button and subdiv are appended to
|
||||
// the children.
|
||||
var tree =
|
||||
{ SECTION: [
|
||||
{ CHECKBUTTON: [ ] }, // t1_checkbox
|
||||
{ PUSHBUTTON: [ ] }, // button, t1_button
|
||||
{ SECTION: [ ] }, // subdiv from the subtree, t1_subdiv
|
||||
{ GROUPING: [ ] } // group from outside, t1_group
|
||||
] };
|
||||
testAccessibleTree("t1_container", tree);
|
||||
}
|
||||
|
||||
this.getID = function addIdToARIAOwns_getID()
|
||||
{
|
||||
return "Add id to @aria-owns attribute value";
|
||||
}
|
||||
}
|
||||
|
||||
function appendEl()
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_SHOW, getNode, "child3"),
|
||||
new invokerChecker(EVENT_REORDER, getNode("container2"))
|
||||
new invokerChecker(EVENT_SHOW, getNode, "t1_child3"),
|
||||
new invokerChecker(EVENT_REORDER, getNode("t1_container"))
|
||||
];
|
||||
|
||||
this.invoke = function appendEl_invoke()
|
||||
{
|
||||
var div = document.createElement("div");
|
||||
div.setAttribute("id", "child3");
|
||||
div.setAttribute("id", "t1_child3");
|
||||
div.setAttribute("role", "radio")
|
||||
getNode("container2").appendChild(div);
|
||||
getNode("t1_container").appendChild(div);
|
||||
}
|
||||
|
||||
this.finalCheck = function appendEl_finalCheck()
|
||||
@ -124,10 +159,11 @@
|
||||
{ SECTION: [
|
||||
{ CHECKBUTTON: [ ] },
|
||||
{ RADIOBUTTON: [ ] },
|
||||
{ PUSHBUTTON: [ ] }, // ARIA owned
|
||||
{ SECTION: [ ] } // ARIA owned
|
||||
{ PUSHBUTTON: [ ] }, // ARIA owned, t1_button
|
||||
{ SECTION: [ ] }, // ARIA owned, t1_subdiv
|
||||
{ GROUPING: [ ] } // ARIA owned, t1_group
|
||||
] };
|
||||
testAccessibleTree("container2", tree);
|
||||
testAccessibleTree("t1_container", tree);
|
||||
}
|
||||
|
||||
this.getID = function appendEl_getID()
|
||||
@ -139,15 +175,15 @@
|
||||
function removeEl()
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getNode, "t2_checkbox"),
|
||||
new invokerChecker(EVENT_SHOW, getNode, "t2_checkbox"),
|
||||
new invokerChecker(EVENT_REORDER, getNode("container2"))
|
||||
new invokerChecker(EVENT_HIDE, getNode, "t1_checkbox"),
|
||||
new invokerChecker(EVENT_SHOW, getNode, "t1_checkbox"),
|
||||
new invokerChecker(EVENT_REORDER, getNode("t1_container"))
|
||||
];
|
||||
|
||||
this.invoke = function removeEl_invoke()
|
||||
{
|
||||
// remove a container of t2_subdiv
|
||||
getNode("t2_span").parentNode.removeChild(getNode("t2_span"));
|
||||
// remove a container of t1_subdiv
|
||||
getNode("t1_span").parentNode.removeChild(getNode("t1_span"));
|
||||
}
|
||||
|
||||
this.finalCheck = function removeEl_finalCheck()
|
||||
@ -157,14 +193,214 @@
|
||||
{ SECTION: [
|
||||
{ CHECKBUTTON: [ ] },
|
||||
{ RADIOBUTTON: [ ] },
|
||||
{ PUSHBUTTON: [ ] } // ARIA owned
|
||||
{ PUSHBUTTON: [ ] }, // ARIA owned, t1_button
|
||||
{ GROUPING: [ ] } // ARIA owned, t1_group
|
||||
] };
|
||||
testAccessibleTree("container2", tree);
|
||||
testAccessibleTree("t1_container", tree);
|
||||
}
|
||||
|
||||
this.getID = function removeEl_getID()
|
||||
{
|
||||
return "Remove a container of ARIA ownded element";
|
||||
return "Remove a container of ARIA owned element";
|
||||
}
|
||||
}
|
||||
|
||||
function removeId()
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_group")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_group")),
|
||||
new invokerChecker(EVENT_REORDER, document)
|
||||
];
|
||||
|
||||
this.invoke = function removeId_invoke()
|
||||
{
|
||||
getNode("t1_group").removeAttribute("id");
|
||||
}
|
||||
|
||||
this.finalCheck = function removeId_finalCheck()
|
||||
{
|
||||
var tree =
|
||||
{ SECTION: [
|
||||
{ CHECKBUTTON: [ ] },
|
||||
{ RADIOBUTTON: [ ] },
|
||||
{ PUSHBUTTON: [ ] } // ARIA owned, t1_button
|
||||
] };
|
||||
testAccessibleTree("t1_container", tree);
|
||||
}
|
||||
|
||||
this.getID = function removeId_getID()
|
||||
{
|
||||
return "Remove ID from ARIA owned element";
|
||||
}
|
||||
}
|
||||
|
||||
function setId()
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_grouptmp")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_grouptmp")),
|
||||
new invokerChecker(EVENT_REORDER, document)
|
||||
];
|
||||
|
||||
this.invoke = function setId_invoke()
|
||||
{
|
||||
getNode("t1_grouptmp").setAttribute("id", "t1_group");
|
||||
}
|
||||
|
||||
this.finalCheck = function setId_finalCheck()
|
||||
{
|
||||
var tree =
|
||||
{ SECTION: [
|
||||
{ CHECKBUTTON: [ ] },
|
||||
{ RADIOBUTTON: [ ] },
|
||||
{ PUSHBUTTON: [ ] }, // ARIA owned, t1_button
|
||||
{ GROUPING: [ ] } // ARIA owned, t1_group, previously t1_grouptmp
|
||||
] };
|
||||
testAccessibleTree("t1_container", tree);
|
||||
}
|
||||
|
||||
this.getID = function setId_getID()
|
||||
{
|
||||
return "Set ID that is referred by ARIA owns";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an accessible DOM element containing an element referred by
|
||||
* ARIA owns.
|
||||
*/
|
||||
function removeA11eteiner()
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_REORDER, getNode("t2_container1"))
|
||||
];
|
||||
|
||||
this.invoke = function removeA11eteiner_invoke()
|
||||
{
|
||||
var tree =
|
||||
{ SECTION: [
|
||||
{ CHECKBUTTON: [ ] } // ARIA owned, 't2_owned'
|
||||
] };
|
||||
testAccessibleTree("t2_container1", tree);
|
||||
|
||||
getNode("t2_container2").removeChild(getNode("t2_container3"));
|
||||
}
|
||||
|
||||
this.finalCheck = function removeA11eteiner_finalCheck()
|
||||
{
|
||||
var tree =
|
||||
{ SECTION: [
|
||||
] };
|
||||
testAccessibleTree("t2_container1", tree);
|
||||
}
|
||||
|
||||
this.getID = function removeA11eteiner_getID()
|
||||
{
|
||||
return "Remove an accessible DOM element containing an element referred by ARIA owns";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Steal an element from other ARIA owns element. This use case guarantees
|
||||
* that result of setAttribute/removeAttribute doesn't depend on their order.
|
||||
*/
|
||||
function stealFromOtherARIAOwns()
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_REORDER, getNode("t3_container2"))
|
||||
];
|
||||
|
||||
this.invoke = function stealFromOtherARIAOwns_invoke()
|
||||
{
|
||||
getNode("t3_container2").setAttribute("aria-owns", "t3_child");
|
||||
}
|
||||
|
||||
this.finalCheck = function stealFromOtherARIAOwns_finalCheck()
|
||||
{
|
||||
var tree =
|
||||
{ SECTION: [
|
||||
] };
|
||||
testAccessibleTree("t3_container1", tree);
|
||||
|
||||
tree =
|
||||
{ SECTION: [
|
||||
{ CHECKBUTTON: [
|
||||
] }
|
||||
] };
|
||||
testAccessibleTree("t3_container2", tree);
|
||||
}
|
||||
|
||||
this.getID = function stealFromOtherARIAOwns_getID()
|
||||
{
|
||||
return "Steal an element from other ARIA owns element";
|
||||
}
|
||||
}
|
||||
|
||||
function appendElToRecacheChildren()
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_REORDER, getNode("t3_container2"))
|
||||
];
|
||||
|
||||
this.invoke = function appendElToRecacheChildren_invoke()
|
||||
{
|
||||
var div = document.createElement("div");
|
||||
div.setAttribute("role", "radio")
|
||||
getNode("t3_container2").appendChild(div);
|
||||
}
|
||||
|
||||
this.finalCheck = function appendElToRecacheChildren_finalCheck()
|
||||
{
|
||||
var tree =
|
||||
{ SECTION: [
|
||||
] };
|
||||
testAccessibleTree("t3_container1", tree);
|
||||
|
||||
tree =
|
||||
{ SECTION: [
|
||||
{ RADIOBUTTON: [ ] },
|
||||
{ CHECKBUTTON: [ ] } // ARIA owned
|
||||
] };
|
||||
testAccessibleTree("t3_container2", tree);
|
||||
}
|
||||
|
||||
this.getID = function appendElToRecacheChildren_getID()
|
||||
{
|
||||
return "Append a child under @aria-owns element to trigger children recache";
|
||||
}
|
||||
}
|
||||
|
||||
function showHiddenElement()
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_REORDER, getNode("t4_container1"))
|
||||
];
|
||||
|
||||
this.invoke = function showHiddenElement_invoke()
|
||||
{
|
||||
var tree =
|
||||
{ SECTION: [
|
||||
{ RADIOBUTTON: [] }
|
||||
] };
|
||||
testAccessibleTree("t4_container1", tree);
|
||||
|
||||
getNode("t4_child1").style.display = "block";
|
||||
}
|
||||
|
||||
this.finalCheck = function showHiddenElement_finalCheck()
|
||||
{
|
||||
var tree =
|
||||
{ SECTION: [
|
||||
{ CHECKBUTTON: [] },
|
||||
{ RADIOBUTTON: [] }
|
||||
] };
|
||||
testAccessibleTree("t4_container1", tree);
|
||||
}
|
||||
|
||||
this.getID = function showHiddenElement_getID()
|
||||
{
|
||||
return "Show hidden ARIA owns referred element";
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,10 +417,24 @@
|
||||
{
|
||||
gQueue = new eventQueue();
|
||||
|
||||
// test1
|
||||
gQueue.push(new removeARIAOwns());
|
||||
gQueue.push(new setARIAOwns());
|
||||
gQueue.push(new addIdToARIAOwns());
|
||||
gQueue.push(new appendEl());
|
||||
gQueue.push(new removeEl());
|
||||
gQueue.push(new removeId());
|
||||
gQueue.push(new setId());
|
||||
|
||||
// test2
|
||||
gQueue.push(new removeA11eteiner());
|
||||
|
||||
// test3
|
||||
gQueue.push(new stealFromOtherARIAOwns());
|
||||
gQueue.push(new appendElToRecacheChildren());
|
||||
|
||||
// test4
|
||||
gQueue.push(new showHiddenElement());
|
||||
|
||||
gQueue.invoke(); // SimpleTest.finish() will be called in the end
|
||||
}
|
||||
@ -202,15 +452,31 @@
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<div id="container2" aria-owns="t2_checkbox t2_button">
|
||||
<div role="button" id="t2_button"></div>
|
||||
<div role="checkbox" id="t2_checkbox">
|
||||
<span id="t2_span">
|
||||
<div id="t2_subdiv"></div>
|
||||
<div id="t1_container" aria-owns="t1_checkbox t1_button">
|
||||
<div role="button" id="t1_button"></div>
|
||||
<div role="checkbox" id="t1_checkbox">
|
||||
<span id="t1_span">
|
||||
<div id="t1_subdiv"></div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="t1_group" role="group"></div>
|
||||
<div id="t1_grouptmp" role="group"></div>
|
||||
|
||||
<div id="t2_container1" aria-owns="t2_owned"></div>
|
||||
<div id="t2_container2">
|
||||
<div id="t2_container3"><div id="t2_owned" role="checkbox"></div></div>
|
||||
</div>
|
||||
|
||||
<div id="t3_container1" aria-owns="t3_child"></div>
|
||||
<div id="t3_child" role="checkbox"></div>
|
||||
<div id="t3_container2"></div>
|
||||
|
||||
<div id="t4_container1" aria-owns="t4_child1 t4_child2"></div>
|
||||
<div id="t4_container2">
|
||||
<div id="t4_child1" style="display:none" role="checkbox"></div>
|
||||
<div id="t4_child2" role="radio"></div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
@ -22,9 +22,9 @@
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getNode("child")),
|
||||
new invokerChecker(EVENT_REORDER, this.containerNode),
|
||||
new invokerChecker(EVENT_HIDE, getNode("childDoc")),
|
||||
new invokerChecker(EVENT_SHOW, "newChildDoc")
|
||||
new invokerChecker(EVENT_SHOW, "newChildDoc"),
|
||||
new invokerChecker(EVENT_REORDER, this.containerNode)
|
||||
];
|
||||
|
||||
this.invoke = function runTest_invoke()
|
||||
|
@ -22,7 +22,9 @@ file.mkpath(storeFile.path);
|
||||
storeFile.append("store.json");
|
||||
var storeFilename = storeFile.path;
|
||||
|
||||
function manager(loader) loader.sandbox("sdk/simple-storage").manager;
|
||||
function manager(loader) {
|
||||
return loader.sandbox("sdk/simple-storage").manager;
|
||||
}
|
||||
|
||||
exports.testSetGet = function (assert, done) {
|
||||
// Load the module once, set a value.
|
||||
|
@ -343,7 +343,6 @@ pref("image.mem.surfacecache.max_size_kb", 131072); // 128MB
|
||||
pref("image.mem.surfacecache.size_factor", 8); // 1/8 of main memory
|
||||
pref("image.mem.surfacecache.discard_factor", 2); // Discard 1/2 of the surface cache at a time.
|
||||
pref("image.mem.surfacecache.min_expiration_ms", 86400000); // 24h, we rely on the out of memory hook
|
||||
pref("image.onload.decode.limit", 24); /* don't decode more than 24 images eagerly */
|
||||
|
||||
// XXX this isn't a good check for "are touch events supported", but
|
||||
// we don't really have a better one at the moment.
|
||||
|
@ -24,7 +24,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="95bc959ff0e4ce73e1c9ed5e6916d84922d0a3cc"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="21f7fdaf4ee341022a0416e9186e7b07218cc4b1"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
<!-- Stock Android things -->
|
||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
|
||||
|
@ -24,7 +24,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="95bc959ff0e4ce73e1c9ed5e6916d84922d0a3cc"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="21f7fdaf4ee341022a0416e9186e7b07218cc4b1"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
<!-- Stock Android things -->
|
||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
|
||||
|
@ -20,7 +20,7 @@
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="91cac94948094cfdcd00cba5c6483e27e80cb3b0"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="956700d9754349b630a34551750ae6353614b6aa"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="27de93fe66c3e80e157d157bd52ca99565351669"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="95bc959ff0e4ce73e1c9ed5e6916d84922d0a3cc"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="21f7fdaf4ee341022a0416e9186e7b07218cc4b1"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
@ -23,7 +23,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="95bc959ff0e4ce73e1c9ed5e6916d84922d0a3cc"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="21f7fdaf4ee341022a0416e9186e7b07218cc4b1"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
<!-- Stock Android things -->
|
||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
|
||||
|
@ -23,7 +23,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="95bc959ff0e4ce73e1c9ed5e6916d84922d0a3cc"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="21f7fdaf4ee341022a0416e9186e7b07218cc4b1"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
<!-- Stock Android things -->
|
||||
<project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="ffc05a232799fe8fcb3e47b7440b52b1fb4244c0"/>
|
||||
|
@ -24,7 +24,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="95bc959ff0e4ce73e1c9ed5e6916d84922d0a3cc"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="21f7fdaf4ee341022a0416e9186e7b07218cc4b1"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
<!-- Stock Android things -->
|
||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
|
||||
|
@ -24,7 +24,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="95bc959ff0e4ce73e1c9ed5e6916d84922d0a3cc"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="21f7fdaf4ee341022a0416e9186e7b07218cc4b1"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
<!-- Stock Android things -->
|
||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
|
||||
|
@ -21,7 +21,7 @@
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="91cac94948094cfdcd00cba5c6483e27e80cb3b0"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="956700d9754349b630a34551750ae6353614b6aa"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="27de93fe66c3e80e157d157bd52ca99565351669"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="95bc959ff0e4ce73e1c9ed5e6916d84922d0a3cc"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="21f7fdaf4ee341022a0416e9186e7b07218cc4b1"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
@ -24,7 +24,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="95bc959ff0e4ce73e1c9ed5e6916d84922d0a3cc"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="21f7fdaf4ee341022a0416e9186e7b07218cc4b1"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
<!-- Stock Android things -->
|
||||
<project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="ffc05a232799fe8fcb3e47b7440b52b1fb4244c0"/>
|
||||
|
@ -159,12 +159,14 @@ let RemoteDirectoryLinksProvider = {
|
||||
*/
|
||||
_newTabHasInadjacentSite: false,
|
||||
|
||||
get _observedPrefs() Object.freeze({
|
||||
get _observedPrefs() {
|
||||
return Object.freeze({
|
||||
enhanced: PREF_NEWTAB_ENHANCED,
|
||||
linksURL: PREF_DIRECTORY_SOURCE,
|
||||
matchOSLocale: PREF_MATCH_OS_LOCALE,
|
||||
prefSelectedLocale: PREF_SELECTED_LOCALE,
|
||||
}),
|
||||
});
|
||||
},
|
||||
|
||||
get _linksURL() {
|
||||
if (!this.__linksURL) {
|
||||
|
@ -7255,9 +7255,6 @@ if test -z "$MOZ_MEMORY"; then
|
||||
esac
|
||||
else
|
||||
AC_DEFINE(MOZ_MEMORY)
|
||||
if test -n "$NIGHTLY_BUILD"; then
|
||||
MOZ_JEMALLOC4=1
|
||||
fi
|
||||
if test -n "$MOZ_JEMALLOC4"; then
|
||||
AC_DEFINE(MOZ_JEMALLOC4)
|
||||
fi
|
||||
|
@ -52,7 +52,9 @@ var Converter = Class({
|
||||
"nsIRequestObserver"
|
||||
],
|
||||
|
||||
get wrappedJSObject() this,
|
||||
get wrappedJSObject() {
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* This component works as such:
|
||||
|
@ -39,7 +39,9 @@ var Sniffer = Class({
|
||||
"nsIContentSniffer",
|
||||
],
|
||||
|
||||
get wrappedJSObject() this,
|
||||
get wrappedJSObject() {
|
||||
return this;
|
||||
},
|
||||
|
||||
getMIMETypeFromContent: function(aRequest, aData, aLength) {
|
||||
// JSON View is enabled only for top level loads only.
|
||||
|
@ -82,6 +82,7 @@
|
||||
#include "nsViewSourceHandler.h"
|
||||
#include "nsWhitespaceTokenizer.h"
|
||||
#include "nsICookieService.h"
|
||||
#include "nsIConsoleReportCollector.h"
|
||||
|
||||
// we want to explore making the document own the load group
|
||||
// so we can associate the document URI with the load group.
|
||||
@ -5015,17 +5016,7 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
|
||||
error.AssignLiteral("corruptedContentError");
|
||||
break;
|
||||
case NS_ERROR_INTERCEPTION_FAILED:
|
||||
case NS_ERROR_OPAQUE_INTERCEPTION_DISABLED:
|
||||
case NS_ERROR_BAD_OPAQUE_INTERCEPTION_REQUEST_MODE:
|
||||
case NS_ERROR_INTERCEPTED_ERROR_RESPONSE:
|
||||
case NS_ERROR_INTERCEPTED_USED_RESPONSE:
|
||||
case NS_ERROR_CLIENT_REQUEST_OPAQUE_INTERCEPTION:
|
||||
case NS_ERROR_BAD_OPAQUE_REDIRECT_INTERCEPTION:
|
||||
case NS_ERROR_INTERCEPTION_CANCELED:
|
||||
case NS_ERROR_REJECTED_RESPONSE_INTERCEPTION:
|
||||
// ServiceWorker intercepted request, but something went wrong.
|
||||
nsContentUtils::MaybeReportInterceptionErrorToConsole(GetDocument(),
|
||||
aError);
|
||||
error.AssignLiteral("corruptedContentError");
|
||||
break;
|
||||
default:
|
||||
@ -7357,6 +7348,11 @@ nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIConsoleReportCollector> reporter = do_QueryInterface(aChannel);
|
||||
if (reporter) {
|
||||
reporter->FlushConsoleReports(GetDocument());
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> url;
|
||||
nsresult rv = aChannel->GetURI(getter_AddRefs(url));
|
||||
if (NS_FAILED(rv)) {
|
||||
@ -7683,13 +7679,6 @@ nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
|
||||
aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE ||
|
||||
aStatus == NS_ERROR_REMOTE_XUL ||
|
||||
aStatus == NS_ERROR_INTERCEPTION_FAILED ||
|
||||
aStatus == NS_ERROR_OPAQUE_INTERCEPTION_DISABLED ||
|
||||
aStatus == NS_ERROR_BAD_OPAQUE_INTERCEPTION_REQUEST_MODE ||
|
||||
aStatus == NS_ERROR_INTERCEPTED_ERROR_RESPONSE ||
|
||||
aStatus == NS_ERROR_INTERCEPTED_USED_RESPONSE ||
|
||||
aStatus == NS_ERROR_CLIENT_REQUEST_OPAQUE_INTERCEPTION ||
|
||||
aStatus == NS_ERROR_INTERCEPTION_CANCELED ||
|
||||
aStatus == NS_ERROR_REJECTED_RESPONSE_INTERCEPTION ||
|
||||
NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_SECURITY) {
|
||||
// Errors to be shown for any frame
|
||||
DisplayLoadError(aStatus, url, nullptr, aChannel);
|
||||
|
@ -67,15 +67,18 @@ ActivitiesDb.prototype = {
|
||||
|
||||
let self = this;
|
||||
|
||||
/**
|
||||
* WARNING!! Before upgrading the Activities DB take into account that an
|
||||
* OTA unregisters all the activities and reinstalls them during the first
|
||||
* run process. Check Bug 1193503.
|
||||
*/
|
||||
|
||||
function upgrade(currentVersion) {
|
||||
let next = upgrade.bind(self, currentVersion + 1);
|
||||
switch (currentVersion) {
|
||||
case 0:
|
||||
self.createSchema(aDb, next);
|
||||
break;
|
||||
case 1:
|
||||
self.upgradeSchemaVersion2(aDb, aTransaction, next);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,47 +97,6 @@ ActivitiesDb.prototype = {
|
||||
aNext();
|
||||
},
|
||||
|
||||
upgradeSchemaVersion2: function(aDb, aTransaction, aNext) {
|
||||
debug("Upgrading DB to version 2");
|
||||
|
||||
// In order to be able to have multiple activities with same name
|
||||
// but different descriptions, we need to update the keypath from
|
||||
// a hash made from {manifest, name} to a hash made from {manifest,
|
||||
// name, description}.
|
||||
//
|
||||
// Unfortunately, updating the keypath is not allowed by IDB, so we
|
||||
// need to remove and recreate the activities object store.
|
||||
|
||||
let activities = [];
|
||||
let objectStore = aTransaction.objectStore(STORE_NAME);
|
||||
objectStore.openCursor().onsuccess = (event) => {
|
||||
let cursor = event.target.result;
|
||||
if (!cursor) {
|
||||
aDb.deleteObjectStore(STORE_NAME);
|
||||
|
||||
let objectStore = aDb.createObjectStore(STORE_NAME, { keyPath: "id" });
|
||||
|
||||
// indexes
|
||||
objectStore.createIndex("name", "name", { unique: false });
|
||||
objectStore.createIndex("manifest", "manifest", { unique: false });
|
||||
|
||||
this.add(activities, () => {
|
||||
debug("DB upgraded to version 2");
|
||||
aNext();
|
||||
}, () => {
|
||||
dump("Error upgrading DB to version 2 " + error + "\n");
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let activity = cursor.value;
|
||||
debug("Upgrading activity " + JSON.stringify(activity));
|
||||
activity.id = this.createId(activity);
|
||||
activities.push(activity);
|
||||
cursor.continue();
|
||||
};
|
||||
},
|
||||
|
||||
// unique ids made of (uri, action)
|
||||
createId: function actdb_createId(aObject) {
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
|
@ -365,6 +365,11 @@ this.PermissionsTable = { geolocation: {
|
||||
privileged: ALLOW_ACTION,
|
||||
certified: ALLOW_ACTION
|
||||
},
|
||||
"audio-capture:3gpp2": {
|
||||
app: DENY_ACTION,
|
||||
privileged: ALLOW_ACTION,
|
||||
certified: ALLOW_ACTION
|
||||
},
|
||||
"nfc": {
|
||||
app: DENY_ACTION,
|
||||
privileged: ALLOW_ACTION,
|
||||
|
86
dom/base/ConsoleReportCollector.cpp
Normal file
86
dom/base/ConsoleReportCollector.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/ConsoleReportCollector.h"
|
||||
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
NS_IMPL_ISUPPORTS(ConsoleReportCollector, nsIConsoleReportCollector)
|
||||
|
||||
ConsoleReportCollector::ConsoleReportCollector()
|
||||
: mMutex("mozilla::ConsoleReportCollector")
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ConsoleReportCollector::AddConsoleReport(uint32_t aErrorFlags,
|
||||
const nsACString& aCategory,
|
||||
nsContentUtils::PropertiesFile aPropertiesFile,
|
||||
const nsACString& aSourceFileURI,
|
||||
uint32_t aLineNumber,
|
||||
uint32_t aColumnNumber,
|
||||
const nsACString& aMessageName,
|
||||
const nsTArray<nsString>& aStringParams)
|
||||
{
|
||||
// any thread
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
mPendingReports.AppendElement(PendingReport(aErrorFlags, aCategory,
|
||||
aPropertiesFile, aSourceFileURI,
|
||||
aLineNumber, aColumnNumber,
|
||||
aMessageName, aStringParams));
|
||||
}
|
||||
|
||||
void
|
||||
ConsoleReportCollector::FlushConsoleReports(nsIDocument* aDocument)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsTArray<PendingReport> reports;
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mPendingReports.SwapElements(reports);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < reports.Length(); ++i) {
|
||||
PendingReport& report = reports[i];
|
||||
|
||||
// It would be nice if we did not have to do this since ReportToConsole()
|
||||
// just turns around and converts it back to a spec.
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(uri), report.mSourceFileURI);
|
||||
if (NS_FAILED(rv)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Convert back from nsTArray<nsString> to the char16_t** format required
|
||||
// by our l10n libraries and ReportToConsole. (bug 1219762)
|
||||
UniquePtr<const char16_t*[]> params;
|
||||
uint32_t paramsLength = report.mStringParams.Length();
|
||||
if (paramsLength > 0) {
|
||||
params = MakeUnique<const char16_t*[]>(paramsLength);
|
||||
for (uint32_t j = 0; j < paramsLength; ++j) {
|
||||
params[j] = report.mStringParams[j].get();
|
||||
}
|
||||
}
|
||||
|
||||
nsContentUtils::ReportToConsole(report.mErrorFlags, report.mCategory,
|
||||
aDocument, report.mPropertiesFile,
|
||||
report.mMessageName.get(),
|
||||
params.get(),
|
||||
paramsLength, uri, EmptyString(),
|
||||
report.mLineNumber, report.mColumnNumber);
|
||||
}
|
||||
}
|
||||
|
||||
ConsoleReportCollector::~ConsoleReportCollector()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
73
dom/base/ConsoleReportCollector.h
Normal file
73
dom/base/ConsoleReportCollector.h
Normal file
@ -0,0 +1,73 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_ConsoleReportCollector_h
|
||||
#define mozilla_ConsoleReportCollector_h
|
||||
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsIConsoleReportCollector.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ConsoleReportCollector final : public nsIConsoleReportCollector
|
||||
{
|
||||
public:
|
||||
ConsoleReportCollector();
|
||||
|
||||
void
|
||||
AddConsoleReport(uint32_t aErrorFlags, const nsACString& aCategory,
|
||||
nsContentUtils::PropertiesFile aPropertiesFile,
|
||||
const nsACString& aSourceFileURI,
|
||||
uint32_t aLineNumber, uint32_t aColumnNumber,
|
||||
const nsACString& aMessageName,
|
||||
const nsTArray<nsString>& aStringParams) override;
|
||||
|
||||
void
|
||||
FlushConsoleReports(nsIDocument* aDocument) override;
|
||||
|
||||
private:
|
||||
~ConsoleReportCollector();
|
||||
|
||||
struct PendingReport
|
||||
{
|
||||
PendingReport(uint32_t aErrorFlags, const nsACString& aCategory,
|
||||
nsContentUtils::PropertiesFile aPropertiesFile,
|
||||
const nsACString& aSourceFileURI, uint32_t aLineNumber,
|
||||
uint32_t aColumnNumber, const nsACString& aMessageName,
|
||||
const nsTArray<nsString>& aStringParams)
|
||||
: mErrorFlags(aErrorFlags)
|
||||
, mCategory(aCategory)
|
||||
, mPropertiesFile(aPropertiesFile)
|
||||
, mSourceFileURI(aSourceFileURI)
|
||||
, mLineNumber(aLineNumber)
|
||||
, mColumnNumber(aColumnNumber)
|
||||
, mMessageName(aMessageName)
|
||||
, mStringParams(aStringParams)
|
||||
{ }
|
||||
|
||||
const uint32_t mErrorFlags;
|
||||
const nsCString mCategory;
|
||||
const nsContentUtils::PropertiesFile mPropertiesFile;
|
||||
const nsCString mSourceFileURI;
|
||||
const uint32_t mLineNumber;
|
||||
const uint32_t mColumnNumber;
|
||||
const nsCString mMessageName;
|
||||
const nsTArray<nsString> mStringParams;
|
||||
};
|
||||
|
||||
Mutex mMutex;
|
||||
|
||||
// protected by mMutex
|
||||
nsTArray<PendingReport> mPendingReports;
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_ConsoleReportCollector_h
|
@ -84,6 +84,7 @@ EXPORTS += [
|
||||
'nsHostObjectURI.h',
|
||||
'nsIAnimationObserver.h',
|
||||
'nsIAttribute.h',
|
||||
'nsIConsoleReportCollector.h',
|
||||
'nsIContent.h',
|
||||
'nsIContentInlines.h',
|
||||
'nsIContentIterator.h',
|
||||
@ -142,6 +143,7 @@ if CONFIG['MOZ_WEBRTC']:
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
'ConsoleReportCollector.h',
|
||||
'CORSMode.h',
|
||||
'FeedWriterEnabled.h',
|
||||
'TextInputProcessor.h',
|
||||
@ -222,6 +224,7 @@ UNIFIED_SOURCES += [
|
||||
'ChromeUtils.cpp',
|
||||
'Comment.cpp',
|
||||
'Console.cpp',
|
||||
'ConsoleReportCollector.cpp',
|
||||
'Crypto.cpp',
|
||||
'DirectionalityUtils.cpp',
|
||||
'DocumentFragment.cpp',
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/LoadInfo.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/DocumentFragment.h"
|
||||
#include "mozilla/dom/DOMTypes.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
@ -188,6 +189,7 @@
|
||||
#include "nsViewManager.h"
|
||||
#include "nsViewportInfo.h"
|
||||
#include "nsWidgetsCID.h"
|
||||
#include "nsIWindowProvider.h"
|
||||
#include "nsWrapperCacheInlines.h"
|
||||
#include "nsXULPopupManager.h"
|
||||
#include "xpcprivate.h" // nsXPConnect
|
||||
@ -3477,42 +3479,6 @@ nsContentUtils::ReportToConsole(uint32_t aErrorFlags,
|
||||
aLineNumber, aColumnNumber);
|
||||
}
|
||||
|
||||
/* static */ nsresult
|
||||
nsContentUtils::MaybeReportInterceptionErrorToConsole(nsIDocument* aDocument,
|
||||
nsresult aError)
|
||||
{
|
||||
const char* messageName = nullptr;
|
||||
if (aError == NS_ERROR_INTERCEPTION_FAILED) {
|
||||
messageName = "InterceptionFailed";
|
||||
} else if (aError == NS_ERROR_OPAQUE_INTERCEPTION_DISABLED) {
|
||||
messageName = "OpaqueInterceptionDisabled";
|
||||
} else if (aError == NS_ERROR_BAD_OPAQUE_INTERCEPTION_REQUEST_MODE) {
|
||||
messageName = "BadOpaqueInterceptionRequestMode";
|
||||
} else if (aError == NS_ERROR_INTERCEPTED_ERROR_RESPONSE) {
|
||||
messageName = "InterceptedErrorResponse";
|
||||
} else if (aError == NS_ERROR_INTERCEPTED_USED_RESPONSE) {
|
||||
messageName = "InterceptedUsedResponse";
|
||||
} else if (aError == NS_ERROR_CLIENT_REQUEST_OPAQUE_INTERCEPTION) {
|
||||
messageName = "ClientRequestOpaqueInterception";
|
||||
} else if (aError == NS_ERROR_BAD_OPAQUE_REDIRECT_INTERCEPTION) {
|
||||
messageName = "BadOpaqueRedirectInterception";
|
||||
} else if (aError == NS_ERROR_INTERCEPTION_CANCELED) {
|
||||
messageName = "InterceptionCanceled";
|
||||
} else if (aError == NS_ERROR_REJECTED_RESPONSE_INTERCEPTION) {
|
||||
messageName = "InterceptionRejectedResponse";
|
||||
}
|
||||
|
||||
if (messageName) {
|
||||
return ReportToConsole(nsIScriptError::warningFlag,
|
||||
NS_LITERAL_CSTRING("Service Worker Interception"),
|
||||
aDocument,
|
||||
nsContentUtils::eDOM_PROPERTIES,
|
||||
messageName);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
/* static */ nsresult
|
||||
nsContentUtils::ReportToConsoleNonLocalized(const nsAString& aErrorText,
|
||||
@ -5206,6 +5172,14 @@ nsContentUtils::RemoveScriptBlocker()
|
||||
sBlockedScriptRunners->RemoveElementsAt(originalFirstBlocker, blockersCount);
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsIWindowProvider*
|
||||
nsContentUtils::GetWindowProviderForContentProcess()
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsContentProcess());
|
||||
return ContentChild::GetSingleton();
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
nsContentUtils::WarnScriptWasIgnored(nsIDocument* aDocument)
|
||||
|
@ -102,6 +102,7 @@ class nsWrapperCache;
|
||||
class nsAttrValue;
|
||||
class nsITransferable;
|
||||
class nsPIWindowRoot;
|
||||
class nsIWindowProvider;
|
||||
|
||||
struct JSPropertyDescriptor;
|
||||
struct JSRuntime;
|
||||
@ -900,9 +901,6 @@ public:
|
||||
uint32_t aLineNumber = 0,
|
||||
uint32_t aColumnNumber = 0);
|
||||
|
||||
static nsresult
|
||||
MaybeReportInterceptionErrorToConsole(nsIDocument* aDocument, nsresult aError);
|
||||
|
||||
static void LogMessageToConsole(const char* aMsg, ...);
|
||||
|
||||
/**
|
||||
@ -1640,6 +1638,11 @@ public:
|
||||
return sScriptBlockerCount == 0;
|
||||
}
|
||||
|
||||
// XXXcatalinb: workaround for weird include error when trying to reference
|
||||
// ipdl types in WindowWatcher.
|
||||
static nsIWindowProvider*
|
||||
GetWindowProviderForContentProcess();
|
||||
|
||||
/**
|
||||
* Call this function if !IsSafeToRunScript() and we fail to run the script
|
||||
* (rather than using AddScriptRunner as we usually do). |aDocument| is
|
||||
|
@ -2117,9 +2117,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
|
||||
tmp->mInUnlinkOrDeletion = false;
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
static bool sPrefsInitialized = false;
|
||||
static uint32_t sOnloadDecodeLimit = 0;
|
||||
|
||||
nsresult
|
||||
nsDocument::Init()
|
||||
{
|
||||
@ -2127,11 +2124,6 @@ nsDocument::Init()
|
||||
return NS_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!sPrefsInitialized) {
|
||||
sPrefsInitialized = true;
|
||||
Preferences::AddUintVarCache(&sOnloadDecodeLimit, "image.onload.decode.limit", 0);
|
||||
}
|
||||
|
||||
// Force initialization.
|
||||
nsINode::nsSlots* slots = Slots();
|
||||
|
||||
@ -10522,12 +10514,8 @@ nsDocument::AddImage(imgIRequest* aImage)
|
||||
|
||||
// If this is the first insertion and we're locking images, lock this image
|
||||
// too.
|
||||
if (oldCount == 0) {
|
||||
if (mLockingImages)
|
||||
if (oldCount == 0 && mLockingImages) {
|
||||
rv = aImage->LockImage();
|
||||
if (NS_SUCCEEDED(rv) && (!sOnloadDecodeLimit ||
|
||||
mImageTracker.Count() < sOnloadDecodeLimit))
|
||||
rv = aImage->StartDecoding();
|
||||
}
|
||||
|
||||
// If this is the first insertion and we're animating images, request
|
||||
@ -10658,7 +10646,6 @@ PLDHashOperator LockEnumerator(imgIRequest* aKey,
|
||||
void* userArg)
|
||||
{
|
||||
aKey->LockImage();
|
||||
aKey->RequestDecode();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
|
81
dom/base/nsIConsoleReportCollector.h
Normal file
81
dom/base/nsIConsoleReportCollector.h
Normal file
@ -0,0 +1,81 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsIConsoleReportCollector_h
|
||||
#define nsIConsoleReportCollector_h
|
||||
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsTArrayForwardDeclare.h"
|
||||
|
||||
class nsACString;
|
||||
class nsIDocument;
|
||||
class nsString;
|
||||
|
||||
#define NS_NSICONSOLEREPORTCOLLECTOR_IID \
|
||||
{0xdd98a481, 0xd2c4, 0x4203, {0x8d, 0xfa, 0x85, 0xbf, 0xd7, 0xdc, 0xd7, 0x05}}
|
||||
|
||||
// An interface for saving reports until we can flush them to the correct
|
||||
// window at a later time.
|
||||
class NS_NO_VTABLE nsIConsoleReportCollector : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_NSICONSOLEREPORTCOLLECTOR_IID)
|
||||
|
||||
// Add a pending report to be later displayed on the console. This may be
|
||||
// called from any thread.
|
||||
//
|
||||
// aErrorFlags A nsIScriptError flags value.
|
||||
// aCategory Name of module reporting error.
|
||||
// aPropertiesFile Properties file containing localized message.
|
||||
// aSourceFileURI The URI of the script generating the error. Must be a URI
|
||||
// spec.
|
||||
// aLineNumber The line number where the error was generated. May be 0 if
|
||||
// the line number is not known.
|
||||
// aColumnNumber The column number where the error was generated. May be 0
|
||||
// if the line number is not known.
|
||||
// aMessageName The name of the localized message contained in the
|
||||
// properties file.
|
||||
// aStringParams An array of nsString parameters to use when localizing the
|
||||
// message.
|
||||
virtual void
|
||||
AddConsoleReport(uint32_t aErrorFlags, const nsACString& aCategory,
|
||||
nsContentUtils::PropertiesFile aPropertiesFile,
|
||||
const nsACString& aSourceFileURI, uint32_t aLineNumber,
|
||||
uint32_t aColumnNumber, const nsACString& aMessageName,
|
||||
const nsTArray<nsString>& aStringParams) = 0;
|
||||
|
||||
// A version of AddConsoleReport() that accepts the message parameters
|
||||
// as variable nsString arguments. Note, the parameters must be exactly
|
||||
// nsString and not another string class. All other args the same as
|
||||
// AddConsoleReport().
|
||||
template<typename... Params>
|
||||
void
|
||||
AddConsoleReport(uint32_t aErrorFlags, const nsACString& aCategory,
|
||||
nsContentUtils::PropertiesFile aPropertiesFile,
|
||||
const nsACString& aSourceFileURI, uint32_t aLineNumber,
|
||||
uint32_t aColumnNumber, const nsACString& aMessageName,
|
||||
Params... aParams)
|
||||
{
|
||||
nsTArray<nsString> params;
|
||||
mozilla::dom::StringArrayAppender::Append(params, sizeof...(Params), aParams...);
|
||||
AddConsoleReport(aErrorFlags, aCategory, aPropertiesFile, aSourceFileURI,
|
||||
aLineNumber, aColumnNumber, aMessageName, params);
|
||||
}
|
||||
|
||||
// Flush all pending reports to the console.
|
||||
//
|
||||
// aDocument An optional document representing where to flush the
|
||||
// reports. If provided, then the corresponding window's
|
||||
// web console will get the reports. Otherwise the reports
|
||||
// go to the browser console.
|
||||
virtual void
|
||||
FlushConsoleReports(nsIDocument* aDocument) = 0;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsIConsoleReportCollector, NS_NSICONSOLEREPORTCOLLECTOR_IID)
|
||||
|
||||
#endif // nsIConsoleReportCollector_h
|
@ -229,53 +229,6 @@ nsImageLoadingContent::OnLoadComplete(imgIRequest* aRequest, nsresult aStatus)
|
||||
MOZ_ASSERT(aRequest == mCurrentRequest,
|
||||
"One way or another, we should be current by now");
|
||||
|
||||
// We just loaded all the data we're going to get. If we're visible and
|
||||
// haven't done an initial paint (*), we want to make sure the image starts
|
||||
// decoding immediately, for two reasons:
|
||||
//
|
||||
// 1) This image is sitting idle but might need to be decoded as soon as we
|
||||
// start painting, in which case we've wasted time.
|
||||
//
|
||||
// 2) We want to block onload until all visible images are decoded. We do this
|
||||
// by blocking onload until all in-progress decodes get at least one frame
|
||||
// decoded. However, if all the data comes in while painting is suppressed
|
||||
// (ie, before the initial paint delay is finished), we fire onload without
|
||||
// doing a paint first. This means that decode-on-draw images don't start
|
||||
// decoding, so we can't wait for them to finish. See bug 512435.
|
||||
//
|
||||
// (*) IsPaintingSuppressed returns false if we haven't gotten the initial
|
||||
// reflow yet, so we have to test !DidInitialize || IsPaintingSuppressed.
|
||||
// It's possible for painting to be suppressed for reasons other than the
|
||||
// initial paint delay (for example, being in the bfcache), but we probably
|
||||
// aren't loading images in those situations.
|
||||
|
||||
// XXXkhuey should this be GetOurCurrentDoc? Decoding if we're not in
|
||||
// the document seems silly.
|
||||
nsIDocument* doc = GetOurOwnerDoc();
|
||||
nsIPresShell* shell = doc ? doc->GetShell() : nullptr;
|
||||
if (shell && shell->IsVisible() &&
|
||||
(!shell->DidInitialize() || shell->IsPaintingSuppressed())) {
|
||||
|
||||
nsIFrame* f = GetOurPrimaryFrame();
|
||||
// If we haven't gotten a frame yet either we aren't going to (so don't
|
||||
// bother kicking off a decode), or we will get very soon on the next
|
||||
// refresh driver tick when it flushes. And it will most likely be a
|
||||
// specific image type frame (we only create generic (ie inline) type
|
||||
// frames for images that don't have a size, and since we have all the data
|
||||
// we should have the size) which will check its own visibility on its
|
||||
// first reflow.
|
||||
if (f) {
|
||||
// If we've gotten a frame and that frame has called FrameCreate and that
|
||||
// frame has been reflowed then we know that it checked it's own visibility
|
||||
// so we can trust our visible count and we don't start decode if we are not
|
||||
// visible.
|
||||
if (!mFrameCreateCalled || (f->GetStateBits() & NS_FRAME_FIRST_REFLOW) ||
|
||||
mVisibleCount > 0 || shell->AssumeAllImagesVisible()) {
|
||||
mCurrentRequest->StartDecoding();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fire the appropriate DOM event.
|
||||
if (NS_SUCCEEDED(aStatus)) {
|
||||
FireEvent(NS_LITERAL_STRING("load"));
|
||||
|
@ -126,7 +126,8 @@ function setUp() {
|
||||
['network.disable.ipc.security', true]]}, runNextTest);
|
||||
}
|
||||
|
||||
function makeKillTest(isApp) function testKill() {
|
||||
function makeKillTest(isApp) {
|
||||
return function testKill() {
|
||||
loadBrowser(isApp, function (iframe) {
|
||||
// We want to make sure we get notified on both the frame and
|
||||
// process message managers.
|
||||
@ -142,6 +143,7 @@ function makeKillTest(isApp) function testKill() {
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
|
@ -495,6 +495,7 @@ DOMInterfaces = {
|
||||
'binaryNames': {
|
||||
'request': 'request_'
|
||||
},
|
||||
'implicitJSContext': [ 'respondWith' ],
|
||||
},
|
||||
|
||||
'FileReader': {
|
||||
|
@ -743,7 +743,7 @@ class CGInterfaceObjectJSClass(CGThing):
|
||||
nullptr, /* unwatch */
|
||||
nullptr, /* getElements */
|
||||
nullptr, /* enumerate */
|
||||
nullptr, /* thisObject */
|
||||
nullptr, /* thisValue */
|
||||
InterfaceObjectToString, /* funToString */
|
||||
}
|
||||
},
|
||||
|
@ -177,7 +177,7 @@ public:
|
||||
// this method always sets Event.defaultPrevented true for web contents.
|
||||
// If default action handler calls this, web applications meet wrong
|
||||
// defaultPrevented value.
|
||||
void PreventDefault(JSContext* aCx);
|
||||
virtual void PreventDefault(JSContext* aCx);
|
||||
|
||||
// You MUST NOT call DefaultPrevented(JSContext*) from C++ code. This may
|
||||
// return false even if PreventDefault() has been called.
|
||||
@ -298,7 +298,8 @@ private:
|
||||
} // namespace mozilla
|
||||
|
||||
#define NS_FORWARD_TO_EVENT \
|
||||
NS_FORWARD_NSIDOMEVENT(Event::)
|
||||
NS_FORWARD_NSIDOMEVENT(Event::) \
|
||||
virtual void PreventDefault(JSContext* aCx) override { Event::PreventDefault(aCx); }
|
||||
|
||||
#define NS_FORWARD_NSIDOMEVENT_NO_SERIALIZATION_NO_DUPLICATION(_to) \
|
||||
NS_IMETHOD GetType(nsAString& aType) override { return _to GetType(aType); } \
|
||||
@ -326,7 +327,8 @@ private:
|
||||
NS_IMETHOD_(Event*) InternalDOMEvent() override { return _to InternalDOMEvent(); }
|
||||
|
||||
#define NS_FORWARD_TO_EVENT_NO_SERIALIZATION_NO_DUPLICATION \
|
||||
NS_FORWARD_NSIDOMEVENT_NO_SERIALIZATION_NO_DUPLICATION(Event::)
|
||||
NS_FORWARD_NSIDOMEVENT_NO_SERIALIZATION_NO_DUPLICATION(Event::) \
|
||||
virtual void PreventDefault(JSContext* aCx) override { Event::PreventDefault(aCx); }
|
||||
|
||||
inline nsISupports*
|
||||
ToSupports(mozilla::dom::Event* e)
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "mozilla/layers/ImageBridgeChild.h"
|
||||
#include "mozilla/layers/PCompositorChild.h"
|
||||
#include "mozilla/layers/SharedBufferManagerChild.h"
|
||||
#include "mozilla/layout/RenderFrameChild.h"
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
#include "mozilla/plugins/PluginInstanceParent.h"
|
||||
#include "mozilla/plugins/PluginModuleParent.h"
|
||||
@ -216,6 +217,7 @@ using namespace mozilla::gmp;
|
||||
using namespace mozilla::hal_sandbox;
|
||||
using namespace mozilla::ipc;
|
||||
using namespace mozilla::layers;
|
||||
using namespace mozilla::layout;
|
||||
using namespace mozilla::net;
|
||||
using namespace mozilla::jsipc;
|
||||
using namespace mozilla::psm;
|
||||
@ -608,7 +610,8 @@ ContentChild::~ContentChild()
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(ContentChild)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIContentChild)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIWindowProvider)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentChild)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
bool
|
||||
@ -741,6 +744,176 @@ ContentChild::SetProcessName(const nsAString& aName, bool aDontOverride)
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContentChild::ProvideWindow(nsIDOMWindow* aParent,
|
||||
uint32_t aChromeFlags,
|
||||
bool aCalledFromJS,
|
||||
bool aPositionSpecified,
|
||||
bool aSizeSpecified,
|
||||
nsIURI* aURI,
|
||||
const nsAString& aName,
|
||||
const nsACString& aFeatures,
|
||||
bool* aWindowIsNew,
|
||||
nsIDOMWindow** aReturn)
|
||||
{
|
||||
return ProvideWindowCommon(nullptr, aParent, false, aChromeFlags,
|
||||
aCalledFromJS, aPositionSpecified,
|
||||
aSizeSpecified, aURI, aName, aFeatures,
|
||||
aWindowIsNew, aReturn);
|
||||
}
|
||||
|
||||
nsresult
|
||||
ContentChild::ProvideWindowCommon(TabChild* aTabOpener,
|
||||
nsIDOMWindow* aParent,
|
||||
bool aIframeMoz,
|
||||
uint32_t aChromeFlags,
|
||||
bool aCalledFromJS,
|
||||
bool aPositionSpecified,
|
||||
bool aSizeSpecified,
|
||||
nsIURI* aURI,
|
||||
const nsAString& aName,
|
||||
const nsACString& aFeatures,
|
||||
bool* aWindowIsNew,
|
||||
nsIDOMWindow** aReturn)
|
||||
{
|
||||
*aReturn = nullptr;
|
||||
|
||||
nsAutoPtr<IPCTabContext> ipcContext;
|
||||
TabId openerTabId = TabId(0);
|
||||
|
||||
if (aTabOpener) {
|
||||
PopupIPCTabContext context;
|
||||
openerTabId = aTabOpener->GetTabId();
|
||||
context.opener() = openerTabId;
|
||||
context.isBrowserElement() = aTabOpener->IsBrowserElement();
|
||||
ipcContext = new IPCTabContext(context);
|
||||
} else {
|
||||
// It's possible to not have a TabChild opener in the case
|
||||
// of ServiceWorker::OpenWindow.
|
||||
UnsafeIPCTabContext unsafeTabContext;
|
||||
ipcContext = new IPCTabContext(unsafeTabContext);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(ipcContext);
|
||||
TabId tabId;
|
||||
SendAllocateTabId(openerTabId,
|
||||
*ipcContext,
|
||||
GetID(),
|
||||
&tabId);
|
||||
|
||||
TabContext newTabContext = aTabOpener ? *aTabOpener : TabContext();
|
||||
RefPtr<TabChild> newChild = new TabChild(this, tabId,
|
||||
newTabContext, aChromeFlags);
|
||||
if (NS_FAILED(newChild->Init())) {
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
if (aTabOpener) {
|
||||
MOZ_ASSERT(ipcContext->type() == IPCTabContext::TPopupIPCTabContext);
|
||||
ipcContext->get_PopupIPCTabContext().opener() = aTabOpener;
|
||||
}
|
||||
|
||||
unused << SendPBrowserConstructor(
|
||||
// We release this ref in DeallocPBrowserChild
|
||||
RefPtr<TabChild>(newChild).forget().take(),
|
||||
tabId, *ipcContext, aChromeFlags,
|
||||
GetID(), IsForApp(), IsForBrowser());
|
||||
|
||||
nsAutoCString spec;
|
||||
if (aURI) {
|
||||
aURI->GetSpec(spec);
|
||||
}
|
||||
|
||||
NS_ConvertUTF8toUTF16 url(spec);
|
||||
nsString name(aName);
|
||||
nsAutoCString features(aFeatures);
|
||||
nsTArray<FrameScriptInfo> frameScripts;
|
||||
nsCString urlToLoad;
|
||||
|
||||
if (aIframeMoz) {
|
||||
MOZ_ASSERT(aTabOpener);
|
||||
newChild->SendBrowserFrameOpenWindow(aTabOpener, url, name,
|
||||
NS_ConvertUTF8toUTF16(features),
|
||||
aWindowIsNew);
|
||||
} else {
|
||||
nsAutoCString baseURIString;
|
||||
if (aTabOpener) {
|
||||
nsCOMPtr<nsPIDOMWindow> opener = do_QueryInterface(aParent);
|
||||
nsCOMPtr<nsIDocument> doc = opener->GetDoc();
|
||||
nsCOMPtr<nsIURI> baseURI = doc->GetDocBaseURI();
|
||||
if (!baseURI) {
|
||||
NS_ERROR("nsIDocument didn't return a base URI");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
baseURI->GetSpec(baseURIString);
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
if (!SendCreateWindow(aTabOpener, newChild,
|
||||
aChromeFlags, aCalledFromJS, aPositionSpecified,
|
||||
aSizeSpecified, url,
|
||||
name, features,
|
||||
NS_ConvertUTF8toUTF16(baseURIString),
|
||||
&rv,
|
||||
aWindowIsNew,
|
||||
&frameScripts,
|
||||
&urlToLoad)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
if (!*aWindowIsNew) {
|
||||
PBrowserChild::Send__delete__(newChild);
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
TextureFactoryIdentifier textureFactoryIdentifier;
|
||||
uint64_t layersId = 0;
|
||||
PRenderFrameChild* renderFrame = newChild->SendPRenderFrameConstructor();
|
||||
newChild->SendGetRenderFrameInfo(renderFrame,
|
||||
&textureFactoryIdentifier,
|
||||
&layersId);
|
||||
if (layersId == 0) { // if renderFrame is invalid.
|
||||
PRenderFrameChild::Send__delete__(renderFrame);
|
||||
renderFrame = nullptr;
|
||||
}
|
||||
|
||||
ShowInfo showInfo(EmptyString(), false, false, true,
|
||||
aTabOpener->mDPI, aTabOpener->mDefaultScale);
|
||||
nsCOMPtr<nsPIDOMWindow> opener = do_QueryInterface(aParent);
|
||||
nsIDocShell* openerShell;
|
||||
if (opener && (openerShell = opener->GetDocShell())) {
|
||||
nsCOMPtr<nsILoadContext> context = do_QueryInterface(openerShell);
|
||||
showInfo = ShowInfo(EmptyString(), false,
|
||||
context->UsePrivateBrowsing(), true,
|
||||
aTabOpener->mDPI, aTabOpener->mDefaultScale);
|
||||
}
|
||||
|
||||
// Unfortunately we don't get a window unless we've shown the frame. That's
|
||||
// pretty bogus; see bug 763602.
|
||||
newChild->DoFakeShow(textureFactoryIdentifier, layersId, renderFrame,
|
||||
showInfo);
|
||||
|
||||
for (size_t i = 0; i < frameScripts.Length(); i++) {
|
||||
FrameScriptInfo& info = frameScripts[i];
|
||||
if (!newChild->RecvLoadRemoteScript(info.url(), info.runInGlobalScope())) {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
|
||||
if (!urlToLoad.IsEmpty()) {
|
||||
newChild->RecvLoadURL(urlToLoad, BrowserConfiguration(), showInfo);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMWindow> win = do_GetInterface(newChild->WebNavigation());
|
||||
win.forget(aReturn);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
ContentChild::GetProcessName(nsAString& aName)
|
||||
{
|
||||
@ -2748,7 +2921,8 @@ ContentChild::RecvShutdown()
|
||||
|
||||
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
||||
if (os) {
|
||||
os->NotifyObservers(this, "content-child-shutdown", nullptr);
|
||||
os->NotifyObservers(static_cast<nsIContentChild*>(this),
|
||||
"content-child-shutdown", nullptr);
|
||||
}
|
||||
|
||||
GetIPCChannel()->SetAbortOnError(false);
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "nsTHashtable.h"
|
||||
|
||||
#include "nsWeakPtr.h"
|
||||
#include "nsIWindowProvider.h"
|
||||
|
||||
|
||||
struct ChromePackage;
|
||||
@ -47,6 +48,7 @@ class ClonedMessageData;
|
||||
class TabChild;
|
||||
|
||||
class ContentChild final : public PContentChild
|
||||
, public nsIWindowProvider
|
||||
, public nsIContentChild
|
||||
{
|
||||
typedef mozilla::dom::ClonedMessageData ClonedMessageData;
|
||||
@ -55,6 +57,8 @@ class ContentChild final : public PContentChild
|
||||
typedef mozilla::ipc::URIParams URIParams;
|
||||
|
||||
public:
|
||||
NS_DECL_NSIWINDOWPROVIDER
|
||||
|
||||
ContentChild();
|
||||
virtual ~ContentChild();
|
||||
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
|
||||
@ -71,6 +75,20 @@ public:
|
||||
nsCString vendor;
|
||||
};
|
||||
|
||||
nsresult
|
||||
ProvideWindowCommon(TabChild* aTabOpener,
|
||||
nsIDOMWindow* aOpener,
|
||||
bool aIframeMoz,
|
||||
uint32_t aChromeFlags,
|
||||
bool aCalledFromJS,
|
||||
bool aPositionSpecified,
|
||||
bool aSizeSpecified,
|
||||
nsIURI* aURI,
|
||||
const nsAString& aName,
|
||||
const nsACString& aFeatures,
|
||||
bool* aWindowIsNew,
|
||||
nsIDOMWindow** aReturn);
|
||||
|
||||
bool Init(MessageLoop* aIOLoop,
|
||||
base::ProcessId aParentPid,
|
||||
IPC::Channel* aChannel);
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "base/basictypes.h"
|
||||
|
||||
#include "ContentParent.h"
|
||||
#include "TabParent.h"
|
||||
|
||||
#if defined(ANDROID) || defined(LINUX)
|
||||
# include <sys/time.h>
|
||||
@ -144,7 +145,13 @@
|
||||
#include "nsISystemMessagesInternal.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsIURIFixup.h"
|
||||
#include "nsIWindowMediator.h"
|
||||
#include "nsIDocShellTreeOwner.h"
|
||||
#include "nsIXULWindow.h"
|
||||
#include "nsIDOMChromeWindow.h"
|
||||
#include "nsIWindowWatcher.h"
|
||||
#include "nsPIWindowWatcher.h"
|
||||
#include "nsWindowWatcher.h"
|
||||
#include "nsIXULRuntime.h"
|
||||
#include "gfxDrawable.h"
|
||||
#include "ImageOps.h"
|
||||
@ -166,6 +173,7 @@
|
||||
#include "nsIWebBrowserChrome.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsDocShell.h"
|
||||
#include "nsOpenURIInFrameParams.h"
|
||||
#include "mozilla/net/NeckoMessageUtils.h"
|
||||
#include "gfxPrefs.h"
|
||||
#include "prio.h"
|
||||
@ -5343,6 +5351,236 @@ ContentParent::DeallocPWebBrowserPersistDocumentParent(PWebBrowserPersistDocumen
|
||||
return true;
|
||||
}
|
||||
|
||||
static already_AddRefed<nsPIDOMWindow>
|
||||
FindMostRecentOpenWindow()
|
||||
{
|
||||
nsCOMPtr<nsIWindowMediator> windowMediator =
|
||||
do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
|
||||
nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
|
||||
windowMediator->GetEnumerator(MOZ_UTF16("navigator:browser"),
|
||||
getter_AddRefs(windowEnumerator));
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> latest;
|
||||
|
||||
bool hasMore = false;
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(windowEnumerator->HasMoreElements(&hasMore)));
|
||||
while (hasMore) {
|
||||
nsCOMPtr<nsISupports> item;
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(windowEnumerator->GetNext(getter_AddRefs(item))));
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(item);
|
||||
|
||||
if (window && !window->Closed()) {
|
||||
latest = window;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(windowEnumerator->HasMoreElements(&hasMore)));
|
||||
}
|
||||
|
||||
return latest.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::RecvCreateWindow(PBrowserParent* aThisTab,
|
||||
PBrowserParent* aNewTab,
|
||||
const uint32_t& aChromeFlags,
|
||||
const bool& aCalledFromJS,
|
||||
const bool& aPositionSpecified,
|
||||
const bool& aSizeSpecified,
|
||||
const nsString& aURI,
|
||||
const nsString& aName,
|
||||
const nsCString& aFeatures,
|
||||
const nsString& aBaseURI,
|
||||
nsresult* aResult,
|
||||
bool* aWindowIsNew,
|
||||
InfallibleTArray<FrameScriptInfo>* aFrameScripts,
|
||||
nsCString* aURLToLoad)
|
||||
{
|
||||
// We always expect to open a new window here. If we don't, it's an error.
|
||||
*aWindowIsNew = true;
|
||||
|
||||
// The content process should never be in charge of computing whether or
|
||||
// not a window should be private or remote - the parent will do that.
|
||||
MOZ_ASSERT(!(aChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW));
|
||||
MOZ_ASSERT(!(aChromeFlags & nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW));
|
||||
MOZ_ASSERT(!(aChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME));
|
||||
MOZ_ASSERT(!(aChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW));
|
||||
|
||||
TabParent* thisTabParent = nullptr;
|
||||
if (aThisTab) {
|
||||
thisTabParent = TabParent::GetFrom(aThisTab);
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(thisTabParent && thisTabParent->IsBrowserOrApp())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIWindowWatcher> pwwatch =
|
||||
do_GetService(NS_WINDOWWATCHER_CONTRACTID, aResult);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(*aResult))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
TabParent* newTab = TabParent::GetFrom(aNewTab);
|
||||
MOZ_ASSERT(newTab);
|
||||
|
||||
// Content has requested that we open this new content window, so
|
||||
// we must have an opener.
|
||||
newTab->SetHasContentOpener(true);
|
||||
|
||||
nsCOMPtr<nsIContent> frame;
|
||||
if (thisTabParent) {
|
||||
frame = do_QueryInterface(thisTabParent->GetOwnerElement());
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> parent;
|
||||
if (frame) {
|
||||
parent = frame->OwnerDoc()->GetWindow();
|
||||
|
||||
// If our chrome window is in the process of closing, don't try to open a
|
||||
// new tab in it.
|
||||
if (parent && parent->Closed()) {
|
||||
parent = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
|
||||
if (thisTabParent) {
|
||||
browserDOMWin = thisTabParent->GetBrowserDOMWindow();
|
||||
}
|
||||
|
||||
// If we haven't found a chrome window to open in, just use the most recently
|
||||
// opened one.
|
||||
if (!parent) {
|
||||
parent = FindMostRecentOpenWindow();
|
||||
if (NS_WARN_IF(!parent)) {
|
||||
*aResult = NS_ERROR_FAILURE;
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(parent);
|
||||
if (rootChromeWin) {
|
||||
rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
|
||||
}
|
||||
}
|
||||
|
||||
int32_t openLocation =
|
||||
nsWindowWatcher::GetWindowOpenLocation(parent, aChromeFlags, aCalledFromJS,
|
||||
aPositionSpecified, aSizeSpecified);
|
||||
|
||||
MOZ_ASSERT(openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
|
||||
openLocation == nsIBrowserDOMWindow::OPEN_NEWWINDOW);
|
||||
|
||||
// Opening new tabs is the easy case...
|
||||
if (openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB) {
|
||||
if (NS_WARN_IF(!browserDOMWin)) {
|
||||
*aResult = NS_ERROR_FAILURE;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isPrivate = false;
|
||||
if (thisTabParent) {
|
||||
nsCOMPtr<nsILoadContext> loadContext = thisTabParent->GetLoadContext();
|
||||
loadContext->GetUsePrivateBrowsing(&isPrivate);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIOpenURIInFrameParams> params = new nsOpenURIInFrameParams();
|
||||
params->SetReferrer(aBaseURI);
|
||||
params->SetIsPrivate(isPrivate);
|
||||
|
||||
TabParent::AutoUseNewTab aunt(newTab, aWindowIsNew, aURLToLoad);
|
||||
|
||||
nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner;
|
||||
browserDOMWin->OpenURIInFrame(nullptr, params,
|
||||
openLocation,
|
||||
nsIBrowserDOMWindow::OPEN_NEW,
|
||||
getter_AddRefs(frameLoaderOwner));
|
||||
if (!frameLoaderOwner) {
|
||||
*aWindowIsNew = false;
|
||||
}
|
||||
|
||||
newTab->SwapFrameScriptsFrom(*aFrameScripts);
|
||||
return true;
|
||||
}
|
||||
|
||||
// WindowWatcher is going to expect a valid URI to open a window
|
||||
// to. If it can't find one, it's going to attempt to figure one
|
||||
// out on its own, which is problematic because it can't access
|
||||
// the document for the remote browser we're opening. Luckily,
|
||||
// TabChild has sent us a baseURI with which we can ensure that
|
||||
// the URI we pass to WindowWatcher is valid.
|
||||
nsCOMPtr<nsIURI> baseURI;
|
||||
*aResult = NS_NewURI(getter_AddRefs(baseURI), aBaseURI);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(*aResult))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsAutoCString finalURIString;
|
||||
if (!aURI.IsEmpty()) {
|
||||
nsCOMPtr<nsIURI> finalURI;
|
||||
*aResult = NS_NewURI(getter_AddRefs(finalURI), NS_ConvertUTF16toUTF8(aURI).get(), baseURI);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(*aResult))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
finalURI->GetSpec(finalURIString);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMWindow> window;
|
||||
|
||||
TabParent::AutoUseNewTab aunt(newTab, aWindowIsNew, aURLToLoad);
|
||||
|
||||
const char* features = aFeatures.Length() ? aFeatures.get() : nullptr;
|
||||
*aResult = pwwatch->OpenWindow2(parent, finalURIString.get(),
|
||||
NS_ConvertUTF16toUTF8(aName).get(),
|
||||
features, aCalledFromJS,
|
||||
false, false, thisTabParent, nullptr, getter_AddRefs(window));
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(*aResult))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
*aResult = NS_ERROR_FAILURE;
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> pwindow = do_QueryInterface(window);
|
||||
if (NS_WARN_IF(!pwindow)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocShell> windowDocShell = pwindow->GetDocShell();
|
||||
if (NS_WARN_IF(!windowDocShell)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
|
||||
windowDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
|
||||
|
||||
nsCOMPtr<nsIXULWindow> xulWin = do_GetInterface(treeOwner);
|
||||
if (NS_WARN_IF(!xulWin)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIXULBrowserWindow> xulBrowserWin;
|
||||
xulWin->GetXULBrowserWindow(getter_AddRefs(xulBrowserWin));
|
||||
if (NS_WARN_IF(!xulBrowserWin)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITabParent> newRemoteTab;
|
||||
*aResult = xulBrowserWin->ForceInitialBrowserRemote(getter_AddRefs(newRemoteTab));
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(*aResult))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(TabParent::GetFrom(newRemoteTab) == newTab);
|
||||
|
||||
newTab->SwapFrameScriptsFrom(*aFrameScripts);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
ContentParent::PermissionManagerAddref(const ContentParentId& aCpId,
|
||||
const TabId& aTabId)
|
||||
|
@ -434,6 +434,21 @@ public:
|
||||
void SetNuwaParent(NuwaParent* aNuwaParent) { mNuwaParent = aNuwaParent; }
|
||||
void ForkNewProcess(bool aBlocking);
|
||||
|
||||
virtual bool RecvCreateWindow(PBrowserParent* aThisTabParent,
|
||||
PBrowserParent* aOpener,
|
||||
const uint32_t& aChromeFlags,
|
||||
const bool& aCalledFromJS,
|
||||
const bool& aPositionSpecified,
|
||||
const bool& aSizeSpecified,
|
||||
const nsString& aURI,
|
||||
const nsString& aName,
|
||||
const nsCString& aFeatures,
|
||||
const nsString& aBaseURI,
|
||||
nsresult* aResult,
|
||||
bool* aWindowIsNew,
|
||||
InfallibleTArray<FrameScriptInfo>* aFrameScripts,
|
||||
nsCString* aURLToLoad) override;
|
||||
|
||||
protected:
|
||||
void OnChannelConnected(int32_t pid) override;
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override;
|
||||
|
@ -150,10 +150,9 @@ ContentProcessManager::AllocateTabId(const TabId& aOpenerTabId,
|
||||
|
||||
struct RemoteFrameInfo info;
|
||||
|
||||
const IPCTabContextUnion& contextUnion = aContext.contextUnion();
|
||||
// If it's a PopupIPCTabContext, it's the case that a TabChild want to
|
||||
// open a new tab. aOpenerTabId has to be it's parent frame's opener id.
|
||||
if (contextUnion.type() == IPCTabContextUnion::TPopupIPCTabContext) {
|
||||
if (aContext.type() == IPCTabContext::TPopupIPCTabContext) {
|
||||
auto remoteFrameIter = iter->second.mRemoteFrames.find(aOpenerTabId);
|
||||
if (remoteFrameIter == iter->second.mRemoteFrames.end()) {
|
||||
ASSERT_UNLESS_FUZZING("Failed to find parent frame's opener id.");
|
||||
@ -162,7 +161,7 @@ ContentProcessManager::AllocateTabId(const TabId& aOpenerTabId,
|
||||
|
||||
info.mOpenerTabId = remoteFrameIter->second.mOpenerTabId;
|
||||
|
||||
const PopupIPCTabContext &ipcContext = contextUnion.get_PopupIPCTabContext();
|
||||
const PopupIPCTabContext &ipcContext = aContext.get_PopupIPCTabContext();
|
||||
MOZ_ASSERT(ipcContext.opener().type() == PBrowserOrId::TTabId);
|
||||
|
||||
remoteFrameIter = iter->second.mRemoteFrames.find(ipcContext.opener().get_TabId());
|
||||
|
@ -90,16 +90,11 @@ struct ShowInfo
|
||||
nsString name;
|
||||
bool fullscreenAllowed;
|
||||
bool isPrivate;
|
||||
bool fakeShowInfo;
|
||||
float dpi;
|
||||
double defaultScale;
|
||||
};
|
||||
|
||||
struct FrameScriptInfo
|
||||
{
|
||||
nsString url;
|
||||
bool runInGlobalScope;
|
||||
};
|
||||
|
||||
prio(normal upto urgent) sync protocol PBrowser
|
||||
{
|
||||
manager PContent or PContentBridge;
|
||||
@ -165,20 +160,6 @@ parent:
|
||||
|
||||
Event(RemoteDOMEvent aEvent);
|
||||
|
||||
sync CreateWindow(PBrowser aNewTab,
|
||||
uint32_t aChromeFlags,
|
||||
bool aCalledFromJS,
|
||||
bool aPositionSpecified,
|
||||
bool aSizeSpecified,
|
||||
nsString aURI,
|
||||
nsString aName,
|
||||
nsCString aFeatures,
|
||||
nsString aBaseURI)
|
||||
returns (nsresult rv,
|
||||
bool windowOpened,
|
||||
FrameScriptInfo[] frameScripts,
|
||||
nsCString urlToLoad);
|
||||
|
||||
sync SyncMessage(nsString aMessage, ClonedMessageData aData,
|
||||
CpowEntry[] aCpows, Principal aPrincipal)
|
||||
returns (StructuredCloneData[] retval);
|
||||
@ -573,7 +554,7 @@ child:
|
||||
nullable PRenderFrame renderFrame,
|
||||
bool parentIsActive);
|
||||
|
||||
LoadURL(nsCString uri, BrowserConfiguration config);
|
||||
LoadURL(nsCString uri, BrowserConfiguration config, ShowInfo info);
|
||||
|
||||
CacheFileDescriptor(nsString path, FileDescriptor fd);
|
||||
|
||||
|
@ -410,6 +410,12 @@ union GamepadChangeEvent {
|
||||
GamepadButtonInformation;
|
||||
};
|
||||
|
||||
struct FrameScriptInfo
|
||||
{
|
||||
nsString url;
|
||||
bool runInGlobalScope;
|
||||
};
|
||||
|
||||
prio(normal upto urgent) sync protocol PContent
|
||||
{
|
||||
parent spawns PPluginModule;
|
||||
@ -1112,6 +1118,21 @@ parent:
|
||||
sync GetGraphicsDeviceInitData()
|
||||
returns (DeviceInitData aData);
|
||||
|
||||
sync CreateWindow(nullable PBrowser aThisTab,
|
||||
PBrowser aNewTab,
|
||||
uint32_t aChromeFlags,
|
||||
bool aCalledFromJS,
|
||||
bool aPositionSpecified,
|
||||
bool aSizeSpecified,
|
||||
nsString aURI,
|
||||
nsString aName,
|
||||
nsCString aFeatures,
|
||||
nsString aBaseURI)
|
||||
returns (nsresult rv,
|
||||
bool windowOpened,
|
||||
FrameScriptInfo[] frameScripts,
|
||||
nsCString urlToLoad);
|
||||
|
||||
sync GetDeviceStorageLocation(nsString type)
|
||||
returns (nsString path);
|
||||
|
||||
|
@ -44,22 +44,25 @@ struct FrameIPCTabContext
|
||||
nsCString signedPkgOriginNoSuffix;
|
||||
};
|
||||
|
||||
// XXXcatalinb: This is only used by ServiceWorkerClients::OpenWindow.
|
||||
// Because service workers don't have an associated TabChild
|
||||
// we can't satisfy the security constraints on b2g. As such, the parent
|
||||
// process will accept this tab context only on desktop.
|
||||
struct UnsafeIPCTabContext
|
||||
{ };
|
||||
|
||||
// IPCTabContext is an analog to mozilla::dom::TabContext. Both specify an
|
||||
// iframe/PBrowser's own and containing app-ids and tell you whether the
|
||||
// iframe/PBrowser is a browser frame. But only IPCTabContext is allowed to
|
||||
// travel over IPC.
|
||||
//
|
||||
// We need IPCTabContext (specifically, PopupIPCTabContext) to prevent a
|
||||
// privilege escalation attack by a compromised child process. See the comment
|
||||
// on AllocPBrowser for details.
|
||||
union IPCTabContextUnion
|
||||
// privilege escalation attack by a compromised child process.
|
||||
union IPCTabContext
|
||||
{
|
||||
PopupIPCTabContext;
|
||||
FrameIPCTabContext;
|
||||
};
|
||||
|
||||
struct IPCTabContext {
|
||||
IPCTabContextUnion contextUnion;
|
||||
UnsafeIPCTabContext;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -617,6 +617,7 @@ TabChild::TabChild(nsIContentChild* aManager,
|
||||
, mDefaultScale(0)
|
||||
, mIPCOpen(true)
|
||||
, mParentIsActive(false)
|
||||
, mDidSetRealShowInfo(false)
|
||||
{
|
||||
// In the general case having the TabParent tell us if APZ is enabled or not
|
||||
// doesn't really work because the TabParent itself may not have a reference
|
||||
@ -1115,7 +1116,9 @@ TabChild::ProvideWindow(nsIDOMWindow* aParent, uint32_t aChromeFlags,
|
||||
// Note that ProvideWindowCommon may return NS_ERROR_ABORT if the
|
||||
// open window call was canceled. It's important that we pass this error
|
||||
// code back to our caller.
|
||||
return ProvideWindowCommon(aParent,
|
||||
ContentChild* cc = ContentChild::GetSingleton();
|
||||
return cc->ProvideWindowCommon(this,
|
||||
aParent,
|
||||
iframeMoz,
|
||||
aChromeFlags,
|
||||
aCalledFromJS,
|
||||
@ -1128,134 +1131,6 @@ TabChild::ProvideWindow(nsIDOMWindow* aParent, uint32_t aChromeFlags,
|
||||
aReturn);
|
||||
}
|
||||
|
||||
nsresult
|
||||
TabChild::ProvideWindowCommon(nsIDOMWindow* aOpener,
|
||||
bool aIframeMoz,
|
||||
uint32_t aChromeFlags,
|
||||
bool aCalledFromJS,
|
||||
bool aPositionSpecified,
|
||||
bool aSizeSpecified,
|
||||
nsIURI* aURI,
|
||||
const nsAString& aName,
|
||||
const nsACString& aFeatures,
|
||||
bool* aWindowIsNew,
|
||||
nsIDOMWindow** aReturn)
|
||||
{
|
||||
*aReturn = nullptr;
|
||||
|
||||
ContentChild* cc = ContentChild::GetSingleton();
|
||||
const TabId openerTabId = GetTabId();
|
||||
|
||||
// We must use PopupIPCTabContext here; ContentParent will not accept the
|
||||
// result of this->AsIPCTabContext() (which will be a
|
||||
// BrowserFrameIPCTabContext or an AppFrameIPCTabContext), for security
|
||||
// reasons.
|
||||
PopupIPCTabContext context;
|
||||
context.opener() = openerTabId;
|
||||
context.isBrowserElement() = IsBrowserElement();
|
||||
|
||||
IPCTabContext ipcContext(context);
|
||||
|
||||
TabId tabId;
|
||||
cc->SendAllocateTabId(openerTabId,
|
||||
ipcContext,
|
||||
cc->GetID(),
|
||||
&tabId);
|
||||
|
||||
RefPtr<TabChild> newChild = new TabChild(ContentChild::GetSingleton(), tabId,
|
||||
/* TabContext */ *this, aChromeFlags);
|
||||
if (NS_FAILED(newChild->Init())) {
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
context.opener() = this;
|
||||
unused << Manager()->SendPBrowserConstructor(
|
||||
// We release this ref in DeallocPBrowserChild
|
||||
RefPtr<TabChild>(newChild).forget().take(),
|
||||
tabId, IPCTabContext(context), aChromeFlags,
|
||||
cc->GetID(), cc->IsForApp(), cc->IsForBrowser());
|
||||
|
||||
nsAutoCString spec;
|
||||
if (aURI) {
|
||||
aURI->GetSpec(spec);
|
||||
}
|
||||
|
||||
NS_ConvertUTF8toUTF16 url(spec);
|
||||
nsString name(aName);
|
||||
nsAutoCString features(aFeatures);
|
||||
nsTArray<FrameScriptInfo> frameScripts;
|
||||
nsCString urlToLoad;
|
||||
|
||||
if (aIframeMoz) {
|
||||
newChild->SendBrowserFrameOpenWindow(this, url, name,
|
||||
NS_ConvertUTF8toUTF16(features),
|
||||
aWindowIsNew);
|
||||
} else {
|
||||
nsCOMPtr<nsPIDOMWindow> opener = do_QueryInterface(aOpener);
|
||||
nsCOMPtr<nsIDocument> doc = opener->GetDoc();
|
||||
nsCOMPtr<nsIURI> baseURI = doc->GetDocBaseURI();
|
||||
if (!baseURI) {
|
||||
NS_ERROR("nsIDocument didn't return a base URI");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsAutoCString baseURIString;
|
||||
baseURI->GetSpec(baseURIString);
|
||||
|
||||
nsresult rv;
|
||||
|
||||
if (!SendCreateWindow(newChild,
|
||||
aChromeFlags, aCalledFromJS, aPositionSpecified,
|
||||
aSizeSpecified, url,
|
||||
name, features,
|
||||
NS_ConvertUTF8toUTF16(baseURIString),
|
||||
&rv,
|
||||
aWindowIsNew,
|
||||
&frameScripts,
|
||||
&urlToLoad)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
if (!*aWindowIsNew) {
|
||||
PBrowserChild::Send__delete__(newChild);
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
TextureFactoryIdentifier textureFactoryIdentifier;
|
||||
uint64_t layersId = 0;
|
||||
PRenderFrameChild* renderFrame = newChild->SendPRenderFrameConstructor();
|
||||
newChild->SendGetRenderFrameInfo(renderFrame,
|
||||
&textureFactoryIdentifier,
|
||||
&layersId);
|
||||
if (layersId == 0) { // if renderFrame is invalid.
|
||||
PRenderFrameChild::Send__delete__(renderFrame);
|
||||
renderFrame = nullptr;
|
||||
}
|
||||
|
||||
// Unfortunately we don't get a window unless we've shown the frame. That's
|
||||
// pretty bogus; see bug 763602.
|
||||
newChild->DoFakeShow(textureFactoryIdentifier, layersId, renderFrame);
|
||||
|
||||
for (size_t i = 0; i < frameScripts.Length(); i++) {
|
||||
FrameScriptInfo& info = frameScripts[i];
|
||||
if (!newChild->RecvLoadRemoteScript(info.url(), info.runInGlobalScope())) {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
|
||||
if (!urlToLoad.IsEmpty()) {
|
||||
newChild->RecvLoadURL(urlToLoad, BrowserConfiguration());
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMWindow> win = do_GetInterface(newChild->WebNavigation());
|
||||
win.forget(aReturn);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
TabChild::DestroyWindow()
|
||||
{
|
||||
@ -1383,12 +1258,15 @@ TabChild::IsRootContentDocument()
|
||||
|
||||
bool
|
||||
TabChild::RecvLoadURL(const nsCString& aURI,
|
||||
const BrowserConfiguration& aConfiguration)
|
||||
const BrowserConfiguration& aConfiguration,
|
||||
const ShowInfo& aInfo)
|
||||
{
|
||||
if (!InitTabChildGlobal()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ApplyShowInfo(aInfo);
|
||||
|
||||
SetProcessNameToAppName();
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
@ -1560,10 +1438,9 @@ TabChild::CancelCachedFileDescriptorCallback(
|
||||
void
|
||||
TabChild::DoFakeShow(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
|
||||
const uint64_t& aLayersId,
|
||||
PRenderFrameChild* aRenderFrame)
|
||||
PRenderFrameChild* aRenderFrame, const ShowInfo& aShowInfo)
|
||||
{
|
||||
ShowInfo info(EmptyString(), false, false, 0, 0);
|
||||
RecvShow(ScreenIntSize(0, 0), info, aTextureFactoryIdentifier,
|
||||
RecvShow(ScreenIntSize(0, 0), aShowInfo, aTextureFactoryIdentifier,
|
||||
aLayersId, aRenderFrame, mParentIsActive);
|
||||
mDidFakeShow = true;
|
||||
}
|
||||
@ -1571,6 +1448,16 @@ TabChild::DoFakeShow(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
|
||||
void
|
||||
TabChild::ApplyShowInfo(const ShowInfo& aInfo)
|
||||
{
|
||||
if (mDidSetRealShowInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aInfo.fakeShowInfo()) {
|
||||
// Once we've got one ShowInfo from parent, no need to update the values
|
||||
// anymore.
|
||||
mDidSetRealShowInfo = true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
|
||||
if (docShell) {
|
||||
nsCOMPtr<nsIDocShellTreeItem> item = do_GetInterface(docShell);
|
||||
@ -1584,6 +1471,9 @@ TabChild::ApplyShowInfo(const ShowInfo& aInfo)
|
||||
}
|
||||
docShell->SetFullscreenAllowed(aInfo.fullscreenAllowed());
|
||||
if (aInfo.isPrivate()) {
|
||||
nsCOMPtr<nsILoadContext> context = do_GetInterface(docShell);
|
||||
// No need to re-set private browsing mode.
|
||||
if (!context->UsePrivateBrowsing()) {
|
||||
bool nonBlank;
|
||||
docShell->GetHasLoadedNonBlankURI(&nonBlank);
|
||||
if (nonBlank) {
|
||||
@ -1593,11 +1483,11 @@ TabChild::ApplyShowInfo(const ShowInfo& aInfo)
|
||||
NS_LITERAL_CSTRING("mozprivatebrowsing"),
|
||||
nullptr);
|
||||
} else {
|
||||
nsCOMPtr<nsILoadContext> context = do_GetInterface(docShell);
|
||||
context->SetUsePrivateBrowsing(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mDPI = aInfo.dpi();
|
||||
mDefaultScale = aInfo.defaultScale();
|
||||
}
|
||||
|
@ -240,6 +240,16 @@ public:
|
||||
static already_AddRefed<TabChild> FindTabChild(const TabId& aTabId);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a new TabChild object.
|
||||
*/
|
||||
TabChild(nsIContentChild* aManager,
|
||||
const TabId& aTabId,
|
||||
const TabContext& aContext,
|
||||
uint32_t aChromeFlags);
|
||||
|
||||
nsresult Init();
|
||||
|
||||
/**
|
||||
* This is expected to be called off the critical path to content
|
||||
* startup. This is an opportunity to load things that are slow
|
||||
@ -289,7 +299,8 @@ public:
|
||||
const ViewID& aViewId,
|
||||
const Maybe<ZoomConstraints>& aConstraints) override;
|
||||
virtual bool RecvLoadURL(const nsCString& aURI,
|
||||
const BrowserConfiguration& aConfiguration) override;
|
||||
const BrowserConfiguration& aConfiguration,
|
||||
const ShowInfo& aInfo) override;
|
||||
virtual bool RecvCacheFileDescriptor(const nsString& aPath,
|
||||
const FileDescriptor& aFileDescriptor)
|
||||
override;
|
||||
@ -510,6 +521,12 @@ public:
|
||||
|
||||
virtual ScreenIntSize GetInnerSize() override;
|
||||
|
||||
// Call RecvShow(nsIntSize(0, 0)) and block future calls to RecvShow().
|
||||
void DoFakeShow(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
|
||||
const uint64_t& aLayersId,
|
||||
PRenderFrameChild* aRenderFrame,
|
||||
const ShowInfo& aShowInfo);
|
||||
|
||||
protected:
|
||||
virtual ~TabChild();
|
||||
|
||||
@ -535,16 +552,6 @@ protected:
|
||||
#endif
|
||||
|
||||
private:
|
||||
/**
|
||||
* Create a new TabChild object.
|
||||
*/
|
||||
TabChild(nsIContentChild* aManager,
|
||||
const TabId& aTabId,
|
||||
const TabContext& aContext,
|
||||
uint32_t aChromeFlags);
|
||||
|
||||
nsresult Init();
|
||||
|
||||
class DelayedFireContextMenuEvent;
|
||||
|
||||
// Notify others that our TabContext has been updated. (At the moment, this
|
||||
@ -564,11 +571,6 @@ private:
|
||||
void DestroyWindow();
|
||||
void SetProcessNameToAppName();
|
||||
|
||||
// Call RecvShow(nsIntSize(0, 0)) and block future calls to RecvShow().
|
||||
void DoFakeShow(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
|
||||
const uint64_t& aLayersId,
|
||||
PRenderFrameChild* aRenderFrame);
|
||||
|
||||
void ApplyShowInfo(const ShowInfo& aInfo);
|
||||
|
||||
// These methods are used for tracking synthetic mouse events
|
||||
@ -581,19 +583,6 @@ private:
|
||||
void CancelTapTracking();
|
||||
void UpdateTapState(const WidgetTouchEvent& aEvent, nsEventStatus aStatus);
|
||||
|
||||
nsresult
|
||||
ProvideWindowCommon(nsIDOMWindow* aOpener,
|
||||
bool aIframeMoz,
|
||||
uint32_t aChromeFlags,
|
||||
bool aCalledFromJS,
|
||||
bool aPositionSpecified,
|
||||
bool aSizeSpecified,
|
||||
nsIURI* aURI,
|
||||
const nsAString& aName,
|
||||
const nsACString& aFeatures,
|
||||
bool* aWindowIsNew,
|
||||
nsIDOMWindow** aReturn);
|
||||
|
||||
bool HasValidInnerSize();
|
||||
|
||||
void SetTabId(const TabId& aTabId);
|
||||
@ -647,12 +636,16 @@ private:
|
||||
// Position of tab, relative to parent widget (typically the window)
|
||||
LayoutDeviceIntPoint mChromeDisp;
|
||||
TabId mUniqueId;
|
||||
|
||||
friend class ContentChild;
|
||||
float mDPI;
|
||||
double mDefaultScale;
|
||||
|
||||
bool mIPCOpen;
|
||||
bool mParentIsActive;
|
||||
bool mAsyncPanZoomEnabled;
|
||||
CSSSize mUnscaledInnerSize;
|
||||
bool mDidSetRealShowInfo;
|
||||
|
||||
nsAutoTArray<bool, NUMBER_OF_AUDIO_CHANNELS> mAudioChannelsActive;
|
||||
|
||||
|
@ -232,10 +232,9 @@ MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams)
|
||||
nsAutoCString originSuffix;
|
||||
nsAutoCString signedPkgOriginNoSuffix;
|
||||
|
||||
const IPCTabContextUnion& contextUnion = aParams.contextUnion();
|
||||
switch(contextUnion.type()) {
|
||||
case IPCTabContextUnion::TPopupIPCTabContext: {
|
||||
const PopupIPCTabContext &ipcContext = contextUnion.get_PopupIPCTabContext();
|
||||
switch(aParams.type()) {
|
||||
case IPCTabContext::TPopupIPCTabContext: {
|
||||
const PopupIPCTabContext &ipcContext = aParams.get_PopupIPCTabContext();
|
||||
|
||||
TabContext *context;
|
||||
if (ipcContext.opener().type() == PBrowserOrId::TPBrowserParent) {
|
||||
@ -281,9 +280,9 @@ MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IPCTabContextUnion::TFrameIPCTabContext: {
|
||||
case IPCTabContext::TFrameIPCTabContext: {
|
||||
const FrameIPCTabContext &ipcContext =
|
||||
contextUnion.get_FrameIPCTabContext();
|
||||
aParams.get_FrameIPCTabContext();
|
||||
|
||||
containingAppId = ipcContext.frameOwnerAppId();
|
||||
signedPkgOriginNoSuffix = ipcContext.signedPkgOriginNoSuffix();
|
||||
@ -291,6 +290,23 @@ MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams)
|
||||
originAttributes.PopulateFromSuffix(originSuffix);
|
||||
break;
|
||||
}
|
||||
case IPCTabContext::TUnsafeIPCTabContext: {
|
||||
// XXXcatalinb: This used *only* by ServiceWorkerClients::OpenWindow.
|
||||
// It is meant as a temporary solution until service workers can
|
||||
// provide a TabChild equivalent. Don't allow this on b2g since
|
||||
// it might be used to escalate privileges.
|
||||
#ifdef MOZ_B2G
|
||||
mInvalidReason = "ServiceWorkerClients::OpenWindow is not supported.";
|
||||
return;
|
||||
#endif
|
||||
if (!Preferences::GetBool("dom.serviceWorkers.enabled", false)) {
|
||||
mInvalidReason = "ServiceWorkers should be enabled.";
|
||||
return;
|
||||
}
|
||||
|
||||
containingAppId = NO_APP_ID;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
@ -50,7 +50,6 @@
|
||||
#include "nsIContent.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsIDocShellTreeOwner.h"
|
||||
#include "nsIDOMChromeWindow.h"
|
||||
#include "nsIDOMElement.h"
|
||||
#include "nsIDOMEvent.h"
|
||||
#include "nsIDOMWindow.h"
|
||||
@ -61,26 +60,20 @@
|
||||
#include "nsIPromptFactory.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsIWebBrowserChrome.h"
|
||||
#include "nsIWindowCreator2.h"
|
||||
#include "nsIXULBrowserWindow.h"
|
||||
#include "nsIXULWindow.h"
|
||||
#include "nsIRemoteBrowser.h"
|
||||
#include "nsViewManager.h"
|
||||
#include "nsVariant.h"
|
||||
#include "nsIWidget.h"
|
||||
#include "nsIWindowMediator.h"
|
||||
#include "nsIWindowWatcher.h"
|
||||
#ifndef XP_WIN
|
||||
#include "nsJARProtocolHandler.h"
|
||||
#endif
|
||||
#include "nsOpenURIInFrameParams.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsPIWindowWatcher.h"
|
||||
#include "nsPresShell.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsWindowWatcher.h"
|
||||
#include "private/pprio.h"
|
||||
#include "PermissionMessageUtils.h"
|
||||
#include "StructuredCloneData.h"
|
||||
@ -723,249 +716,6 @@ TabParent::RecvEvent(const RemoteDOMEvent& aEvent)
|
||||
return true;
|
||||
}
|
||||
|
||||
struct MOZ_STACK_CLASS TabParent::AutoUseNewTab final
|
||||
{
|
||||
public:
|
||||
AutoUseNewTab(TabParent* aNewTab, bool* aWindowIsNew, nsCString* aURLToLoad)
|
||||
: mNewTab(aNewTab), mWindowIsNew(aWindowIsNew), mURLToLoad(aURLToLoad)
|
||||
{
|
||||
MOZ_ASSERT(!TabParent::sNextTabParent);
|
||||
MOZ_ASSERT(!aNewTab->mCreatingWindow);
|
||||
|
||||
TabParent::sNextTabParent = aNewTab;
|
||||
aNewTab->mCreatingWindow = true;
|
||||
aNewTab->mDelayedURL.Truncate();
|
||||
}
|
||||
|
||||
~AutoUseNewTab()
|
||||
{
|
||||
mNewTab->mCreatingWindow = false;
|
||||
*mURLToLoad = mNewTab->mDelayedURL;
|
||||
|
||||
if (TabParent::sNextTabParent) {
|
||||
MOZ_ASSERT(TabParent::sNextTabParent == mNewTab);
|
||||
TabParent::sNextTabParent = nullptr;
|
||||
*mWindowIsNew = false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
TabParent* mNewTab;
|
||||
bool* mWindowIsNew;
|
||||
nsCString* mURLToLoad;
|
||||
};
|
||||
|
||||
static already_AddRefed<nsPIDOMWindow>
|
||||
FindMostRecentOpenWindow()
|
||||
{
|
||||
nsCOMPtr<nsIWindowMediator> windowMediator =
|
||||
do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
|
||||
nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
|
||||
windowMediator->GetEnumerator(MOZ_UTF16("navigator:browser"),
|
||||
getter_AddRefs(windowEnumerator));
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> latest;
|
||||
|
||||
bool hasMore = false;
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(windowEnumerator->HasMoreElements(&hasMore)));
|
||||
while (hasMore) {
|
||||
nsCOMPtr<nsISupports> item;
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(windowEnumerator->GetNext(getter_AddRefs(item))));
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(item);
|
||||
|
||||
if (window && !window->Closed()) {
|
||||
latest = window;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(windowEnumerator->HasMoreElements(&hasMore)));
|
||||
}
|
||||
|
||||
return latest.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
TabParent::RecvCreateWindow(PBrowserParent* aNewTab,
|
||||
const uint32_t& aChromeFlags,
|
||||
const bool& aCalledFromJS,
|
||||
const bool& aPositionSpecified,
|
||||
const bool& aSizeSpecified,
|
||||
const nsString& aURI,
|
||||
const nsString& aName,
|
||||
const nsCString& aFeatures,
|
||||
const nsString& aBaseURI,
|
||||
nsresult* aResult,
|
||||
bool* aWindowIsNew,
|
||||
InfallibleTArray<FrameScriptInfo>* aFrameScripts,
|
||||
nsCString* aURLToLoad)
|
||||
{
|
||||
// We always expect to open a new window here. If we don't, it's an error.
|
||||
*aWindowIsNew = true;
|
||||
|
||||
// The content process should never be in charge of computing whether or
|
||||
// not a window should be private or remote - the parent will do that.
|
||||
MOZ_ASSERT(!(aChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW));
|
||||
MOZ_ASSERT(!(aChromeFlags & nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW));
|
||||
MOZ_ASSERT(!(aChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME));
|
||||
MOZ_ASSERT(!(aChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW));
|
||||
|
||||
if (NS_WARN_IF(IsBrowserOrApp()))
|
||||
return false;
|
||||
|
||||
nsCOMPtr<nsPIWindowWatcher> pwwatch =
|
||||
do_GetService(NS_WINDOWWATCHER_CONTRACTID, aResult);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(*aResult)))
|
||||
return true;
|
||||
|
||||
TabParent* newTab = TabParent::GetFrom(aNewTab);
|
||||
MOZ_ASSERT(newTab);
|
||||
|
||||
// Content has requested that we open this new content window, so
|
||||
// we must have an opener.
|
||||
newTab->SetHasContentOpener(true);
|
||||
|
||||
nsCOMPtr<nsIContent> frame(do_QueryInterface(mFrameElement));
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> parent;
|
||||
if (frame) {
|
||||
parent = frame->OwnerDoc()->GetWindow();
|
||||
|
||||
// If our chrome window is in the process of closing, don't try to open a
|
||||
// new tab in it.
|
||||
if (parent && parent->Closed()) {
|
||||
parent = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin = mBrowserDOMWindow;
|
||||
|
||||
// If we haven't found a chrome window to open in, just use the most recently
|
||||
// opened one.
|
||||
if (!parent) {
|
||||
parent = FindMostRecentOpenWindow();
|
||||
if (NS_WARN_IF(!parent)) {
|
||||
*aResult = NS_ERROR_FAILURE;
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(parent);
|
||||
if (rootChromeWin) {
|
||||
rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
|
||||
}
|
||||
}
|
||||
|
||||
int32_t openLocation =
|
||||
nsWindowWatcher::GetWindowOpenLocation(parent, aChromeFlags, aCalledFromJS,
|
||||
aPositionSpecified, aSizeSpecified);
|
||||
|
||||
MOZ_ASSERT(openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
|
||||
openLocation == nsIBrowserDOMWindow::OPEN_NEWWINDOW);
|
||||
|
||||
// Opening new tabs is the easy case...
|
||||
if (openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB) {
|
||||
if (NS_WARN_IF(!browserDOMWin)) {
|
||||
*aResult = NS_ERROR_FAILURE;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isPrivate;
|
||||
nsCOMPtr<nsILoadContext> loadContext = GetLoadContext();
|
||||
loadContext->GetUsePrivateBrowsing(&isPrivate);
|
||||
|
||||
nsCOMPtr<nsIOpenURIInFrameParams> params = new nsOpenURIInFrameParams();
|
||||
params->SetReferrer(aBaseURI);
|
||||
params->SetIsPrivate(isPrivate);
|
||||
|
||||
AutoUseNewTab aunt(newTab, aWindowIsNew, aURLToLoad);
|
||||
|
||||
nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner;
|
||||
browserDOMWin->OpenURIInFrame(nullptr, params,
|
||||
openLocation,
|
||||
nsIBrowserDOMWindow::OPEN_NEW,
|
||||
getter_AddRefs(frameLoaderOwner));
|
||||
if (!frameLoaderOwner) {
|
||||
*aWindowIsNew = false;
|
||||
}
|
||||
|
||||
aFrameScripts->SwapElements(newTab->mDelayedFrameScripts);
|
||||
return true;
|
||||
}
|
||||
|
||||
// WindowWatcher is going to expect a valid URI to open a window
|
||||
// to. If it can't find one, it's going to attempt to figure one
|
||||
// out on its own, which is problematic because it can't access
|
||||
// the document for the remote browser we're opening. Luckily,
|
||||
// TabChild has sent us a baseURI with which we can ensure that
|
||||
// the URI we pass to WindowWatcher is valid.
|
||||
nsCOMPtr<nsIURI> baseURI;
|
||||
*aResult = NS_NewURI(getter_AddRefs(baseURI), aBaseURI);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(*aResult)))
|
||||
return true;
|
||||
|
||||
nsAutoCString finalURIString;
|
||||
if (!aURI.IsEmpty()) {
|
||||
nsCOMPtr<nsIURI> finalURI;
|
||||
*aResult = NS_NewURI(getter_AddRefs(finalURI), NS_ConvertUTF16toUTF8(aURI).get(), baseURI);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(*aResult)))
|
||||
return true;
|
||||
|
||||
finalURI->GetSpec(finalURIString);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMWindow> window;
|
||||
|
||||
AutoUseNewTab aunt(newTab, aWindowIsNew, aURLToLoad);
|
||||
|
||||
const char* features = aFeatures.Length() ? aFeatures.get() : nullptr;
|
||||
|
||||
*aResult = pwwatch->OpenWindow2(parent, finalURIString.get(),
|
||||
NS_ConvertUTF16toUTF8(aName).get(),
|
||||
features, aCalledFromJS,
|
||||
false, false, this, nullptr, getter_AddRefs(window));
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(*aResult)))
|
||||
return true;
|
||||
|
||||
*aResult = NS_ERROR_FAILURE;
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> pwindow = do_QueryInterface(window);
|
||||
if (NS_WARN_IF(!pwindow)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocShell> windowDocShell = pwindow->GetDocShell();
|
||||
if (NS_WARN_IF(!windowDocShell)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
|
||||
windowDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
|
||||
|
||||
nsCOMPtr<nsIXULWindow> xulWin = do_GetInterface(treeOwner);
|
||||
if (NS_WARN_IF(!xulWin)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIXULBrowserWindow> xulBrowserWin;
|
||||
xulWin->GetXULBrowserWindow(getter_AddRefs(xulBrowserWin));
|
||||
if (NS_WARN_IF(!xulBrowserWin)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITabParent> newRemoteTab;
|
||||
*aResult = xulBrowserWin->ForceInitialBrowserRemote(getter_AddRefs(newRemoteTab));
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(*aResult)))
|
||||
return true;
|
||||
|
||||
MOZ_ASSERT(TabParent::GetFrom(newRemoteTab) == newTab);
|
||||
|
||||
aFrameScripts->SwapElements(newTab->mDelayedFrameScripts);
|
||||
return true;
|
||||
}
|
||||
|
||||
TabParent* TabParent::sNextTabParent;
|
||||
|
||||
/* static */ TabParent*
|
||||
@ -1029,7 +779,7 @@ TabParent::LoadURL(nsIURI* aURI)
|
||||
return;
|
||||
}
|
||||
|
||||
unused << SendLoadURL(spec, configuration);
|
||||
unused << SendLoadURL(spec, configuration, GetShowInfo());
|
||||
|
||||
// If this app is a packaged app then we can speed startup by sending over
|
||||
// the file descriptor for the "application.zip" file that it will
|
||||
@ -1117,20 +867,7 @@ TabParent::Show(const ScreenIntSize& size, bool aParentIsActive)
|
||||
}
|
||||
}
|
||||
|
||||
TryCacheDPIAndScale();
|
||||
ShowInfo info(EmptyString(), false, false, mDPI, mDefaultScale.scale);
|
||||
|
||||
if (mFrameElement) {
|
||||
nsAutoString name;
|
||||
mFrameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
|
||||
bool allowFullscreen =
|
||||
mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) ||
|
||||
mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen);
|
||||
bool isPrivate = mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozprivatebrowsing);
|
||||
info = ShowInfo(name, allowFullscreen, isPrivate, mDPI, mDefaultScale.scale);
|
||||
}
|
||||
|
||||
unused << SendShow(size, info, textureFactoryIdentifier,
|
||||
unused << SendShow(size, GetShowInfo(), textureFactoryIdentifier,
|
||||
layersId, renderFrame, aParentIsActive);
|
||||
}
|
||||
|
||||
@ -3631,6 +3368,25 @@ TabParent::StartPersistence(uint64_t aOuterWindowID,
|
||||
// (The actor will be destroyed on constructor failure.)
|
||||
}
|
||||
|
||||
ShowInfo
|
||||
TabParent::GetShowInfo()
|
||||
{
|
||||
TryCacheDPIAndScale();
|
||||
if (mFrameElement) {
|
||||
nsAutoString name;
|
||||
mFrameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
|
||||
bool allowFullscreen =
|
||||
mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) ||
|
||||
mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen);
|
||||
bool isPrivate = mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozprivatebrowsing);
|
||||
return ShowInfo(name, allowFullscreen, isPrivate, false,
|
||||
mDPI, mDefaultScale.scale);
|
||||
}
|
||||
|
||||
return ShowInfo(EmptyString(), false, false, false,
|
||||
mDPI, mDefaultScale.scale);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FakeChannel::OnAuthAvailable(nsISupports *aContext, nsIAuthInformation *aAuthInfo)
|
||||
{
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "mozilla/ContentCache.h"
|
||||
#include "mozilla/dom/ipc/IdType.h"
|
||||
#include "mozilla/dom/PBrowserParent.h"
|
||||
#include "mozilla/dom/PContent.h"
|
||||
#include "mozilla/dom/PFilePickerParent.h"
|
||||
#include "mozilla/dom/TabContext.h"
|
||||
#include "mozilla/EventForwards.h"
|
||||
@ -93,6 +94,9 @@ class TabParent final : public PBrowserParent
|
||||
virtual ~TabParent();
|
||||
|
||||
public:
|
||||
// Helper class for ContentParent::RecvCreateWindow.
|
||||
struct AutoUseNewTab;
|
||||
|
||||
// nsITabParent
|
||||
NS_DECL_NSITABPARENT
|
||||
// nsIDOMEventListener interfaces
|
||||
@ -125,6 +129,12 @@ public:
|
||||
mBrowserDOMWindow = aBrowserDOMWindow;
|
||||
}
|
||||
|
||||
void SetHasContentOpener(bool aHasContentOpener);
|
||||
|
||||
void SwapFrameScriptsFrom(nsTArray<FrameScriptInfo>& aFrameScripts) {
|
||||
aFrameScripts.SwapElements(mDelayedFrameScripts);
|
||||
}
|
||||
|
||||
already_AddRefed<nsILoadContext> GetLoadContext();
|
||||
already_AddRefed<nsIWidget> GetTopLevelWidget();
|
||||
nsIXULBrowserWindow* GetXULBrowserWindow();
|
||||
@ -147,19 +157,6 @@ public:
|
||||
const nsString& aName,
|
||||
const nsString& aFeatures,
|
||||
bool* aOutWindowOpened) override;
|
||||
virtual bool RecvCreateWindow(PBrowserParent* aOpener,
|
||||
const uint32_t& aChromeFlags,
|
||||
const bool& aCalledFromJS,
|
||||
const bool& aPositionSpecified,
|
||||
const bool& aSizeSpecified,
|
||||
const nsString& aURI,
|
||||
const nsString& aName,
|
||||
const nsCString& aFeatures,
|
||||
const nsString& aBaseURI,
|
||||
nsresult* aResult,
|
||||
bool* aWindowIsNew,
|
||||
InfallibleTArray<FrameScriptInfo>* aFrameScripts,
|
||||
nsCString* aURLToLoad) override;
|
||||
virtual bool RecvSyncMessage(const nsString& aMessage,
|
||||
const ClonedMessageData& aData,
|
||||
InfallibleTArray<CpowEntry>&& aCpows,
|
||||
@ -497,8 +494,6 @@ protected:
|
||||
bool InitBrowserConfiguration(const nsCString& aURI,
|
||||
BrowserConfiguration& aConfiguration);
|
||||
|
||||
void SetHasContentOpener(bool aHasContentOpener);
|
||||
|
||||
// Decide whether we have to use a new process to reload the URI associated
|
||||
// with the given channel.
|
||||
bool ShouldSwitchProcess(nsIChannel* aChannel);
|
||||
@ -584,9 +579,6 @@ private:
|
||||
|
||||
TabId mTabId;
|
||||
|
||||
// Helper class for RecvCreateWindow.
|
||||
struct AutoUseNewTab;
|
||||
|
||||
// When loading a new tab or window via window.open, the child process sends
|
||||
// a new PBrowser to use. We store that tab in sNextTabParent and then
|
||||
// proceed through the browser's normal paths to create a new
|
||||
@ -642,6 +634,8 @@ private:
|
||||
bool mHasContentOpener;
|
||||
|
||||
DebugOnly<int32_t> mActiveSupressDisplayportCount;
|
||||
|
||||
ShowInfo GetShowInfo();
|
||||
private:
|
||||
// This is used when APZ needs to find the TabParent associated with a layer
|
||||
// to dispatch events.
|
||||
@ -655,6 +649,38 @@ public:
|
||||
static TabParent* GetTabParentFromLayersId(uint64_t aLayersId);
|
||||
};
|
||||
|
||||
struct MOZ_STACK_CLASS TabParent::AutoUseNewTab final
|
||||
{
|
||||
public:
|
||||
AutoUseNewTab(TabParent* aNewTab, bool* aWindowIsNew, nsCString* aURLToLoad)
|
||||
: mNewTab(aNewTab), mWindowIsNew(aWindowIsNew), mURLToLoad(aURLToLoad)
|
||||
{
|
||||
MOZ_ASSERT(!TabParent::sNextTabParent);
|
||||
MOZ_ASSERT(!aNewTab->mCreatingWindow);
|
||||
|
||||
TabParent::sNextTabParent = aNewTab;
|
||||
aNewTab->mCreatingWindow = true;
|
||||
aNewTab->mDelayedURL.Truncate();
|
||||
}
|
||||
|
||||
~AutoUseNewTab()
|
||||
{
|
||||
mNewTab->mCreatingWindow = false;
|
||||
*mURLToLoad = mNewTab->mDelayedURL;
|
||||
|
||||
if (TabParent::sNextTabParent) {
|
||||
MOZ_ASSERT(TabParent::sNextTabParent == mNewTab);
|
||||
TabParent::sNextTabParent = nullptr;
|
||||
*mWindowIsNew = false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
TabParent* mNewTab;
|
||||
bool* mWindowIsNew;
|
||||
nsCString* mURLToLoad;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -71,18 +71,18 @@ nsIContentParent::DeallocPJavaScriptParent(PJavaScriptParent* aParent)
|
||||
bool
|
||||
nsIContentParent::CanOpenBrowser(const IPCTabContext& aContext)
|
||||
{
|
||||
const IPCTabContextUnion& contextUnion = aContext.contextUnion();
|
||||
|
||||
// We don't trust the IPCTabContext we receive from the child, so we'll bail
|
||||
// if we receive an IPCTabContext that's not a PopupIPCTabContext.
|
||||
// (PopupIPCTabContext lets the child process prove that it has access to
|
||||
// the app it's trying to open.)
|
||||
if (contextUnion.type() != IPCTabContextUnion::TPopupIPCTabContext) {
|
||||
// On e10s we also allow UnsafeTabContext to allow service workers to open
|
||||
// windows. This is enforced in MaybeInvalidTabContext.
|
||||
if (aContext.type() != IPCTabContext::TPopupIPCTabContext &&
|
||||
aContext.type() != IPCTabContext::TUnsafeIPCTabContext) {
|
||||
ASSERT_UNLESS_FUZZING("Unexpected IPCTabContext type. Aborting AllocPBrowserParent.");
|
||||
return false;
|
||||
}
|
||||
|
||||
const PopupIPCTabContext& popupContext = contextUnion.get_PopupIPCTabContext();
|
||||
if (aContext.type() == IPCTabContext::TPopupIPCTabContext) {
|
||||
const PopupIPCTabContext& popupContext = aContext.get_PopupIPCTabContext();
|
||||
if (popupContext.opener().type() != PBrowserOrId::TPBrowserParent) {
|
||||
ASSERT_UNLESS_FUZZING("Unexpected PopupIPCTabContext type. Aborting AllocPBrowserParent.");
|
||||
return false;
|
||||
@ -101,6 +101,7 @@ nsIContentParent::CanOpenBrowser(const IPCTabContext& aContext)
|
||||
ASSERT_UNLESS_FUZZING("Child trying to escalate privileges! Aborting AllocPBrowserParent.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MaybeInvalidTabContext tc(aContext);
|
||||
if (!tc.IsValid()) {
|
||||
@ -129,14 +130,12 @@ nsIContentParent::AllocPBrowserParent(const TabId& aTabId,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const IPCTabContextUnion& contextUnion = aContext.contextUnion();
|
||||
const PopupIPCTabContext& popupContext = contextUnion.get_PopupIPCTabContext();
|
||||
|
||||
uint32_t chromeFlags = aChromeFlags;
|
||||
|
||||
if (aContext.type() == IPCTabContext::TPopupIPCTabContext) {
|
||||
// CanOpenBrowser has ensured that the IPCTabContext is of
|
||||
// type PopupIPCTabContext, and that the opener TabParent is
|
||||
// reachable.
|
||||
const PopupIPCTabContext& popupContext = aContext.get_PopupIPCTabContext();
|
||||
auto opener = TabParent::GetFrom(popupContext.opener().get_PBrowserParent());
|
||||
// We must ensure that the private browsing and remoteness flags
|
||||
// match those of the opener.
|
||||
@ -150,6 +149,7 @@ nsIContentParent::AllocPBrowserParent(const TabId& aTabId,
|
||||
if (isPrivate) {
|
||||
chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
|
||||
}
|
||||
}
|
||||
|
||||
// And because we're allocating a remote browser, of course the
|
||||
// window is remote.
|
||||
|
@ -168,23 +168,21 @@ PannerNodeDopplerWarning=Use of setVelocity on the PannerNode and AudioListener,
|
||||
AppCacheWarning=The Application Cache API (AppCache) is deprecated and will be removed at a future date. Please consider using ServiceWorker for offline support.
|
||||
# LOCALIZATION NOTE: Do not translate "Worker".
|
||||
EmptyWorkerSourceWarning=Attempting to create a Worker from an empty source. This is probably unintentional.
|
||||
# LOCALIZATION NOTE: Do not translate "ServiceWorker".
|
||||
InterceptionFailed=ServiceWorker network interception failed due to an unexpected error.
|
||||
# LOCALIZATION NOTE: Do not translate "ServiceWorker", "FetchEvent.respondWith()", "opaque", or "Response".
|
||||
OpaqueInterceptionDisabled=A ServiceWorker passed an opaque Response to FetchEvent.respondWith() while opaque interception is disabled.
|
||||
# LOCALIZATION NOTE: Do not translate "ServiceWorker", "FetchEvent.respondWith()", "FetchEvent.request.type", "same-origin", "cors", "no-cors", "opaque", "Response", or "RequestMode".
|
||||
BadOpaqueInterceptionRequestMode=A ServiceWorker passed an opaque Response to FetchEvent.respondWith() while the FetchEvent.request.type was either "same-origin" or "cors". Opaque Response objects are only valid when the RequestMode is "no-cors".
|
||||
# LOCALIZATION NOTE: Do not translate "ServiceWorker", "Error", "Response", "FetchEvent.respondWith()", or "fetch()".
|
||||
InterceptedErrorResponse=A ServiceWorker passed an Error Response to FetchEvent.respondWith(). This typically means the ServiceWorker performed an invalid fetch() call.
|
||||
# LOCALIZATION NOTE: Do not translate "ServiceWorker", "Response", "FetchEvent.respondWith()", or "Response.clone()".
|
||||
InterceptedUsedResponse=A ServiceWorker passed a used Response to FetchEvent.respondWith(). The body of a Response may only be read once. Use Response.clone() to access the body multiple times.
|
||||
# LOCALIZATION NOTE: Do not translate "ServiceWorker", "opaque", "Response", "FetchEvent.respondWith()", "FetchEvent.request", or "Worker".
|
||||
ClientRequestOpaqueInterception=A ServiceWorker passed an opaque Response to FetchEvent.respondWith() while FetchEvent.request was a client request. A client request is generally a browser navigation or top-level Worker script.
|
||||
# LOCALIZATION NOTE: Do not translate "ServiceWorker", "opaqueredirect", "Response", "FetchEvent.respondWith()", or "FetchEvent.request".
|
||||
BadOpaqueRedirectInterception=A ServiceWorker passed an opaqueredirect Response to FetchEvent.respondWith() while FetchEvent.request was not a navigation request.
|
||||
# LOCALIZATION NOTE: Do not translate "ServiceWorker" or "FetchEvent.preventDefault()".
|
||||
InterceptionCanceled=ServiceWorker canceled network interception by calling FetchEvent.preventDefault().
|
||||
# LOCALIZATION NOTE: Do not translate "ServiceWorker", "Promise", or "FetchEvent.respondWith()".
|
||||
InterceptionRejectedResponse=ServiceWorker passed a rejected Promise to FetchEvent.respondWith(). This typically means that the code that resolves the Promise has failed.
|
||||
WebrtcDeprecatedPrefixWarning=WebRTC interfaces with the "moz" prefix (mozRTCPeerConnection, mozRTCSessionDescription, mozRTCIceCandidate) have been deprecated.
|
||||
NavigatorGetUserMediaWarning=navigator.mozGetUserMedia has been replaced by navigator.mediaDevices.getUserMedia
|
||||
# LOCALIZATION NOTE: Do not translate "ServiceWorker". %S is a URL.
|
||||
InterceptionFailedWithURL=Failed to load '%S'. A ServiceWorker intercepted the request and encountered an unexpected error.
|
||||
# LOCALIZATION NOTE: Do not translate "ServiceWorker", "FetchEvent.respondWith()", "opaque", or "Response". %S is a URL.
|
||||
OpaqueInterceptionDisabledWithURL=Failed to load '%S'. A ServiceWorker passed an opaque Response to FetchEvent.respondWith() while opaque interception is disabled.
|
||||
# LOCALIZATION NOTE: Do not translate "ServiceWorker", "FetchEvent.respondWith()", "FetchEvent", "no-cors", "opaque", "Response", or "RequestMode". %1$S is a URL. %2$S is a RequestMode value.
|
||||
BadOpaqueInterceptionRequestModeWithURL=Failed to load '%1$S'. A ServiceWorker passed an opaque Response to FetchEvent.respondWith() while handling a '%2$S' FetchEvent. Opaque Response objects are only valid when the RequestMode is 'no-cors'.
|
||||
# LOCALIZATION NOTE: Do not translate "ServiceWorker", "Error", "Response", "FetchEvent.respondWith()", or "fetch()". %S is a URL.
|
||||
InterceptedErrorResponseWithURL=Failed to load '%S'. A ServiceWorker passed an Error Response to FetchEvent.respondWith(). This typically means the ServiceWorker performed an invalid fetch() call.
|
||||
# LOCALIZATION NOTE: Do not translate "ServiceWorker", "Response", "FetchEvent.respondWith()", or "Response.clone()". %S is a URL.
|
||||
InterceptedUsedResponseWithURL=Failed to load '%S'. A ServiceWorker passed a used Response to FetchEvent.respondWith(). The body of a Response may only be read once. Use Response.clone() to access the body multiple times.
|
||||
# LOCALIZATION NOTE: Do not translate "ServiceWorker", "opaqueredirect", "Response", "FetchEvent.respondWith()", or "FetchEvent". %s is a URL.
|
||||
BadOpaqueRedirectInterceptionWithURL=Failed to load '%S'. A ServiceWorker passed an opaqueredirect Response to FetchEvent.respondWith() while handling a non-navigation FetchEvent.
|
||||
# LOCALIZATION NOTE: Do not translate "ServiceWorker" or "FetchEvent.preventDefault()". %S is a URL.
|
||||
InterceptionCanceledWithURL=Failed to load '%S'. A ServiceWorker canceled the load by calling FetchEvent.preventDefault().
|
||||
# LOCALIZATION NOTE: Do not translate "ServiceWorker", "promise", or "FetchEvent.respondWith()". %1$S is a URL. %2$S is an error string.
|
||||
InterceptionRejectedResponseWithURL=Failed to load '%1$S'. A ServiceWorker passed a promise to FetchEvent.respondWith() that rejected with '%2$S'.
|
||||
|
@ -259,6 +259,7 @@ MediaDecoderReader::AsyncReadMetadata()
|
||||
// Attempt to read the metadata.
|
||||
RefPtr<MetadataHolder> metadata = new MetadataHolder();
|
||||
nsresult rv = ReadMetadata(&metadata->mInfo, getter_Transfers(metadata->mTags));
|
||||
metadata->mInfo.AssertValid();
|
||||
|
||||
// We're not waiting for anything. If we didn't get the metadata, that's an
|
||||
// error.
|
||||
|
@ -343,6 +343,12 @@ public:
|
||||
mToken = nullptr;
|
||||
}
|
||||
|
||||
void DisconnectIfExists() {
|
||||
if (mToken) {
|
||||
Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Avoid exposing RevocableToken directly to the client code so that
|
||||
// listeners can be disconnected in a controlled manner.
|
||||
|
@ -37,7 +37,7 @@ public:
|
||||
const nsAString& aLabel,
|
||||
const nsAString& aLanguage,
|
||||
bool aEnabled,
|
||||
TrackID aTrackId = TRACK_INVALID)
|
||||
TrackID aTrackId)
|
||||
: mId(aId)
|
||||
, mKind(aKind)
|
||||
, mLabel(aLabel)
|
||||
@ -53,21 +53,17 @@ public:
|
||||
}
|
||||
|
||||
// Only used for backward compatibility. Do not use in new code.
|
||||
void Init(TrackType aType,
|
||||
const nsAString& aId,
|
||||
void Init(const nsAString& aId,
|
||||
const nsAString& aKind,
|
||||
const nsAString& aLabel,
|
||||
const nsAString& aLanguage,
|
||||
bool aEnabled,
|
||||
TrackID aTrackId = TRACK_INVALID)
|
||||
bool aEnabled)
|
||||
{
|
||||
mId = aId;
|
||||
mKind = aKind;
|
||||
mLabel = aLabel;
|
||||
mLanguage = aLanguage;
|
||||
mEnabled = aEnabled;
|
||||
mTrackId = aTrackId;
|
||||
mType = aType;
|
||||
}
|
||||
|
||||
// Fields common with MediaTrack object.
|
||||
@ -381,6 +377,17 @@ public:
|
||||
return HasVideo() || HasAudio();
|
||||
}
|
||||
|
||||
void AssertValid() const
|
||||
{
|
||||
NS_ASSERTION(!HasAudio() || mAudio.mTrackId != TRACK_INVALID,
|
||||
"Audio track ID must be valid");
|
||||
NS_ASSERTION(!HasVideo() || mVideo.mTrackId != TRACK_INVALID,
|
||||
"Audio track ID must be valid");
|
||||
NS_ASSERTION(!HasAudio() || !HasVideo() ||
|
||||
mAudio.mTrackId != mVideo.mTrackId,
|
||||
"Duplicate track IDs");
|
||||
}
|
||||
|
||||
// TODO: Store VideoInfo and AudioIndo in arrays to support multi-tracks.
|
||||
VideoInfo mVideo;
|
||||
AudioInfo mAudio;
|
||||
|
@ -553,7 +553,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
bool Check3gppPermission()
|
||||
bool CheckPermission(const char* aType)
|
||||
{
|
||||
nsCOMPtr<nsIDocument> doc = mRecorder->GetOwner()->GetExtantDoc();
|
||||
if (!doc) {
|
||||
@ -576,7 +576,7 @@ private:
|
||||
}
|
||||
|
||||
uint32_t perm = nsIPermissionManager::DENY_ACTION;
|
||||
pm->TestExactPermissionFromPrincipal(doc->NodePrincipal(), "audio-capture:3gpp", &perm);
|
||||
pm->TestExactPermissionFromPrincipal(doc->NodePrincipal(), aType, &perm);
|
||||
return perm == nsIPermissionManager::ALLOW_ACTION;
|
||||
}
|
||||
|
||||
@ -593,12 +593,18 @@ private:
|
||||
// At this stage, the API doesn't allow UA to choose the output mimeType format.
|
||||
|
||||
// Make sure the application has permission to assign AUDIO_3GPP
|
||||
if (mRecorder->mMimeType.EqualsLiteral(AUDIO_3GPP) && Check3gppPermission()) {
|
||||
if (mRecorder->mMimeType.EqualsLiteral(AUDIO_3GPP) && CheckPermission("audio-capture:3gpp")) {
|
||||
mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(AUDIO_3GPP),
|
||||
mRecorder->GetAudioBitrate(),
|
||||
mRecorder->GetVideoBitrate(),
|
||||
mRecorder->GetBitrate(),
|
||||
aTrackTypes);
|
||||
} else if (mRecorder->mMimeType.EqualsLiteral(AUDIO_3GPP2) && CheckPermission("audio-capture:3gpp2")) {
|
||||
mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(AUDIO_3GPP2),
|
||||
mRecorder->GetAudioBitrate(),
|
||||
mRecorder->GetVideoBitrate(),
|
||||
mRecorder->GetBitrate(),
|
||||
aTrackTypes);
|
||||
} else {
|
||||
mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(""),
|
||||
mRecorder->GetAudioBitrate(),
|
||||
|
@ -156,7 +156,7 @@ private:
|
||||
void RegisterActivityObserver();
|
||||
void UnRegisterActivityObserver();
|
||||
|
||||
bool Check3gppPermission();
|
||||
bool CheckPermission(const nsString &aType);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -580,10 +580,13 @@ MediaStreamGraphImpl::CreateOrDestroyAudioStreams(MediaStream* aStream)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aStream->GetStreamBuffer().GetAndResetTracksDirty()) {
|
||||
if (!aStream->GetStreamBuffer().GetAndResetTracksDirty() &&
|
||||
!aStream->mAudioOutputStreams.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
STREAM_LOG(LogLevel::Debug, ("Updating AudioOutputStreams for MediaStream %p", aStream));
|
||||
|
||||
nsAutoTArray<bool,2> audioOutputStreamsFound;
|
||||
for (uint32_t i = 0; i < aStream->mAudioOutputStreams.Length(); ++i) {
|
||||
audioOutputStreamsFound.AppendElement(false);
|
||||
@ -1804,9 +1807,19 @@ MediaStream::SetAudioOutputVolume(void* aKey, float aVolume)
|
||||
GraphImpl()->AppendMessage(new Message(this, aKey, aVolume));
|
||||
}
|
||||
|
||||
void
|
||||
MediaStream::AddAudioOutputImpl(void* aKey)
|
||||
{
|
||||
STREAM_LOG(LogLevel::Info, ("MediaStream %p Adding AudioOutput for key %p",
|
||||
this, aKey));
|
||||
mAudioOutputs.AppendElement(AudioOutput(aKey));
|
||||
}
|
||||
|
||||
void
|
||||
MediaStream::RemoveAudioOutputImpl(void* aKey)
|
||||
{
|
||||
STREAM_LOG(LogLevel::Info, ("MediaStream %p Removing AudioOutput for key %p",
|
||||
this, aKey));
|
||||
for (uint32_t i = 0; i < mAudioOutputs.Length(); ++i) {
|
||||
if (mAudioOutputs[i].mKey == aKey) {
|
||||
mAudioOutputs.RemoveElementAt(i);
|
||||
@ -1832,6 +1845,26 @@ MediaStream::RemoveAudioOutput(void* aKey)
|
||||
GraphImpl()->AppendMessage(new Message(this, aKey));
|
||||
}
|
||||
|
||||
void
|
||||
MediaStream::AddVideoOutputImpl(already_AddRefed<VideoFrameContainer> aContainer)
|
||||
{
|
||||
RefPtr<VideoFrameContainer> container = aContainer;
|
||||
STREAM_LOG(LogLevel::Info, ("MediaStream %p Adding VideoFrameContainer %p as output",
|
||||
this, container.get()));
|
||||
*mVideoOutputs.AppendElement() = container.forget();
|
||||
}
|
||||
|
||||
void
|
||||
MediaStream::RemoveVideoOutputImpl(VideoFrameContainer* aContainer)
|
||||
{
|
||||
STREAM_LOG(LogLevel::Info, ("MediaStream %p Removing VideoFrameContainer %p as output",
|
||||
this, aContainer));
|
||||
// Ensure that any frames currently queued for playback by the compositor
|
||||
// are removed.
|
||||
aContainer->ClearFutureFrames();
|
||||
mVideoOutputs.RemoveElement(aContainer);
|
||||
}
|
||||
|
||||
void
|
||||
MediaStream::AddVideoOutput(VideoFrameContainer* aContainer)
|
||||
{
|
||||
|
@ -440,27 +440,15 @@ public:
|
||||
void DumpTrackInfo() { return mBuffer.DumpTrackInfo(); }
|
||||
#endif
|
||||
void SetAudioOutputVolumeImpl(void* aKey, float aVolume);
|
||||
void AddAudioOutputImpl(void* aKey)
|
||||
{
|
||||
mAudioOutputs.AppendElement(AudioOutput(aKey));
|
||||
}
|
||||
void AddAudioOutputImpl(void* aKey);
|
||||
// Returns true if this stream has an audio output.
|
||||
bool HasAudioOutput()
|
||||
{
|
||||
return !mAudioOutputs.IsEmpty();
|
||||
}
|
||||
void RemoveAudioOutputImpl(void* aKey);
|
||||
void AddVideoOutputImpl(already_AddRefed<VideoFrameContainer> aContainer)
|
||||
{
|
||||
*mVideoOutputs.AppendElement() = aContainer;
|
||||
}
|
||||
void RemoveVideoOutputImpl(VideoFrameContainer* aContainer)
|
||||
{
|
||||
// Ensure that any frames currently queued for playback by the compositor
|
||||
// are removed.
|
||||
aContainer->ClearFutureFrames();
|
||||
mVideoOutputs.RemoveElement(aContainer);
|
||||
}
|
||||
void AddVideoOutputImpl(already_AddRefed<VideoFrameContainer> aContainer);
|
||||
void RemoveVideoOutputImpl(VideoFrameContainer* aContainer);
|
||||
void AddListenerImpl(already_AddRefed<MediaStreamListener> aListener);
|
||||
void RemoveListenerImpl(MediaStreamListener* aListener);
|
||||
void RemoveAllListenersImpl();
|
||||
|
@ -60,6 +60,8 @@ public:
|
||||
AAC_CSD, // AAC codec specific data
|
||||
AMR_AUDIO_CSD,
|
||||
AMR_AUDIO_FRAME,
|
||||
EVRC_AUDIO_CSD,
|
||||
EVRC_AUDIO_FRAME,
|
||||
UNKNOWN // FrameType not set
|
||||
};
|
||||
void SwapInFrameData(nsTArray<uint8_t>& aData)
|
||||
|
@ -130,6 +130,14 @@ MediaEncoder::CreateEncoder(const nsAString& aMIMEType, uint32_t aAudioBitrate,
|
||||
writer = new ISOMediaWriter(aTrackTypes, ISOMediaWriter::TYPE_FRAG_3GP);
|
||||
NS_ENSURE_TRUE(writer, nullptr);
|
||||
mimeType = NS_LITERAL_STRING(AUDIO_3GPP);
|
||||
} else if (MediaEncoder::IsOMXEncoderEnabled() &&
|
||||
(aMIMEType.EqualsLiteral(AUDIO_3GPP2))) {
|
||||
audioEncoder = new OmxEVRCAudioTrackEncoder();
|
||||
NS_ENSURE_TRUE(audioEncoder, nullptr);
|
||||
|
||||
writer = new ISOMediaWriter(aTrackTypes, ISOMediaWriter::TYPE_FRAG_3G2);
|
||||
NS_ENSURE_TRUE(writer, nullptr);
|
||||
mimeType = NS_LITERAL_STRING(AUDIO_3GPP2) ;
|
||||
}
|
||||
#endif // MOZ_OMX_ENCODER
|
||||
else if (MediaDecoder::IsOggEnabled() && MediaDecoder::IsOpusEnabled() &&
|
||||
|
@ -184,7 +184,7 @@ OmxAudioTrackEncoder::AppendEncodedFrames(EncodedFrameContainer& aContainer)
|
||||
3000); // wait up to 3ms
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!frameData.IsEmpty()) {
|
||||
if (!frameData.IsEmpty() || outFlags & OMXCodecWrapper::BUFFER_EOS) { // Some hw codec may send out EOS with an empty frame
|
||||
bool isCSD = false;
|
||||
if (outFlags & OMXCodecWrapper::BUFFER_CODEC_CONFIG) { // codec specific data
|
||||
isCSD = true;
|
||||
@ -199,6 +199,9 @@ OmxAudioTrackEncoder::AppendEncodedFrames(EncodedFrameContainer& aContainer)
|
||||
} else if (mEncoder->GetCodecType() == OMXCodecWrapper::AMR_NB_ENC){
|
||||
audiodata->SetFrameType(isCSD ?
|
||||
EncodedFrame::AMR_AUDIO_CSD : EncodedFrame::AMR_AUDIO_FRAME);
|
||||
} else if (mEncoder->GetCodecType() == OMXCodecWrapper::EVRC_ENC){
|
||||
audiodata->SetFrameType(isCSD ?
|
||||
EncodedFrame::EVRC_AUDIO_CSD : EncodedFrame::EVRC_AUDIO_FRAME);
|
||||
} else {
|
||||
MOZ_ASSERT(false, "audio codec not supported");
|
||||
}
|
||||
@ -343,4 +346,44 @@ OmxAMRAudioTrackEncoder::GetMetadata()
|
||||
return meta.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
OmxEVRCAudioTrackEncoder::Init(int aChannels, int aSamplingRate)
|
||||
{
|
||||
mChannels = aChannels;
|
||||
mSamplingRate = aSamplingRate;
|
||||
|
||||
mEncoder = OMXCodecWrapper::CreateEVRCEncoder();
|
||||
NS_ENSURE_TRUE(mEncoder, NS_ERROR_FAILURE);
|
||||
|
||||
nsresult rv = mEncoder->Configure(mChannels, mSamplingRate, EVRC_SAMPLERATE);
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
mInitialized = (rv == NS_OK);
|
||||
|
||||
mReentrantMonitor.NotifyAll();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<TrackMetadataBase>
|
||||
OmxEVRCAudioTrackEncoder::GetMetadata()
|
||||
{
|
||||
PROFILER_LABEL("OmxEVRCAudioTrackEncoder", "GetMetadata",
|
||||
js::ProfileEntry::Category::OTHER);
|
||||
{
|
||||
// Wait if mEncoder is not initialized nor is being canceled.
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
while (!mCanceled && !mInitialized) {
|
||||
mReentrantMonitor.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
if (mCanceled || mEncodingComplete) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<EVRCTrackMetadata> meta = new EVRCTrackMetadata();
|
||||
meta->mChannels = mChannels;
|
||||
return meta.forget();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -90,5 +90,21 @@ protected:
|
||||
nsresult Init(int aChannels, int aSamplingRate) override;
|
||||
};
|
||||
|
||||
class OmxEVRCAudioTrackEncoder final : public OmxAudioTrackEncoder
|
||||
{
|
||||
public:
|
||||
OmxEVRCAudioTrackEncoder()
|
||||
: OmxAudioTrackEncoder()
|
||||
{}
|
||||
|
||||
enum {
|
||||
EVRC_SAMPLERATE = 8000,
|
||||
};
|
||||
already_AddRefed<TrackMetadataBase> GetMetadata() override;
|
||||
|
||||
protected:
|
||||
nsresult Init(int aChannels, int aSamplingRate) override;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
|
@ -22,6 +22,7 @@ public:
|
||||
METADATA_AVC,
|
||||
METADATA_AAC,
|
||||
METADATA_AMR,
|
||||
METADATA_EVRC,
|
||||
METADATA_UNKNOWN // Metadata Kind not set
|
||||
};
|
||||
// Return the specific metadata kind
|
||||
|
84
dom/media/encoder/fmp4_muxer/EVRCBox.cpp
Normal file
84
dom/media/encoder/fmp4_muxer/EVRCBox.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
/* -*- 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 "ISOControl.h"
|
||||
#include "ISOMediaBoxes.h"
|
||||
#include "EVRCBox.h"
|
||||
#include "ISOTrackMetadata.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
nsresult
|
||||
EVRCSampleEntry::Generate(uint32_t* aBoxSize)
|
||||
{
|
||||
uint32_t box_size;
|
||||
nsresult rv = evrc_special_box->Generate(&box_size);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
size += box_size;
|
||||
|
||||
*aBoxSize = size;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
EVRCSampleEntry::Write()
|
||||
{
|
||||
BoxSizeChecker checker(mControl, size);
|
||||
nsresult rv;
|
||||
rv = AudioSampleEntry::Write();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = evrc_special_box->Write();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
EVRCSampleEntry::EVRCSampleEntry(ISOControl* aControl)
|
||||
: AudioSampleEntry(NS_LITERAL_CSTRING("sevc"), aControl)
|
||||
{
|
||||
evrc_special_box = new EVRCSpecificBox(aControl);
|
||||
MOZ_COUNT_CTOR(EVRCSampleEntry);
|
||||
}
|
||||
|
||||
EVRCSampleEntry::~EVRCSampleEntry()
|
||||
{
|
||||
MOZ_COUNT_DTOR(EVRCSampleEntry);
|
||||
}
|
||||
|
||||
nsresult
|
||||
EVRCSpecificBox::Generate(uint32_t* aBoxSize)
|
||||
{
|
||||
nsresult rv;
|
||||
FragmentBuffer* frag = mControl->GetFragment(Audio_Track);
|
||||
rv = frag->GetCSD(evrcDecSpecInfo);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
size += evrcDecSpecInfo.Length();
|
||||
*aBoxSize = size;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
EVRCSpecificBox::Write()
|
||||
{
|
||||
BoxSizeChecker checker(mControl, size);
|
||||
Box::Write();
|
||||
mControl->Write(evrcDecSpecInfo.Elements(), evrcDecSpecInfo.Length());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
EVRCSpecificBox::EVRCSpecificBox(ISOControl* aControl)
|
||||
: Box(NS_LITERAL_CSTRING("devc"), aControl)
|
||||
{
|
||||
MOZ_COUNT_CTOR(EVRCSpecificBox);
|
||||
}
|
||||
|
||||
EVRCSpecificBox::~EVRCSpecificBox()
|
||||
{
|
||||
MOZ_COUNT_DTOR(EVRCSpecificBox);
|
||||
}
|
||||
|
||||
}
|
50
dom/media/encoder/fmp4_muxer/EVRCBox.h
Normal file
50
dom/media/encoder/fmp4_muxer/EVRCBox.h
Normal file
@ -0,0 +1,50 @@
|
||||
/* -*- 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 EVRCBOX_h_
|
||||
#define EVRCBOX_h_
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "MuxerOperation.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ISOControl;
|
||||
|
||||
// 3GPP TS 26.244 6.7 'EVRCSpecificBox field for EVRCSampleEntry box'
|
||||
// Box type: 'devc'
|
||||
class EVRCSpecificBox : public Box {
|
||||
public:
|
||||
// 3GPP members
|
||||
nsTArray<uint8_t> evrcDecSpecInfo;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// EVRCSpecificBox methods
|
||||
EVRCSpecificBox(ISOControl* aControl);
|
||||
~EVRCSpecificBox();
|
||||
};
|
||||
|
||||
// 3GPP TS 26.244 6.5 'EVRCSampleEntry box'
|
||||
// Box type: 'sevc'
|
||||
class EVRCSampleEntry : public AudioSampleEntry {
|
||||
public:
|
||||
// 3GPP members
|
||||
RefPtr<EVRCSpecificBox> evrc_special_box;
|
||||
|
||||
// MuxerOperation methods
|
||||
nsresult Generate(uint32_t* aBoxSize) override;
|
||||
nsresult Write() override;
|
||||
|
||||
// EVRCSampleEntry methods
|
||||
EVRCSampleEntry(ISOControl* aControl);
|
||||
~EVRCSampleEntry();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // EVRCBOX_h_
|
@ -62,7 +62,7 @@ FragmentBuffer::AddFrame(EncodedFrame* aFrame)
|
||||
|
||||
EncodedFrame::FrameType type = aFrame->GetFrameType();
|
||||
if (type == EncodedFrame::AAC_CSD || type == EncodedFrame::AVC_CSD ||
|
||||
type == EncodedFrame::AMR_AUDIO_CSD) {
|
||||
type == EncodedFrame::AMR_AUDIO_CSD || type == EncodedFrame::EVRC_AUDIO_CSD) {
|
||||
mCSDFrame = aFrame;
|
||||
// Use CSD's timestamp as the start time. Encoder should send CSD frame first
|
||||
// and then data frames.
|
||||
@ -168,7 +168,8 @@ ISOControl::SetMetadata(TrackMetadataBase* aTrackMeta)
|
||||
{
|
||||
if (aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AAC ||
|
||||
aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AMR ||
|
||||
aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AVC) {
|
||||
aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AVC ||
|
||||
aTrackMeta->GetKind() == TrackMetadataBase::METADATA_EVRC) {
|
||||
mMetaArray.AppendElement(aTrackMeta);
|
||||
return NS_OK;
|
||||
}
|
||||
@ -180,7 +181,8 @@ ISOControl::GetAudioMetadata(RefPtr<AudioTrackMetadata>& aAudMeta)
|
||||
{
|
||||
for (uint32_t i = 0; i < mMetaArray.Length() ; i++) {
|
||||
if (mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AAC ||
|
||||
mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AMR) {
|
||||
mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AMR ||
|
||||
mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_EVRC) {
|
||||
aAudMeta = static_cast<AudioTrackMetadata*>(mMetaArray[i].get());
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "MP4ESDS.h"
|
||||
#include "AMRBox.h"
|
||||
#include "AVCBox.h"
|
||||
#include "EVRCBox.h"
|
||||
#include "VideoUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
@ -666,6 +667,8 @@ SampleDescriptionBox::CreateAudioSampleEntry(RefPtr<SampleEntryBox>& aSampleEntr
|
||||
aSampleEntry = new AMRSampleEntry(mControl);
|
||||
} else if (mAudioMeta->GetKind() == TrackMetadataBase::METADATA_AAC) {
|
||||
aSampleEntry = new MP4AudioSampleEntry(mControl);
|
||||
} else if (mAudioMeta->GetKind() == TrackMetadataBase::METADATA_EVRC) {
|
||||
aSampleEntry = new EVRCSampleEntry(mControl);
|
||||
} else {
|
||||
MOZ_ASSERT(0);
|
||||
}
|
||||
@ -1250,6 +1253,17 @@ FileTypeBox::Generate(uint32_t* aBoxSize)
|
||||
compatible_brands.AppendElement("3gp7");
|
||||
compatible_brands.AppendElement("3gp6");
|
||||
compatible_brands.AppendElement("isom");
|
||||
} else if (mControl->GetMuxingType() == ISOMediaWriter::TYPE_FRAG_3G2) {
|
||||
major_brand = "3g2a";
|
||||
// 3GPP2 Release 0 and A and 3GPP Release 6 allow movie fragmentation
|
||||
compatible_brands.AppendElement("3gp9");
|
||||
compatible_brands.AppendElement("3gp8");
|
||||
compatible_brands.AppendElement("3gp7");
|
||||
compatible_brands.AppendElement("3gp6");
|
||||
compatible_brands.AppendElement("isom");
|
||||
compatible_brands.AppendElement("3g2c");
|
||||
compatible_brands.AppendElement("3g2b");
|
||||
compatible_brands.AppendElement("3g2a");
|
||||
} else {
|
||||
MOZ_ASSERT(0);
|
||||
}
|
||||
|
@ -104,7 +104,9 @@ ISOMediaWriter::WriteEncodedTrack(const EncodedFrameContainer& aData,
|
||||
if (type == EncodedFrame::AAC_AUDIO_FRAME ||
|
||||
type == EncodedFrame::AAC_CSD ||
|
||||
type == EncodedFrame::AMR_AUDIO_FRAME ||
|
||||
type == EncodedFrame::AMR_AUDIO_CSD) {
|
||||
type == EncodedFrame::AMR_AUDIO_CSD ||
|
||||
type == EncodedFrame::EVRC_AUDIO_FRAME ||
|
||||
type == EncodedFrame::EVRC_AUDIO_CSD) {
|
||||
frag = mAudioFragmentBuffer;
|
||||
} else if (type == EncodedFrame::AVC_I_FRAME ||
|
||||
type == EncodedFrame::AVC_P_FRAME ||
|
||||
@ -212,7 +214,8 @@ ISOMediaWriter::SetMetadata(TrackMetadataBase* aMetadata)
|
||||
PROFILER_LABEL("ISOMediaWriter", "SetMetadata",
|
||||
js::ProfileEntry::Category::OTHER);
|
||||
if (aMetadata->GetKind() == TrackMetadataBase::METADATA_AAC ||
|
||||
aMetadata->GetKind() == TrackMetadataBase::METADATA_AMR) {
|
||||
aMetadata->GetKind() == TrackMetadataBase::METADATA_AMR ||
|
||||
aMetadata->GetKind() == TrackMetadataBase::METADATA_EVRC) {
|
||||
mControl->SetMetadata(aMetadata);
|
||||
mAudioFragmentBuffer = new FragmentBuffer(Audio_Track, FRAG_DURATION);
|
||||
mControl->SetFragment(mAudioFragmentBuffer);
|
||||
|
@ -26,6 +26,10 @@ public:
|
||||
// Brand names in 'ftyp' box are '3gp9' and 'isom'.
|
||||
const static uint32_t TYPE_FRAG_3GP = 1 << 1;
|
||||
|
||||
// Generate an fragmented 3G2 stream, 3GPP2 C.S0050-B
|
||||
// Brand names in 'ftyp' box are '3g2c' and 'isom'
|
||||
const static uint32_t TYPE_FRAG_3G2 = 1 << 2;
|
||||
|
||||
// aType is the combination of CREATE_AUDIO_TRACK and CREATE_VIDEO_TRACK.
|
||||
// It is a hint to muxer that the output streaming contains audio, video
|
||||
// or both.
|
||||
|
@ -99,6 +99,33 @@ public:
|
||||
~AMRTrackMetadata() { MOZ_COUNT_DTOR(AMRTrackMetadata); }
|
||||
};
|
||||
|
||||
// EVRC sample rate is 8000 samples/s.
|
||||
#define EVRC_SAMPLE_RATE 8000
|
||||
|
||||
class EVRCTrackMetadata : public AudioTrackMetadata {
|
||||
public:
|
||||
// AudioTrackMetadata members
|
||||
//
|
||||
// The number of sample sets generates by encoder is variant. So the
|
||||
// frame duration and frame size are both 0.
|
||||
uint32_t GetAudioFrameDuration() override { return 0; }
|
||||
uint32_t GetAudioFrameSize() override { return 0; }
|
||||
uint32_t GetAudioSampleRate() override { return EVRC_SAMPLE_RATE; }
|
||||
uint32_t GetAudioChannels() override { return mChannels; }
|
||||
|
||||
// TrackMetadataBase member
|
||||
MetadataKind GetKind() const override { return METADATA_EVRC; }
|
||||
|
||||
// EVRCTrackMetadata members
|
||||
EVRCTrackMetadata()
|
||||
: mChannels(0) {
|
||||
MOZ_COUNT_CTOR(EVRCTrackMetadata);
|
||||
}
|
||||
~EVRCTrackMetadata() { MOZ_COUNT_DTOR(EVRCTrackMetadata); }
|
||||
|
||||
uint32_t mChannels; // Channel number, it should be 1 or 2.
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // ISOTrackMetadata_h_
|
||||
|
@ -12,6 +12,7 @@ EXPORTS += [
|
||||
UNIFIED_SOURCES += [
|
||||
'AMRBox.cpp',
|
||||
'AVCBox.cpp',
|
||||
'EVRCBox.cpp',
|
||||
'ISOControl.cpp',
|
||||
'ISOMediaBoxes.cpp',
|
||||
'ISOMediaWriter.cpp',
|
||||
|
@ -106,8 +106,7 @@ static const nsString GetKind(const nsCString& aRole)
|
||||
return EmptyString();
|
||||
}
|
||||
|
||||
static void InitTrack(TrackInfo::TrackType aTrackType,
|
||||
MessageField* aMsgInfo,
|
||||
static void InitTrack(MessageField* aMsgInfo,
|
||||
TrackInfo* aInfo,
|
||||
bool aEnable)
|
||||
{
|
||||
@ -118,8 +117,7 @@ static void InitTrack(TrackInfo::TrackType aTrackType,
|
||||
nsCString* sRole = aMsgInfo->mValuesStore.Get(eRole);
|
||||
nsCString* sTitle = aMsgInfo->mValuesStore.Get(eTitle);
|
||||
nsCString* sLanguage = aMsgInfo->mValuesStore.Get(eLanguage);
|
||||
aInfo->Init(aTrackType,
|
||||
sName? NS_ConvertUTF8toUTF16(*sName):EmptyString(),
|
||||
aInfo->Init(sName? NS_ConvertUTF8toUTF16(*sName):EmptyString(),
|
||||
sRole? GetKind(*sRole):EmptyString(),
|
||||
sTitle? NS_ConvertUTF8toUTF16(*sTitle):EmptyString(),
|
||||
sLanguage? NS_ConvertUTF8toUTF16(*sLanguage):EmptyString(),
|
||||
@ -319,8 +317,7 @@ void OggReader::SetupMediaTracksInfo(const nsTArray<uint32_t>& aSerials)
|
||||
}
|
||||
|
||||
if (msgInfo) {
|
||||
InitTrack(TrackInfo::kVideoTrack,
|
||||
msgInfo,
|
||||
InitTrack(msgInfo,
|
||||
&mInfo.mVideo,
|
||||
mTheoraState == theoraState);
|
||||
}
|
||||
@ -344,8 +341,7 @@ void OggReader::SetupMediaTracksInfo(const nsTArray<uint32_t>& aSerials)
|
||||
}
|
||||
|
||||
if (msgInfo) {
|
||||
InitTrack(TrackInfo::kAudioTrack,
|
||||
msgInfo,
|
||||
InitTrack(msgInfo,
|
||||
&mInfo.mAudio,
|
||||
mVorbisState == vorbisState);
|
||||
}
|
||||
@ -359,8 +355,7 @@ void OggReader::SetupMediaTracksInfo(const nsTArray<uint32_t>& aSerials)
|
||||
}
|
||||
|
||||
if (msgInfo) {
|
||||
InitTrack(TrackInfo::kAudioTrack,
|
||||
msgInfo,
|
||||
InitTrack(msgInfo,
|
||||
&mInfo.mAudio,
|
||||
mOpusState == opusState);
|
||||
}
|
||||
@ -777,7 +772,7 @@ bool OggReader::ReadOggChain()
|
||||
LOG(LogLevel::Debug, ("New vorbis ogg link, serial=%d\n", mVorbisSerial));
|
||||
|
||||
if (msgInfo) {
|
||||
InitTrack(TrackInfo::kAudioTrack, msgInfo, &mInfo.mAudio, true);
|
||||
InitTrack(msgInfo, &mInfo.mAudio, true);
|
||||
}
|
||||
mInfo.mAudio.mRate = newVorbisState->mInfo.rate;
|
||||
mInfo.mAudio.mChannels = newVorbisState->mInfo.channels;
|
||||
@ -793,7 +788,7 @@ bool OggReader::ReadOggChain()
|
||||
SetupTargetOpus(newOpusState);
|
||||
|
||||
if (msgInfo) {
|
||||
InitTrack(TrackInfo::kAudioTrack, msgInfo, &mInfo.mAudio, true);
|
||||
InitTrack(msgInfo, &mInfo.mAudio, true);
|
||||
}
|
||||
mInfo.mAudio.mRate = newOpusState->mRate;
|
||||
mInfo.mAudio.mChannels = newOpusState->mChannels;
|
||||
|
@ -30,6 +30,7 @@ using namespace mozilla::layers;
|
||||
#define INPUT_BUFFER_TIMEOUT_US (5 * 1000ll)
|
||||
// AMR NB kbps
|
||||
#define AMRNB_BITRATE 12200
|
||||
#define EVRC_BITRATE 8755
|
||||
|
||||
#define CODEC_ERROR(args...) \
|
||||
do { \
|
||||
@ -38,6 +39,8 @@ using namespace mozilla::layers;
|
||||
|
||||
namespace android {
|
||||
|
||||
const char *MEDIA_MIMETYPE_AUDIO_EVRC = "audio/evrc";
|
||||
|
||||
enum BufferState
|
||||
{
|
||||
BUFFER_OK,
|
||||
@ -85,6 +88,16 @@ OMXCodecWrapper::CreateAMRNBEncoder()
|
||||
return amr.forget();
|
||||
}
|
||||
|
||||
OMXAudioEncoder*
|
||||
OMXCodecWrapper::CreateEVRCEncoder()
|
||||
{
|
||||
nsAutoPtr<OMXAudioEncoder> evrc(new OMXAudioEncoder(CodecType::EVRC_ENC));
|
||||
// Return the object only when media codec is valid.
|
||||
NS_ENSURE_TRUE(evrc->IsValid(), nullptr);
|
||||
|
||||
return evrc.forget();
|
||||
}
|
||||
|
||||
OMXVideoEncoder*
|
||||
OMXCodecWrapper::CreateAVCEncoder()
|
||||
{
|
||||
@ -99,6 +112,7 @@ OMXCodecWrapper::OMXCodecWrapper(CodecType aCodecType)
|
||||
: mCodecType(aCodecType)
|
||||
, mStarted(false)
|
||||
, mAMRCSDProvided(false)
|
||||
, mEVRCCSDProvided(false)
|
||||
{
|
||||
ProcessState::self()->startThreadPool();
|
||||
|
||||
@ -111,6 +125,8 @@ OMXCodecWrapper::OMXCodecWrapper(CodecType aCodecType)
|
||||
mCodec = MediaCodec::CreateByType(mLooper, MEDIA_MIMETYPE_AUDIO_AMR_NB, true);
|
||||
} else if (aCodecType == CodecType::AAC_ENC) {
|
||||
mCodec = MediaCodec::CreateByType(mLooper, MEDIA_MIMETYPE_AUDIO_AAC, true);
|
||||
} else if (aCodecType == CodecType::EVRC_ENC) {
|
||||
mCodec = MediaCodec::CreateByType(mLooper, MEDIA_MIMETYPE_AUDIO_EVRC, true);
|
||||
} else {
|
||||
NS_ERROR("Unknown codec type.");
|
||||
}
|
||||
@ -633,6 +649,10 @@ OMXAudioEncoder::Configure(int aChannels, int aInputSampleRate,
|
||||
format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_NB);
|
||||
format->setInt32("bitrate", AMRNB_BITRATE);
|
||||
format->setInt32("sample-rate", aEncodedSampleRate);
|
||||
} else if (mCodecType == EVRC_ENC) {
|
||||
format->setString("mime", MEDIA_MIMETYPE_AUDIO_EVRC);
|
||||
format->setInt32("bitrate", EVRC_BITRATE);
|
||||
format->setInt32("sample-rate", aEncodedSampleRate);
|
||||
} else {
|
||||
MOZ_ASSERT(false, "Can't support this codec type!!");
|
||||
}
|
||||
@ -1070,6 +1090,18 @@ OMXCodecWrapper::GetNextEncodedFrame(nsTArray<uint8_t>* aOutputBuf,
|
||||
aOutputBuf->AppendElements(decConfig, sizeof(decConfig));
|
||||
outFlags |= MediaCodec::BUFFER_FLAG_CODECCONFIG;
|
||||
mAMRCSDProvided = true;
|
||||
} else if ((mCodecType == EVRC_ENC) && !mEVRCCSDProvided){
|
||||
// OMX EVRC codec won't provide csd data, need to generate a fake one.
|
||||
RefPtr<EncodedFrame> audiodata = new EncodedFrame();
|
||||
// Decoder config descriptor
|
||||
const uint8_t decConfig[] = {
|
||||
0x0, 0x0, 0x0, 0x0, // vendor: 4 bytes
|
||||
0x0, // decoder version
|
||||
0x01, // frames per sample
|
||||
};
|
||||
aOutputBuf->AppendElements(decConfig, sizeof(decConfig));
|
||||
outFlags |= MediaCodec::BUFFER_FLAG_CODECCONFIG;
|
||||
mEVRCCSDProvided = true;
|
||||
} else {
|
||||
AppendFrame(aOutputBuf, omxBuf->data(), omxBuf->size());
|
||||
}
|
||||
|
@ -88,6 +88,7 @@ public:
|
||||
AAC_ENC, // AAC encoder.
|
||||
AMR_NB_ENC, // AMR_NB encoder.
|
||||
AVC_ENC, // AVC/H.264 encoder.
|
||||
EVRC_ENC, // EVRC encoder
|
||||
TYPE_COUNT
|
||||
};
|
||||
|
||||
@ -120,6 +121,9 @@ public:
|
||||
/** Create a AMR audio encoder. Returns nullptr when failed. */
|
||||
static OMXAudioEncoder* CreateAMRNBEncoder();
|
||||
|
||||
/** Create a EVRC audio encoder. Returns nullptr when failed. */
|
||||
static OMXAudioEncoder* CreateEVRCEncoder();
|
||||
|
||||
/** Create a AVC/H.264 video encoder. Returns nullptr when failed. */
|
||||
static OMXVideoEncoder* CreateAVCEncoder();
|
||||
|
||||
@ -204,6 +208,7 @@ private:
|
||||
int mCodecType;
|
||||
bool mStarted; // Has MediaCodec been started?
|
||||
bool mAMRCSDProvided;
|
||||
bool mEVRCCSDProvided;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -130,7 +130,7 @@ static_assert(MAX_WORKERS_PER_DOMAIN >= 1,
|
||||
{ \
|
||||
MutexAutoLock lock(mMutex); \
|
||||
\
|
||||
mDomainMap.EnumerateRead(AddAllTopLevelWorkersToArray, &workers); \
|
||||
AddAllTopLevelWorkersToArray(workers); \
|
||||
} \
|
||||
\
|
||||
if (!workers.IsEmpty()) { \
|
||||
@ -1580,6 +1580,28 @@ RuntimeService::RegisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
RuntimeService::RemoveSharedWorker(WorkerDomainInfo* aDomainInfo,
|
||||
WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
for (auto iter = aDomainInfo->mSharedWorkerInfos.Iter();
|
||||
!iter.Done();
|
||||
iter.Next()) {
|
||||
SharedWorkerInfo* data = iter.UserData();
|
||||
if (data->mWorkerPrivate == aWorkerPrivate) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "njn: RemoveSharedWorker\n");
|
||||
nsAutoCString key;
|
||||
GenerateSharedWorkerKey(data->mScriptSpec, data->mName,
|
||||
aWorkerPrivate->IsInPrivateBrowsing(), key);
|
||||
MOZ_ASSERT(iter.Key() == key);
|
||||
#endif
|
||||
iter.Remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RuntimeService::UnregisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
@ -1622,19 +1644,8 @@ RuntimeService::UnregisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
domainInfo->mActiveWorkers.RemoveElement(aWorkerPrivate);
|
||||
}
|
||||
|
||||
|
||||
if (aWorkerPrivate->IsSharedWorker()) {
|
||||
MatchSharedWorkerInfo match(aWorkerPrivate);
|
||||
domainInfo->mSharedWorkerInfos.EnumerateRead(FindSharedWorkerInfo,
|
||||
&match);
|
||||
|
||||
if (match.mSharedWorkerInfo) {
|
||||
nsAutoCString key;
|
||||
GenerateSharedWorkerKey(match.mSharedWorkerInfo->mScriptSpec,
|
||||
match.mSharedWorkerInfo->mName,
|
||||
aWorkerPrivate->IsInPrivateBrowsing(), key);
|
||||
domainInfo->mSharedWorkerInfos.Remove(key);
|
||||
}
|
||||
RemoveSharedWorker(domainInfo, aWorkerPrivate);
|
||||
}
|
||||
|
||||
// See if there's a queued worker we can schedule.
|
||||
@ -1676,7 +1687,21 @@ RuntimeService::UnregisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
parent->RemoveChildWorker(aCx, aWorkerPrivate);
|
||||
}
|
||||
else if (aWorkerPrivate->IsSharedWorker()) {
|
||||
mWindowMap.Enumerate(RemoveSharedWorkerFromWindowMap, aWorkerPrivate);
|
||||
AssertIsOnMainThread();
|
||||
|
||||
for (auto iter = mWindowMap.Iter(); !iter.Done(); iter.Next()) {
|
||||
nsAutoPtr<nsTArray<WorkerPrivate*>>& workers = iter.Data();
|
||||
MOZ_ASSERT(workers.get());
|
||||
|
||||
if (workers->RemoveElement(aWorkerPrivate)) {
|
||||
MOZ_ASSERT(!workers->Contains(aWorkerPrivate),
|
||||
"Added worker more than once!");
|
||||
|
||||
if (workers->IsEmpty()) {
|
||||
iter.Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (aWorkerPrivate->IsDedicatedWorker()) {
|
||||
// May be null.
|
||||
@ -1966,7 +1991,7 @@ RuntimeService::Shutdown()
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
nsAutoTArray<WorkerPrivate*, 100> workers;
|
||||
mDomainMap.EnumerateRead(AddAllTopLevelWorkersToArray, &workers);
|
||||
AddAllTopLevelWorkersToArray(workers);
|
||||
|
||||
if (!workers.IsEmpty()) {
|
||||
// Cancel all top-level workers.
|
||||
@ -2007,7 +2032,7 @@ RuntimeService::Cleanup()
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
nsAutoTArray<WorkerPrivate*, 100> workers;
|
||||
mDomainMap.EnumerateRead(AddAllTopLevelWorkersToArray, &workers);
|
||||
AddAllTopLevelWorkersToArray(workers);
|
||||
|
||||
if (!workers.IsEmpty()) {
|
||||
nsIThread* currentThread = NS_GetCurrentThread();
|
||||
@ -2118,14 +2143,12 @@ RuntimeService::Cleanup()
|
||||
nsLayoutStatics::Release();
|
||||
}
|
||||
|
||||
// static
|
||||
PLDHashOperator
|
||||
RuntimeService::AddAllTopLevelWorkersToArray(const nsACString& aKey,
|
||||
WorkerDomainInfo* aData,
|
||||
void* aUserArg)
|
||||
void
|
||||
RuntimeService::AddAllTopLevelWorkersToArray(nsTArray<WorkerPrivate*>& aWorkers)
|
||||
{
|
||||
nsTArray<WorkerPrivate*>* array =
|
||||
static_cast<nsTArray<WorkerPrivate*>*>(aUserArg);
|
||||
for (auto iter = mDomainMap.Iter(); !iter.Done(); iter.Next()) {
|
||||
|
||||
WorkerDomainInfo* aData = iter.UserData();
|
||||
|
||||
#ifdef DEBUG
|
||||
for (uint32_t index = 0; index < aData->mActiveWorkers.Length(); index++) {
|
||||
@ -2138,60 +2161,17 @@ RuntimeService::AddAllTopLevelWorkersToArray(const nsACString& aKey,
|
||||
}
|
||||
#endif
|
||||
|
||||
array->AppendElements(aData->mActiveWorkers);
|
||||
array->AppendElements(aData->mActiveServiceWorkers);
|
||||
aWorkers.AppendElements(aData->mActiveWorkers);
|
||||
aWorkers.AppendElements(aData->mActiveServiceWorkers);
|
||||
|
||||
// These might not be top-level workers...
|
||||
for (uint32_t index = 0; index < aData->mQueuedWorkers.Length(); index++) {
|
||||
WorkerPrivate* worker = aData->mQueuedWorkers[index];
|
||||
if (!worker->GetParent()) {
|
||||
array->AppendElement(worker);
|
||||
aWorkers.AppendElement(worker);
|
||||
}
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// static
|
||||
PLDHashOperator
|
||||
RuntimeService::RemoveSharedWorkerFromWindowMap(
|
||||
nsPIDOMWindow* aKey,
|
||||
nsAutoPtr<nsTArray<WorkerPrivate*> >& aData,
|
||||
void* aUserArg)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aData.get());
|
||||
MOZ_ASSERT(aUserArg);
|
||||
|
||||
auto workerPrivate = static_cast<WorkerPrivate*>(aUserArg);
|
||||
|
||||
MOZ_ASSERT(workerPrivate->IsSharedWorker());
|
||||
|
||||
if (aData->RemoveElement(workerPrivate)) {
|
||||
MOZ_ASSERT(!aData->Contains(workerPrivate), "Added worker more than once!");
|
||||
|
||||
if (aData->IsEmpty()) {
|
||||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// static
|
||||
PLDHashOperator
|
||||
RuntimeService::FindSharedWorkerInfo(const nsACString& aKey,
|
||||
SharedWorkerInfo* aData,
|
||||
void* aUserArg)
|
||||
{
|
||||
auto match = static_cast<MatchSharedWorkerInfo*>(aUserArg);
|
||||
|
||||
if (aData->mWorkerPrivate == match->mWorkerPrivate) {
|
||||
match->mSharedWorkerInfo = aData;
|
||||
return PL_DHASH_STOP;
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
@ -2437,17 +2417,7 @@ RuntimeService::ForgetSharedWorker(WorkerPrivate* aWorkerPrivate)
|
||||
|
||||
WorkerDomainInfo* domainInfo;
|
||||
if (mDomainMap.Get(aWorkerPrivate->Domain(), &domainInfo)) {
|
||||
MatchSharedWorkerInfo match(aWorkerPrivate);
|
||||
domainInfo->mSharedWorkerInfos.EnumerateRead(FindSharedWorkerInfo,
|
||||
&match);
|
||||
|
||||
if (match.mSharedWorkerInfo) {
|
||||
nsAutoCString key;
|
||||
GenerateSharedWorkerKey(match.mSharedWorkerInfo->mScriptSpec,
|
||||
match.mSharedWorkerInfo->mName,
|
||||
aWorkerPrivate->IsInPrivateBrowsing(), key);
|
||||
domainInfo->mSharedWorkerInfos.Remove(key);
|
||||
}
|
||||
RemoveSharedWorker(domainInfo, aWorkerPrivate);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,16 +75,6 @@ class RuntimeService final : public nsIObserver
|
||||
|
||||
struct IdleThreadInfo;
|
||||
|
||||
struct MatchSharedWorkerInfo
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
SharedWorkerInfo* mSharedWorkerInfo;
|
||||
|
||||
explicit MatchSharedWorkerInfo(WorkerPrivate* aWorkerPrivate)
|
||||
: mWorkerPrivate(aWorkerPrivate), mSharedWorkerInfo(nullptr)
|
||||
{ }
|
||||
};
|
||||
|
||||
mozilla::Mutex mMutex;
|
||||
|
||||
// Protected by mMutex.
|
||||
@ -139,6 +129,10 @@ public:
|
||||
void
|
||||
UnregisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
void
|
||||
RemoveSharedWorker(WorkerDomainInfo* aDomainInfo,
|
||||
WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
void
|
||||
CancelWorkersForWindow(nsPIDOMWindow* aWindow);
|
||||
|
||||
@ -268,20 +262,8 @@ private:
|
||||
void
|
||||
Cleanup();
|
||||
|
||||
static PLDHashOperator
|
||||
AddAllTopLevelWorkersToArray(const nsACString& aKey,
|
||||
WorkerDomainInfo* aData,
|
||||
void* aUserArg);
|
||||
|
||||
static PLDHashOperator
|
||||
RemoveSharedWorkerFromWindowMap(nsPIDOMWindow* aKey,
|
||||
nsAutoPtr<nsTArray<WorkerPrivate*> >& aData,
|
||||
void* aUserArg);
|
||||
|
||||
static PLDHashOperator
|
||||
FindSharedWorkerInfo(const nsACString& aKey,
|
||||
SharedWorkerInfo* aData,
|
||||
void* aUserArg);
|
||||
void
|
||||
AddAllTopLevelWorkersToArray(nsTArray<WorkerPrivate*>& aWorkers);
|
||||
|
||||
void
|
||||
GetWorkersForWindow(nsPIDOMWindow* aWindow,
|
||||
|
@ -20,9 +20,12 @@
|
||||
#include "nsIDOMChromeWindow.h"
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "nsIWebNavigation.h"
|
||||
#include "nsIWindowMediator.h"
|
||||
#include "nsIWebProgress.h"
|
||||
#include "nsIWebProgressListener.h"
|
||||
#include "nsIWindowMediator.h"
|
||||
#include "nsIWindowWatcher.h"
|
||||
#include "nsPIWindowWatcher.h"
|
||||
#include "nsWindowWatcher.h"
|
||||
#include "nsWeakReference.h"
|
||||
|
||||
using namespace mozilla;
|
||||
@ -498,6 +501,31 @@ private:
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (XRE_IsContentProcess()) {
|
||||
// ContentProcess
|
||||
nsCOMPtr<nsIWindowWatcher> wwatch =
|
||||
do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
nsCOMPtr<nsPIWindowWatcher> pwwatch(do_QueryInterface(wwatch));
|
||||
NS_ENSURE_STATE(pwwatch);
|
||||
|
||||
nsCString spec;
|
||||
uri->GetSpec(spec);
|
||||
|
||||
nsCOMPtr<nsIDOMWindow> newWindow;
|
||||
pwwatch->OpenWindow2(nullptr,
|
||||
spec.get(),
|
||||
nullptr,
|
||||
nullptr,
|
||||
false, false, true, nullptr, nullptr,
|
||||
getter_AddRefs(newWindow));
|
||||
nsCOMPtr<nsPIDOMWindow> pwindow = do_QueryInterface(newWindow);
|
||||
pwindow.forget(aWindow);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Find the most recent browser window and open a new tab in it.
|
||||
nsCOMPtr<nsIDOMWindow> browserWindow;
|
||||
rv = wm->GetMostRecentWindow(MOZ_UTF16("navigator:browser"),
|
||||
@ -580,13 +608,6 @@ already_AddRefed<Promise>
|
||||
ServiceWorkerClients::OpenWindow(const nsAString& aUrl,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
// XXXcatalinb: This works only on non-multiprocess for now, bail if we're
|
||||
// running in a content process.
|
||||
if (XRE_IsContentProcess()) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "ServiceWorkerEvents.h"
|
||||
|
||||
#include "nsIConsoleReportCollector.h"
|
||||
#include "nsIHttpChannelInternal.h"
|
||||
#include "nsINetworkInterceptController.h"
|
||||
#include "nsIOutputStream.h"
|
||||
@ -19,6 +20,7 @@
|
||||
#include "nsSerializationHelper.h"
|
||||
#include "nsQueryObject.h"
|
||||
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/FetchEventBinding.h"
|
||||
#include "mozilla/dom/PromiseNativeHandler.h"
|
||||
@ -36,6 +38,7 @@
|
||||
#include "mozilla/dom/TypedArray.h"
|
||||
#endif
|
||||
|
||||
#include "js/Conversions.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
@ -60,6 +63,8 @@ CancelChannelRunnable::Run()
|
||||
|
||||
FetchEvent::FetchEvent(EventTarget* aOwner)
|
||||
: ExtendableEvent(aOwner)
|
||||
, mPreventDefaultLineNumber(0)
|
||||
, mPreventDefaultColumnNumber(0)
|
||||
, mIsReload(false)
|
||||
, mWaitToRespond(false)
|
||||
{
|
||||
@ -97,6 +102,42 @@ FetchEvent::Constructor(const GlobalObject& aGlobal,
|
||||
|
||||
namespace {
|
||||
|
||||
void
|
||||
AsyncLog(nsIInterceptedChannel *aInterceptedChannel,
|
||||
const nsACString& aRespondWithScriptSpec,
|
||||
uint32_t aRespondWithLineNumber, uint32_t aRespondWithColumnNumber,
|
||||
const nsACString& aMessageName, const nsTArray<nsString>& aParams)
|
||||
{
|
||||
MOZ_ASSERT(aInterceptedChannel);
|
||||
// Since the intercepted channel is kept alive and paused while handling
|
||||
// the FetchEvent, we are guaranteed the reporter is stable on the worker
|
||||
// thread.
|
||||
nsIConsoleReportCollector* reporter =
|
||||
aInterceptedChannel->GetConsoleReportCollector();
|
||||
if (reporter) {
|
||||
reporter->AddConsoleReport(nsIScriptError::errorFlag,
|
||||
NS_LITERAL_CSTRING("Service Worker Interception"),
|
||||
nsContentUtils::eDOM_PROPERTIES,
|
||||
aRespondWithScriptSpec,
|
||||
aRespondWithLineNumber,
|
||||
aRespondWithColumnNumber,
|
||||
aMessageName, aParams);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Params>
|
||||
void
|
||||
AsyncLog(nsIInterceptedChannel* aInterceptedChannel,
|
||||
const nsACString& aRespondWithScriptSpec,
|
||||
uint32_t aRespondWithLineNumber, uint32_t aRespondWithColumnNumber,
|
||||
const nsACString& aMessageName, Params... aParams)
|
||||
{
|
||||
nsTArray<nsString> paramsList(sizeof...(Params));
|
||||
StringArrayAppender::Append(paramsList, sizeof...(Params), aParams...);
|
||||
AsyncLog(aInterceptedChannel, aRespondWithScriptSpec, aRespondWithLineNumber,
|
||||
aRespondWithColumnNumber, aMessageName, paramsList);
|
||||
}
|
||||
|
||||
class FinishResponse final : public nsRunnable
|
||||
{
|
||||
nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
|
||||
@ -197,6 +238,10 @@ class RespondWithHandler final : public PromiseNativeHandler
|
||||
const DebugOnly<bool> mIsClientRequest;
|
||||
const bool mIsNavigationRequest;
|
||||
const nsCString mScriptSpec;
|
||||
const nsString mRequestURL;
|
||||
const nsCString mRespondWithScriptSpec;
|
||||
const uint32_t mRespondWithLineNumber;
|
||||
const uint32_t mRespondWithColumnNumber;
|
||||
bool mRequestWasHandled;
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
@ -204,12 +249,20 @@ public:
|
||||
RespondWithHandler(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
|
||||
RequestMode aRequestMode, bool aIsClientRequest,
|
||||
bool aIsNavigationRequest,
|
||||
const nsACString& aScriptSpec)
|
||||
const nsACString& aScriptSpec,
|
||||
const nsAString& aRequestURL,
|
||||
const nsACString& aRespondWithScriptSpec,
|
||||
uint32_t aRespondWithLineNumber,
|
||||
uint32_t aRespondWithColumnNumber)
|
||||
: mInterceptedChannel(aChannel)
|
||||
, mRequestMode(aRequestMode)
|
||||
, mIsClientRequest(aIsClientRequest)
|
||||
, mIsNavigationRequest(aIsNavigationRequest)
|
||||
, mScriptSpec(aScriptSpec)
|
||||
, mRequestURL(aRequestURL)
|
||||
, mRespondWithScriptSpec(aRespondWithScriptSpec)
|
||||
, mRespondWithLineNumber(aRespondWithLineNumber)
|
||||
, mRespondWithColumnNumber(aRespondWithColumnNumber)
|
||||
, mRequestWasHandled(false)
|
||||
{
|
||||
}
|
||||
@ -219,10 +272,20 @@ public:
|
||||
void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
|
||||
|
||||
void CancelRequest(nsresult aStatus);
|
||||
|
||||
void AsyncLog(const nsACString& aMessageName, const nsTArray<nsString>& aParams)
|
||||
{
|
||||
::AsyncLog(mInterceptedChannel, mRespondWithScriptSpec, mRespondWithLineNumber,
|
||||
mRespondWithColumnNumber, aMessageName, aParams);
|
||||
}
|
||||
|
||||
private:
|
||||
~RespondWithHandler()
|
||||
{
|
||||
if (!mRequestWasHandled) {
|
||||
::AsyncLog(mInterceptedChannel, mRespondWithScriptSpec,
|
||||
mRespondWithLineNumber, mRespondWithColumnNumber,
|
||||
NS_LITERAL_CSTRING("InterceptionFailedWithURL"), &mRequestURL);
|
||||
CancelRequest(NS_ERROR_INTERCEPTION_FAILED);
|
||||
}
|
||||
}
|
||||
@ -235,17 +298,29 @@ struct RespondWithClosure
|
||||
ChannelInfo mWorkerChannelInfo;
|
||||
const nsCString mScriptSpec;
|
||||
const nsCString mResponseURLSpec;
|
||||
const nsString mRequestURL;
|
||||
const nsCString mRespondWithScriptSpec;
|
||||
const uint32_t mRespondWithLineNumber;
|
||||
const uint32_t mRespondWithColumnNumber;
|
||||
|
||||
RespondWithClosure(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
|
||||
InternalResponse* aInternalResponse,
|
||||
const ChannelInfo& aWorkerChannelInfo,
|
||||
const nsCString& aScriptSpec,
|
||||
const nsACString& aResponseURLSpec)
|
||||
const nsACString& aResponseURLSpec,
|
||||
const nsAString& aRequestURL,
|
||||
const nsACString& aRespondWithScriptSpec,
|
||||
uint32_t aRespondWithLineNumber,
|
||||
uint32_t aRespondWithColumnNumber)
|
||||
: mInterceptedChannel(aChannel)
|
||||
, mInternalResponse(aInternalResponse)
|
||||
, mWorkerChannelInfo(aWorkerChannelInfo)
|
||||
, mScriptSpec(aScriptSpec)
|
||||
, mResponseURLSpec(aResponseURLSpec)
|
||||
, mRequestURL(aRequestURL)
|
||||
, mRespondWithScriptSpec(aRespondWithScriptSpec)
|
||||
, mRespondWithLineNumber(aRespondWithLineNumber)
|
||||
, mRespondWithColumnNumber(aRespondWithColumnNumber)
|
||||
{
|
||||
}
|
||||
};
|
||||
@ -261,6 +336,10 @@ void RespondWithCopyComplete(void* aClosure, nsresult aStatus)
|
||||
data->mScriptSpec,
|
||||
data->mResponseURLSpec);
|
||||
} else {
|
||||
AsyncLog(data->mInterceptedChannel, data->mRespondWithScriptSpec,
|
||||
data->mRespondWithLineNumber, data->mRespondWithColumnNumber,
|
||||
NS_LITERAL_CSTRING("InterceptionFailedWithURL"),
|
||||
&data->mRequestURL);
|
||||
event = new CancelChannelRunnable(data->mInterceptedChannel,
|
||||
NS_ERROR_INTERCEPTION_FAILED);
|
||||
}
|
||||
@ -270,26 +349,34 @@ void RespondWithCopyComplete(void* aClosure, nsresult aStatus)
|
||||
class MOZ_STACK_CLASS AutoCancel
|
||||
{
|
||||
RefPtr<RespondWithHandler> mOwner;
|
||||
nsresult mStatus;
|
||||
nsCString mMessageName;
|
||||
nsTArray<nsString> mParams;
|
||||
|
||||
public:
|
||||
explicit AutoCancel(RespondWithHandler* aOwner)
|
||||
AutoCancel(RespondWithHandler* aOwner, const nsString& aRequestURL)
|
||||
: mOwner(aOwner)
|
||||
, mStatus(NS_ERROR_INTERCEPTION_FAILED)
|
||||
, mMessageName(NS_LITERAL_CSTRING("InterceptionFailedWithURL"))
|
||||
{
|
||||
mParams.AppendElement(aRequestURL);
|
||||
}
|
||||
|
||||
~AutoCancel()
|
||||
{
|
||||
if (mOwner) {
|
||||
mOwner->CancelRequest(mStatus);
|
||||
mOwner->AsyncLog(mMessageName, mParams);
|
||||
mOwner->CancelRequest(NS_ERROR_INTERCEPTION_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
void SetCancelStatus(nsresult aStatus)
|
||||
template<typename... Params>
|
||||
void SetCancelMessage(const nsACString& aMessageName, Params... aParams)
|
||||
{
|
||||
MOZ_ASSERT(NS_FAILED(aStatus));
|
||||
mStatus = aStatus;
|
||||
MOZ_ASSERT(mOwner);
|
||||
MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
|
||||
MOZ_ASSERT(mParams.Length() == 1);
|
||||
mMessageName = aMessageName;
|
||||
mParams.Clear();
|
||||
StringArrayAppender::Append(mParams, sizeof...(Params), aParams...);
|
||||
}
|
||||
|
||||
void Reset()
|
||||
@ -303,7 +390,7 @@ NS_IMPL_ISUPPORTS0(RespondWithHandler)
|
||||
void
|
||||
RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
AutoCancel autoCancel(this);
|
||||
AutoCancel autoCancel(this, mRequestURL);
|
||||
|
||||
if (!aValue.isObject()) {
|
||||
NS_WARNING("FetchEvent::RespondWith was passed a promise resolved to a non-Object value");
|
||||
@ -324,7 +411,8 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
|
||||
// security implications are not a complete disaster.
|
||||
if (response->Type() == ResponseType::Opaque &&
|
||||
!worker->OpaqueInterceptionEnabled()) {
|
||||
autoCancel.SetCancelStatus(NS_ERROR_OPAQUE_INTERCEPTION_DISABLED);
|
||||
autoCancel.SetCancelMessage(
|
||||
NS_LITERAL_CSTRING("OpaqueInterceptionDisabledWithURL"), &mRequestURL);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -336,24 +424,33 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
|
||||
// "opaqueredirect".
|
||||
|
||||
if (response->Type() == ResponseType::Error) {
|
||||
autoCancel.SetCancelStatus(NS_ERROR_INTERCEPTED_ERROR_RESPONSE);
|
||||
autoCancel.SetCancelMessage(
|
||||
NS_LITERAL_CSTRING("InterceptedErrorResponseWithURL"), &mRequestURL);
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(mIsClientRequest, mRequestMode == RequestMode::Same_origin);
|
||||
|
||||
if (response->Type() == ResponseType::Opaque && mRequestMode != RequestMode::No_cors) {
|
||||
autoCancel.SetCancelStatus(NS_ERROR_BAD_OPAQUE_INTERCEPTION_REQUEST_MODE);
|
||||
uint32_t mode = static_cast<uint32_t>(mRequestMode);
|
||||
NS_ConvertASCIItoUTF16 modeString(RequestModeValues::strings[mode].value,
|
||||
RequestModeValues::strings[mode].length);
|
||||
|
||||
autoCancel.SetCancelMessage(
|
||||
NS_LITERAL_CSTRING("BadOpaqueInterceptionRequestModeWithURL"),
|
||||
&mRequestURL, &modeString);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mIsNavigationRequest && response->Type() == ResponseType::Opaqueredirect) {
|
||||
autoCancel.SetCancelStatus(NS_ERROR_BAD_OPAQUE_REDIRECT_INTERCEPTION);
|
||||
autoCancel.SetCancelMessage(
|
||||
NS_LITERAL_CSTRING("BadOpaqueRedirectInterceptionWithURL"), &mRequestURL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(response->BodyUsed())) {
|
||||
autoCancel.SetCancelStatus(NS_ERROR_INTERCEPTED_USED_RESPONSE);
|
||||
autoCancel.SetCancelMessage(
|
||||
NS_LITERAL_CSTRING("InterceptedUsedResponseWithURL"), &mRequestURL);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -369,7 +466,6 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
|
||||
if (response->Type() == ResponseType::Opaque) {
|
||||
ir->GetUnfilteredUrl(responseURL);
|
||||
if (NS_WARN_IF(responseURL.IsEmpty())) {
|
||||
autoCancel.SetCancelStatus(NS_ERROR_INTERCEPTION_FAILED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -377,7 +473,11 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
|
||||
nsAutoPtr<RespondWithClosure> closure(new RespondWithClosure(mInterceptedChannel, ir,
|
||||
worker->GetChannelInfo(),
|
||||
mScriptSpec,
|
||||
responseURL));
|
||||
responseURL,
|
||||
mRequestURL,
|
||||
mRespondWithScriptSpec,
|
||||
mRespondWithLineNumber,
|
||||
mRespondWithColumnNumber));
|
||||
nsCOMPtr<nsIInputStream> body;
|
||||
ir->GetUnfilteredBody(getter_AddRefs(body));
|
||||
// Errors and redirects may not have a body.
|
||||
@ -414,7 +514,14 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
|
||||
void
|
||||
RespondWithHandler::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
CancelRequest(NS_ERROR_REJECTED_RESPONSE_INTERCEPTION);
|
||||
nsAutoJSString rejectionString;
|
||||
if (rejectionString.init(aCx, aValue)) {
|
||||
::AsyncLog(mInterceptedChannel, mRespondWithScriptSpec, mRespondWithLineNumber,
|
||||
mRespondWithColumnNumber,
|
||||
NS_LITERAL_CSTRING("InterceptionRejectedResponseWithURL"),
|
||||
&mRequestURL, &rejectionString);
|
||||
}
|
||||
CancelRequest(NS_ERROR_INTERCEPTION_FAILED);
|
||||
}
|
||||
|
||||
void
|
||||
@ -429,24 +536,78 @@ RespondWithHandler::CancelRequest(nsresult aStatus)
|
||||
} // namespace
|
||||
|
||||
void
|
||||
FetchEvent::RespondWith(Promise& aArg, ErrorResult& aRv)
|
||||
FetchEvent::RespondWith(JSContext* aCx, Promise& aArg, ErrorResult& aRv)
|
||||
{
|
||||
if (EventPhase() == nsIDOMEvent::NONE || mWaitToRespond) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Record where respondWith() was called in the script so we can include the
|
||||
// information in any error reporting. We should be guaranteed not to get
|
||||
// a file:// string here because service workers require http/https.
|
||||
nsCString spec;
|
||||
uint32_t line = 0;
|
||||
uint32_t column = 0;
|
||||
nsJSUtils::GetCallingLocation(aCx, spec, &line, &column);
|
||||
|
||||
RefPtr<InternalRequest> ir = mRequest->GetInternalRequest();
|
||||
|
||||
nsAutoCString requestURL;
|
||||
ir->GetURL(requestURL);
|
||||
|
||||
StopImmediatePropagation();
|
||||
mWaitToRespond = true;
|
||||
RefPtr<RespondWithHandler> handler =
|
||||
new RespondWithHandler(mChannel, mRequest->Mode(), ir->IsClientRequest(),
|
||||
ir->IsNavigationRequest(), mScriptSpec);
|
||||
ir->IsNavigationRequest(), mScriptSpec,
|
||||
NS_ConvertUTF8toUTF16(requestURL),
|
||||
spec, line, column);
|
||||
aArg.AppendNativeHandler(handler);
|
||||
|
||||
WaitUntil(aArg, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
FetchEvent::PreventDefault(JSContext* aCx)
|
||||
{
|
||||
MOZ_ASSERT(aCx);
|
||||
|
||||
if (mPreventDefaultScriptSpec.IsEmpty()) {
|
||||
// Note when the FetchEvent might have been canceled by script, but don't
|
||||
// actually log the location until we are sure it matters. This is
|
||||
// determined in ServiceWorkerPrivate.cpp. We only remember the first
|
||||
// call to preventDefault() as its the most likely to have actually canceled
|
||||
// the event.
|
||||
nsJSUtils::GetCallingLocation(aCx, mPreventDefaultScriptSpec,
|
||||
&mPreventDefaultLineNumber,
|
||||
&mPreventDefaultColumnNumber);
|
||||
}
|
||||
|
||||
Event::PreventDefault(aCx);
|
||||
}
|
||||
|
||||
void
|
||||
FetchEvent::ReportCanceled()
|
||||
{
|
||||
MOZ_ASSERT(!mPreventDefaultScriptSpec.IsEmpty());
|
||||
|
||||
RefPtr<InternalRequest> ir = mRequest->GetInternalRequest();
|
||||
nsAutoCString url;
|
||||
ir->GetURL(url);
|
||||
|
||||
// The variadic template provided by StringArrayAppender requires exactly
|
||||
// an nsString.
|
||||
NS_ConvertUTF8toUTF16 requestURL(url);
|
||||
//nsString requestURL;
|
||||
//CopyUTF8toUTF16(url, requestURL);
|
||||
|
||||
::AsyncLog(mChannel.get(), mPreventDefaultScriptSpec,
|
||||
mPreventDefaultLineNumber, mPreventDefaultColumnNumber,
|
||||
NS_LITERAL_CSTRING("InterceptionCanceledWithURL"), &requestURL);
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(FetchEvent, ExtendableEvent)
|
||||
NS_IMPL_RELEASE_INHERITED(FetchEvent, ExtendableEvent)
|
||||
|
||||
|
@ -103,6 +103,9 @@ class FetchEvent final : public ExtendableEvent
|
||||
nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
|
||||
RefPtr<Request> mRequest;
|
||||
nsCString mScriptSpec;
|
||||
nsCString mPreventDefaultScriptSpec;
|
||||
uint32_t mPreventDefaultLineNumber;
|
||||
uint32_t mPreventDefaultColumnNumber;
|
||||
bool mIsReload;
|
||||
bool mWaitToRespond;
|
||||
protected:
|
||||
@ -112,7 +115,10 @@ protected:
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FetchEvent, ExtendableEvent)
|
||||
NS_FORWARD_TO_EVENT
|
||||
|
||||
// Note, we cannot use NS_FORWARD_TO_EVENT because we want a different
|
||||
// PreventDefault(JSContext*) override.
|
||||
NS_FORWARD_NSIDOMEVENT(Event::)
|
||||
|
||||
virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
|
||||
{
|
||||
@ -147,13 +153,19 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
RespondWith(Promise& aArg, ErrorResult& aRv);
|
||||
RespondWith(JSContext* aCx, Promise& aArg, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ForwardTo(const nsAString& aUrl);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Default();
|
||||
|
||||
void
|
||||
PreventDefault(JSContext* aCx) override;
|
||||
|
||||
void
|
||||
ReportCanceled();
|
||||
};
|
||||
|
||||
#ifndef MOZ_SIMPLEPUSH
|
||||
|
@ -3325,44 +3325,6 @@ ServiceWorkerManager::SoftUpdate(const nsACString& aScopeKey,
|
||||
|
||||
namespace {
|
||||
|
||||
class MOZ_STACK_CLASS FilterRegistrationData
|
||||
{
|
||||
public:
|
||||
FilterRegistrationData(nsTArray<ServiceWorkerClientInfo>& aDocuments,
|
||||
ServiceWorkerRegistrationInfo* aRegistration)
|
||||
: mDocuments(aDocuments),
|
||||
mRegistration(aRegistration)
|
||||
{
|
||||
}
|
||||
|
||||
nsTArray<ServiceWorkerClientInfo>& mDocuments;
|
||||
RefPtr<ServiceWorkerRegistrationInfo> mRegistration;
|
||||
};
|
||||
|
||||
static PLDHashOperator
|
||||
EnumControlledDocuments(nsISupports* aKey,
|
||||
ServiceWorkerRegistrationInfo* aRegistration,
|
||||
void* aData)
|
||||
{
|
||||
FilterRegistrationData* data = static_cast<FilterRegistrationData*>(aData);
|
||||
MOZ_ASSERT(data->mRegistration);
|
||||
MOZ_ASSERT(aRegistration);
|
||||
if (!data->mRegistration->mScope.Equals(aRegistration->mScope)) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> document = do_QueryInterface(aKey);
|
||||
|
||||
if (!document || !document->GetWindow()) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
ServiceWorkerClientInfo clientInfo(document);
|
||||
data->mDocuments.AppendElement(clientInfo);
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
static void
|
||||
FireControllerChangeOnDocument(nsIDocument* aDocument)
|
||||
{
|
||||
@ -3388,28 +3350,6 @@ FireControllerChangeOnDocument(nsIDocument* aDocument)
|
||||
}
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
FireControllerChangeOnMatchingDocument(nsISupports* aKey,
|
||||
ServiceWorkerRegistrationInfo* aValue,
|
||||
void* aData)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
ServiceWorkerRegistrationInfo* contextReg = static_cast<ServiceWorkerRegistrationInfo*>(aData);
|
||||
if (aValue != contextReg) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = do_QueryInterface(aKey);
|
||||
if (NS_WARN_IF(!doc)) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
FireControllerChangeOnDocument(doc);
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void
|
||||
@ -3427,9 +3367,22 @@ ServiceWorkerManager::GetAllClients(nsIPrincipal* aPrincipal,
|
||||
return;
|
||||
}
|
||||
|
||||
FilterRegistrationData data(aControlledDocuments, registration);
|
||||
for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
|
||||
ServiceWorkerRegistrationInfo* thisRegistration = iter.UserData();
|
||||
MOZ_ASSERT(thisRegistration);
|
||||
if (!registration->mScope.Equals(thisRegistration->mScope)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mControlledDocuments.EnumerateRead(EnumControlledDocuments, &data);
|
||||
nsCOMPtr<nsIDocument> document = do_QueryInterface(iter.Key());
|
||||
|
||||
if (!document || !document->GetWindow()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ServiceWorkerClientInfo clientInfo(document);
|
||||
aControlledDocuments.AppendElement(clientInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -3517,7 +3470,19 @@ ServiceWorkerManager::SetSkipWaitingFlag(nsIPrincipal* aPrincipal,
|
||||
void
|
||||
ServiceWorkerManager::FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration)
|
||||
{
|
||||
mControlledDocuments.EnumerateRead(FireControllerChangeOnMatchingDocument, aRegistration);
|
||||
AssertIsOnMainThread();
|
||||
for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
|
||||
if (iter.UserData() != aRegistration) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
|
||||
if (NS_WARN_IF(!doc)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FireControllerChangeOnDocument(doc);
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<ServiceWorkerRegistrationInfo>
|
||||
@ -3687,171 +3652,6 @@ HasRootDomain(nsIURI* aURI, const nsACString& aDomain)
|
||||
return prevChar == '.';
|
||||
}
|
||||
|
||||
struct UnregisterIfMatchesUserData final
|
||||
{
|
||||
UnregisterIfMatchesUserData(
|
||||
ServiceWorkerManager::RegistrationDataPerPrincipal* aRegistrationData,
|
||||
void* aUserData)
|
||||
: mRegistrationData(aRegistrationData)
|
||||
, mUserData(aUserData)
|
||||
{}
|
||||
|
||||
ServiceWorkerManager::RegistrationDataPerPrincipal* mRegistrationData;
|
||||
void *mUserData;
|
||||
};
|
||||
|
||||
// If host/aData is null, unconditionally unregisters.
|
||||
PLDHashOperator
|
||||
UnregisterIfMatchesHost(const nsACString& aScope,
|
||||
ServiceWorkerRegistrationInfo* aReg,
|
||||
void* aPtr)
|
||||
{
|
||||
UnregisterIfMatchesUserData* data =
|
||||
static_cast<UnregisterIfMatchesUserData*>(aPtr);
|
||||
|
||||
// We avoid setting toRemove = aReg by default since there is a possibility
|
||||
// of failure when data->mUserData is passed, in which case we don't want to
|
||||
// remove the registration.
|
||||
ServiceWorkerRegistrationInfo* toRemove = nullptr;
|
||||
if (data->mUserData) {
|
||||
const nsACString& domain = *static_cast<nsACString*>(data->mUserData);
|
||||
nsCOMPtr<nsIURI> scopeURI;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
|
||||
// This way subdomains are also cleared.
|
||||
if (NS_SUCCEEDED(rv) && HasRootDomain(scopeURI, domain)) {
|
||||
toRemove = aReg;
|
||||
}
|
||||
} else {
|
||||
toRemove = aReg;
|
||||
}
|
||||
|
||||
if (toRemove) {
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
swm->ForceUnregister(data->mRegistrationData, toRemove);
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// If host/aData is null, unconditionally unregisters.
|
||||
PLDHashOperator
|
||||
UnregisterIfMatchesHostPerPrincipal(const nsACString& aKey,
|
||||
ServiceWorkerManager::RegistrationDataPerPrincipal* aData,
|
||||
void* aUserData)
|
||||
{
|
||||
UnregisterIfMatchesUserData data(aData, aUserData);
|
||||
aData->mInfos.EnumerateRead(UnregisterIfMatchesHost, &data);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
UnregisterIfMatchesClearOriginParams(const nsACString& aScope,
|
||||
ServiceWorkerRegistrationInfo* aReg,
|
||||
void* aPtr)
|
||||
{
|
||||
UnregisterIfMatchesUserData* data =
|
||||
static_cast<UnregisterIfMatchesUserData*>(aPtr);
|
||||
MOZ_ASSERT(data);
|
||||
|
||||
if (data->mUserData) {
|
||||
OriginAttributes* params = static_cast<OriginAttributes*>(data->mUserData);
|
||||
MOZ_ASSERT(params);
|
||||
MOZ_ASSERT(aReg);
|
||||
MOZ_ASSERT(aReg->mPrincipal);
|
||||
|
||||
bool equals = false;
|
||||
|
||||
if (params->mInBrowser) {
|
||||
// When we do a system wide "clear cookies and stored data" on B2G we get
|
||||
// the "clear-origin-data" notification with the System app appID and
|
||||
// the browserOnly flag set to true.
|
||||
// Web sites registering a service worker on B2G have a principal with the
|
||||
// following information: web site origin + System app appId + inBrowser=1
|
||||
// So we need to check if the service worker registration info contains
|
||||
// the System app appID and the enabled inBrowser flag and in that case
|
||||
// remove it from the registry.
|
||||
OriginAttributes attrs =
|
||||
mozilla::BasePrincipal::Cast(aReg->mPrincipal)->OriginAttributesRef();
|
||||
equals = attrs == *params;
|
||||
} else {
|
||||
// If we get the "clear-origin-data" notification because of an app
|
||||
// uninstallation, we need to check the full principal to get the match
|
||||
// in the service workers registry. If we find a match, we unregister the
|
||||
// worker.
|
||||
nsCOMPtr<nsIAppsService> appsService =
|
||||
do_GetService(APPS_SERVICE_CONTRACTID);
|
||||
if (NS_WARN_IF(!appsService)) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsCOMPtr<mozIApplication> app;
|
||||
appsService->GetAppByLocalId(params->mAppId, getter_AddRefs(app));
|
||||
if (NS_WARN_IF(!app)) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
app->GetPrincipal(getter_AddRefs(principal));
|
||||
if (NS_WARN_IF(!principal)) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
aReg->mPrincipal->Equals(principal, &equals);
|
||||
}
|
||||
|
||||
if (equals) {
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
swm->ForceUnregister(data->mRegistrationData, aReg);
|
||||
}
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
UnregisterIfMatchesClearOriginParams(const nsACString& aKey,
|
||||
ServiceWorkerManager::RegistrationDataPerPrincipal* aData,
|
||||
void* aUserData)
|
||||
{
|
||||
UnregisterIfMatchesUserData data(aData, aUserData);
|
||||
// We can use EnumerateRead because ForceUnregister (and Unregister) are async.
|
||||
// Otherwise doing some R/W operations on an hashtable during an EnumerateRead
|
||||
// will crash.
|
||||
aData->mInfos.EnumerateRead(UnregisterIfMatchesClearOriginParams,
|
||||
&data);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
GetAllRegistrationsEnumerator(const nsACString& aScope,
|
||||
ServiceWorkerRegistrationInfo* aReg,
|
||||
void* aData)
|
||||
{
|
||||
nsIMutableArray* array = static_cast<nsIMutableArray*>(aData);
|
||||
MOZ_ASSERT(aReg);
|
||||
|
||||
if (aReg->mPendingUninstall) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIServiceWorkerInfo> info = ServiceWorkerDataInfo::Create(aReg);
|
||||
if (NS_WARN_IF(!info)) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
array->AppendElement(info, false);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
GetAllRegistrationsPerPrincipalEnumerator(const nsACString& aKey,
|
||||
ServiceWorkerManager::RegistrationDataPerPrincipal* aData,
|
||||
void* aUserData)
|
||||
{
|
||||
aData->mInfos.EnumerateRead(GetAllRegistrationsEnumerator, aUserData);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NS_IMPL_ISUPPORTS(ServiceWorkerDataInfo, nsIServiceWorkerInfo)
|
||||
@ -3939,12 +3739,29 @@ ServiceWorkerManager::GetAllRegistrations(nsIArray** aResult)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
mRegistrationInfos.EnumerateRead(GetAllRegistrationsPerPrincipalEnumerator, array);
|
||||
for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
|
||||
for (auto it2 = it1.UserData()->mInfos.Iter(); !it2.Done(); it2.Next()) {
|
||||
ServiceWorkerRegistrationInfo* reg = it2.UserData();
|
||||
MOZ_ASSERT(reg);
|
||||
|
||||
if (reg->mPendingUninstall) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIServiceWorkerInfo> info = ServiceWorkerDataInfo::Create(reg);
|
||||
if (NS_WARN_IF(!info)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
array->AppendElement(info, false);
|
||||
}
|
||||
}
|
||||
|
||||
array.forget(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// MUST ONLY BE CALLED FROM UnregisterIfMatchesHost!
|
||||
// MUST ONLY BE CALLED FROM Remove(), RemoveAll() and RemoveAllRegistrations()!
|
||||
void
|
||||
ServiceWorkerManager::ForceUnregister(RegistrationDataPerPrincipal* aRegistrationData,
|
||||
ServiceWorkerRegistrationInfo* aRegistration)
|
||||
@ -3983,8 +3800,20 @@ ServiceWorkerManager::Remove(const nsACString& aHost)
|
||||
return;
|
||||
}
|
||||
|
||||
mRegistrationInfos.EnumerateRead(UnregisterIfMatchesHostPerPrincipal,
|
||||
&const_cast<nsACString&>(aHost));
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
|
||||
ServiceWorkerManager::RegistrationDataPerPrincipal* data = it1.UserData();
|
||||
for (auto it2 = data->mInfos.Iter(); !it2.Done(); it2.Next()) {
|
||||
ServiceWorkerRegistrationInfo* reg = it2.UserData();
|
||||
nsCOMPtr<nsIURI> scopeURI;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), it2.Key(),
|
||||
nullptr, nullptr);
|
||||
// This way subdomains are also cleared.
|
||||
if (NS_SUCCEEDED(rv) && HasRootDomain(scopeURI, aHost)) {
|
||||
swm->ForceUnregister(data, reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -4005,7 +3834,15 @@ void
|
||||
ServiceWorkerManager::RemoveAll()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mRegistrationInfos.EnumerateRead(UnregisterIfMatchesHostPerPrincipal, nullptr);
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
|
||||
ServiceWorkerManager::RegistrationDataPerPrincipal* data = it1.UserData();
|
||||
for (auto it2 = data->mInfos.Iter(); !it2.Done(); it2.Next()) {
|
||||
ServiceWorkerRegistrationInfo* reg = it2.UserData();
|
||||
swm->ForceUnregister(data, reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -4023,17 +3860,6 @@ ServiceWorkerManager::PropagateRemoveAll()
|
||||
mActor->SendPropagateRemoveAll();
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
UpdateEachRegistration(const nsACString& aKey,
|
||||
ServiceWorkerRegistrationInfo* aInfo,
|
||||
void* aUserArg) {
|
||||
auto This = static_cast<ServiceWorkerManager*>(aUserArg);
|
||||
MOZ_ASSERT(!aInfo->mScope.IsEmpty());
|
||||
|
||||
This->SoftUpdate(aInfo->mPrincipal, aInfo->mScope);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerManager::RemoveAllRegistrations(OriginAttributes* aParams)
|
||||
{
|
||||
@ -4041,16 +3867,63 @@ ServiceWorkerManager::RemoveAllRegistrations(OriginAttributes* aParams)
|
||||
|
||||
MOZ_ASSERT(aParams);
|
||||
|
||||
mRegistrationInfos.EnumerateRead(UnregisterIfMatchesClearOriginParams,
|
||||
aParams);
|
||||
for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
|
||||
ServiceWorkerManager::RegistrationDataPerPrincipal* data = it1.UserData();
|
||||
|
||||
// We can use iteration because ForceUnregister (and Unregister) are
|
||||
// async. Otherwise doing some R/W operations on an hashtable during
|
||||
// iteration will crash.
|
||||
for (auto it2 = data->mInfos.Iter(); !it2.Done(); it2.Next()) {
|
||||
ServiceWorkerRegistrationInfo* reg = it2.UserData();
|
||||
|
||||
MOZ_ASSERT(reg);
|
||||
MOZ_ASSERT(reg->mPrincipal);
|
||||
|
||||
bool equals = false;
|
||||
|
||||
if (aParams->mInBrowser) {
|
||||
// When we do a system wide "clear cookies and stored data" on B2G we
|
||||
// get the "clear-origin-data" notification with the System app appID
|
||||
// and the browserOnly flag set to true. Web sites registering a
|
||||
// service worker on B2G have a principal with the following
|
||||
// information: web site origin + System app appId + inBrowser=1 So
|
||||
// we need to check if the service worker registration info contains
|
||||
// the System app appID and the enabled inBrowser flag and in that
|
||||
// case remove it from the registry.
|
||||
OriginAttributes attrs =
|
||||
mozilla::BasePrincipal::Cast(reg->mPrincipal)->OriginAttributesRef();
|
||||
equals = attrs == *aParams;
|
||||
} else {
|
||||
// If we get the "clear-origin-data" notification because of an app
|
||||
// uninstallation, we need to check the full principal to get the
|
||||
// match in the service workers registry. If we find a match, we
|
||||
// unregister the worker.
|
||||
nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
|
||||
if (NS_WARN_IF(!appsService)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
UpdateEachRegistrationPerPrincipal(const nsACString& aKey,
|
||||
ServiceWorkerManager::RegistrationDataPerPrincipal* aData,
|
||||
void* aUserArg) {
|
||||
aData->mInfos.EnumerateRead(UpdateEachRegistration, aUserArg);
|
||||
return PL_DHASH_NEXT;
|
||||
nsCOMPtr<mozIApplication> app;
|
||||
appsService->GetAppByLocalId(aParams->mAppId, getter_AddRefs(app));
|
||||
if (NS_WARN_IF(!app)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
app->GetPrincipal(getter_AddRefs(principal));
|
||||
if (NS_WARN_IF(!principal)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
reg->mPrincipal->Equals(principal, &equals);
|
||||
}
|
||||
|
||||
if (equals) {
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
swm->ForceUnregister(data, reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -4058,7 +3931,14 @@ ServiceWorkerManager::UpdateAllRegistrations()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
mRegistrationInfos.EnumerateRead(UpdateEachRegistrationPerPrincipal, this);
|
||||
for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
|
||||
for (auto it2 = it1.UserData()->mInfos.Iter(); !it2.Done(); it2.Next()) {
|
||||
ServiceWorkerRegistrationInfo* info = it2.UserData();
|
||||
MOZ_ASSERT(!info->mScope.IsEmpty());
|
||||
|
||||
SoftUpdate(info->mPrincipal, info->mScope);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1167,7 +1167,9 @@ private:
|
||||
if (NS_WARN_IF(NS_FAILED(rv2)) || !event->WaitToRespond()) {
|
||||
nsCOMPtr<nsIRunnable> runnable;
|
||||
if (event->DefaultPrevented(aCx)) {
|
||||
runnable = new CancelChannelRunnable(mInterceptedChannel, NS_ERROR_INTERCEPTION_CANCELED);
|
||||
event->ReportCanceled();
|
||||
runnable = new CancelChannelRunnable(mInterceptedChannel,
|
||||
NS_ERROR_INTERCEPTION_FAILED);
|
||||
} else {
|
||||
runnable = new ResumeRequest(mInterceptedChannel);
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ parent:
|
||||
PPrintProgressDialog printProgressDialog,
|
||||
bool isForPrinting)
|
||||
returns(bool notifyOnOpen,
|
||||
bool success);
|
||||
nsresult rv);
|
||||
|
||||
async ShowPrintDialog(PPrintSettingsDialog dialog,
|
||||
PBrowser browser,
|
||||
|
@ -30,9 +30,10 @@ PrintingParent::RecvShowProgress(PBrowserParent* parent,
|
||||
PPrintProgressDialogParent* printProgressDialog,
|
||||
const bool& isForPrinting,
|
||||
bool* notifyOnOpen,
|
||||
bool* success)
|
||||
nsresult* result)
|
||||
{
|
||||
*success = false;
|
||||
*result = NS_ERROR_FAILURE;
|
||||
*notifyOnOpen = false;
|
||||
|
||||
nsCOMPtr<nsIDOMWindow> parentWin = DOMWindowFromBrowserParent(parent);
|
||||
if (!parentWin) {
|
||||
@ -52,17 +53,16 @@ PrintingParent::RecvShowProgress(PBrowserParent* parent,
|
||||
nsCOMPtr<nsIWebProgressListener> printProgressListener;
|
||||
nsCOMPtr<nsIPrintProgressParams> printProgressParams;
|
||||
|
||||
nsresult rv = pps->ShowProgress(parentWin, nullptr, nullptr, observer,
|
||||
*result = pps->ShowProgress(parentWin, nullptr, nullptr, observer,
|
||||
isForPrinting,
|
||||
getter_AddRefs(printProgressListener),
|
||||
getter_AddRefs(printProgressParams),
|
||||
notifyOnOpen);
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
NS_ENSURE_SUCCESS(*result, true);
|
||||
|
||||
dialogParent->SetWebProgressListener(printProgressListener);
|
||||
dialogParent->SetPrintProgressParams(printProgressParams);
|
||||
|
||||
*success = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ public:
|
||||
PPrintProgressDialogParent* printProgressDialog,
|
||||
const bool& isForPrinting,
|
||||
bool* notifyOnOpen,
|
||||
bool* success);
|
||||
nsresult* result);
|
||||
virtual bool
|
||||
RecvShowPrintDialog(PPrintSettingsDialogParent* aDialog,
|
||||
PBrowserParent* aParent,
|
||||
|
@ -149,10 +149,12 @@ nsPrintingProxy::ShowProgress(nsIDOMWindow* parent,
|
||||
|
||||
SendPPrintProgressDialogConstructor(dialogChild);
|
||||
|
||||
bool success = false;
|
||||
|
||||
mozilla::unused << SendShowProgress(pBrowser, dialogChild,
|
||||
isForPrinting, notifyOnOpen, &success);
|
||||
isForPrinting, notifyOnOpen, &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_ADDREF(*webProgressListener = dialogChild);
|
||||
NS_ADDREF(*printProgressParams = dialogChild);
|
||||
|
||||
|
@ -545,8 +545,8 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow* aParent,
|
||||
|
||||
// no extant window? make a new one.
|
||||
|
||||
// If no parent, consider it chrome.
|
||||
bool hasChromeParent = true;
|
||||
// If no parent, consider it chrome when running in the parent process.
|
||||
bool hasChromeParent = XRE_IsContentProcess() ? false : true;
|
||||
if (aParent) {
|
||||
// Check if the parent document has chrome privileges.
|
||||
nsIDocument* doc = parentWindow->GetDoc();
|
||||
@ -613,7 +613,9 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow* aParent,
|
||||
// based on whether the docshell type is chrome or content.
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> parentGlobalObject = do_QueryInterface(aParent);
|
||||
if (NS_WARN_IF(!jsapiChromeGuard.Init(parentGlobalObject))) {
|
||||
if (!aParent) {
|
||||
jsapiChromeGuard.Init();
|
||||
} else if (NS_WARN_IF(!jsapiChromeGuard.Init(parentGlobalObject))) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
}
|
||||
@ -645,10 +647,16 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow* aParent,
|
||||
!(chromeFlags & (nsIWebBrowserChrome::CHROME_MODAL |
|
||||
nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
|
||||
nsIWebBrowserChrome::CHROME_OPENAS_CHROME))) {
|
||||
nsCOMPtr<nsIWindowProvider> provider = do_GetInterface(parentTreeOwner);
|
||||
if (provider) {
|
||||
NS_ASSERTION(aParent, "We've _got_ to have a parent here!");
|
||||
nsCOMPtr<nsIWindowProvider> provider;
|
||||
if (parentTreeOwner) {
|
||||
provider = do_GetInterface(parentTreeOwner);
|
||||
} else if (XRE_IsContentProcess()) {
|
||||
// we're in a content process but we don't have a tabchild we can
|
||||
// use.
|
||||
provider = nsContentUtils::GetWindowProviderForContentProcess();
|
||||
}
|
||||
|
||||
if (provider) {
|
||||
nsCOMPtr<nsIDOMWindow> newWindow;
|
||||
rv = provider->ProvideWindow(aParent, chromeFlags, aCalledFromJS,
|
||||
sizeSpec.PositionSpecified(),
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "GLScreenBuffer.h"
|
||||
|
||||
#include "gfxCrashReporterUtils.h"
|
||||
#include "gfxEnv.h"
|
||||
#include "gfxUtils.h"
|
||||
#include "GLContextProvider.h"
|
||||
#include "GLTextureImage.h"
|
||||
@ -637,16 +638,16 @@ GLContext::InitWithPrefix(const char *prefix, bool trygl)
|
||||
|
||||
|
||||
#ifdef MOZ_GL_DEBUG
|
||||
if (PR_GetEnv("MOZ_GL_DEBUG"))
|
||||
if (gfxEnv::GlDebug())
|
||||
sDebugMode |= DebugEnabled;
|
||||
|
||||
// enables extra verbose output, informing of the start and finish of every GL call.
|
||||
// useful e.g. to record information to investigate graphics system crashes/lockups
|
||||
if (PR_GetEnv("MOZ_GL_DEBUG_VERBOSE"))
|
||||
if (gfxEnv::GlDebugVerbose())
|
||||
sDebugMode |= DebugTrace;
|
||||
|
||||
// aborts on GL error. Can be useful to debug quicker code that is known not to generate any GL error in principle.
|
||||
if (PR_GetEnv("MOZ_GL_DEBUG_ABORT_ON_ERROR"))
|
||||
if (gfxEnv::GlDebugAbortOnError())
|
||||
sDebugMode |= DebugAbortOnError;
|
||||
#endif
|
||||
|
||||
@ -2582,8 +2583,7 @@ GLContext::FlushIfHeavyGLCallsSinceLastFlush()
|
||||
/*static*/ bool
|
||||
GLContext::ShouldDumpExts()
|
||||
{
|
||||
static bool ret = PR_GetEnv("MOZ_GL_DUMP_EXTS");
|
||||
return ret;
|
||||
return gfxEnv::GlDumpExtensions();
|
||||
}
|
||||
|
||||
bool
|
||||
@ -2613,8 +2613,7 @@ DoesStringMatch(const char* aString, const char *aWantedString)
|
||||
/*static*/ bool
|
||||
GLContext::ShouldSpew()
|
||||
{
|
||||
static bool ret = PR_GetEnv("MOZ_GL_SPEW");
|
||||
return ret;
|
||||
return gfxEnv::GlSpew();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "GLXLibrary.h"
|
||||
#include "gfxXlibSurface.h"
|
||||
#include "gfxContext.h"
|
||||
#include "gfxEnv.h"
|
||||
#include "gfxPlatform.h"
|
||||
#include "GLContextGLX.h"
|
||||
#include "gfxUtils.h"
|
||||
@ -98,7 +99,7 @@ GLXLibrary::EnsureInitialized()
|
||||
reporter.SetSuccessful();
|
||||
}
|
||||
|
||||
if (PR_GetEnv("MOZ_GLX_DEBUG")) {
|
||||
if (gfxEnv::GlxDebug()) {
|
||||
mDebug = true;
|
||||
}
|
||||
|
||||
@ -1283,16 +1284,8 @@ GLContextProviderGLX::CreateOffscreen(const IntSize& size,
|
||||
/*static*/ GLContext*
|
||||
GLContextProviderGLX::GetGlobalContext()
|
||||
{
|
||||
static bool checkedContextSharing = false;
|
||||
static bool useContextSharing = false;
|
||||
|
||||
if (!checkedContextSharing) {
|
||||
useContextSharing = getenv("MOZ_DISABLE_CONTEXT_SHARING_GLX") == 0;
|
||||
checkedContextSharing = true;
|
||||
}
|
||||
|
||||
// TODO: get GLX context sharing to work well with multiple threads
|
||||
if (!useContextSharing) {
|
||||
if (gfxEnv::DisableContextSharingGlx()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -111,7 +111,13 @@ Compositor::DrawDiagnosticsInternal(DiagnosticFlags aFlags,
|
||||
color = gfx::Color(0.0f, 1.0f, 1.0f, 1.0f); // greenish blue
|
||||
}
|
||||
} else if (aFlags & DiagnosticFlags::IMAGE) {
|
||||
if (aFlags & DiagnosticFlags::NV12) {
|
||||
color = gfx::Color(1.0f, 1.0f, 0.0f, 1.0f); // yellow
|
||||
} else if (aFlags & DiagnosticFlags::YCBCR) {
|
||||
color = gfx::Color(1.0f, 0.55f, 0.0f, 1.0f); // orange
|
||||
} else {
|
||||
color = gfx::Color(1.0f, 0.0f, 0.0f, 1.0f); // red
|
||||
}
|
||||
} else if (aFlags & DiagnosticFlags::COLOR) {
|
||||
color = gfx::Color(0.0f, 0.0f, 1.0f, 1.0f); // blue
|
||||
} else if (aFlags & DiagnosticFlags::CONTAINER) {
|
||||
|
@ -107,7 +107,9 @@ enum class DiagnosticFlags : uint16_t {
|
||||
TILE = 1 << 5,
|
||||
BIGIMAGE = 1 << 6,
|
||||
COMPONENT_ALPHA = 1 << 7,
|
||||
REGION_RECT = 1 << 8
|
||||
REGION_RECT = 1 << 8,
|
||||
NV12 = 1 << 9,
|
||||
YCBCR = 1 << 10
|
||||
};
|
||||
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(DiagnosticFlags)
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <stdlib.h> // for getenv
|
||||
#include "DirectedGraph.h" // for DirectedGraph
|
||||
#include "Layers.h" // for Layer
|
||||
#include "gfxEnv.h" // for gfxEnv
|
||||
#include "gfxLineSegment.h" // for gfxLineSegment
|
||||
#include "gfxPoint.h" // for gfxPoint
|
||||
#include "gfxQuad.h" // for gfxQuad
|
||||
@ -152,8 +153,6 @@ static LayerSortOrder CompareDepth(Layer* aOne, Layer* aTwo) {
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static bool gDumpLayerSortList = getenv("MOZ_DUMP_LAYER_SORT_LIST") != 0;
|
||||
|
||||
// #define USE_XTERM_COLORING
|
||||
#ifdef USE_XTERM_COLORING
|
||||
// List of color values, which can be added to the xterm foreground offset or
|
||||
@ -251,7 +250,7 @@ void SortLayersBy3DZOrder(nsTArray<Layer*>& aLayers)
|
||||
DirectedGraph<Layer*> graph;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (gDumpLayerSortList) {
|
||||
if (gfxEnv::DumpLayerSortList()) {
|
||||
for (uint32_t i = 0; i < nodeCount; i++) {
|
||||
if (aLayers.ElementAt(i)->GetDebugColorIndex() == 0) {
|
||||
aLayers.ElementAt(i)->SetDebugColorIndex(gColorIndex++);
|
||||
@ -280,7 +279,7 @@ void SortLayersBy3DZOrder(nsTArray<Layer*>& aLayers)
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (gDumpLayerSortList) {
|
||||
if (gfxEnv::DumpLayerSortList()) {
|
||||
fprintf(stderr, " --- Edge List: --- \n");
|
||||
DumpEdgeList(graph);
|
||||
}
|
||||
@ -347,7 +346,7 @@ void SortLayersBy3DZOrder(nsTArray<Layer*>& aLayers)
|
||||
} while (!noIncoming.IsEmpty());
|
||||
NS_ASSERTION(!graph.GetEdgeCount(), "Cycles detected!");
|
||||
#ifdef DEBUG
|
||||
if (gDumpLayerSortList) {
|
||||
if (gfxEnv::DumpLayerSortList()) {
|
||||
fprintf(stderr, " --- Layers after sorting: --- \n");
|
||||
DumpLayerList(sortedList);
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "LayersLogging.h" // for AppendToString
|
||||
#include "ReadbackLayer.h" // for ReadbackLayer
|
||||
#include "UnitTransforms.h" // for ViewAs
|
||||
#include "gfxEnv.h"
|
||||
#include "gfxPlatform.h" // for gfxPlatform
|
||||
#include "gfxPrefs.h"
|
||||
#include "gfxUtils.h" // for gfxUtils, etc
|
||||
@ -1052,6 +1053,12 @@ Layer::GetVisibleRegionRelativeToRootLayer(nsIntRegion& aResult,
|
||||
nsIntRegion siblingVisibleRegion(sibling->GetEffectiveVisibleRegion());
|
||||
// Translate the siblings region to |layer|'s origin.
|
||||
siblingVisibleRegion.MoveBy(-siblingOffset.x, -siblingOffset.y);
|
||||
// Apply the sibling's clip.
|
||||
// Layer clip rects are not affected by the layer's transform.
|
||||
Maybe<ParentLayerIntRect> clipRect = sibling->GetEffectiveClipRect();
|
||||
if (clipRect) {
|
||||
siblingVisibleRegion.AndWith(ParentLayerIntRect::ToUntyped(*clipRect));
|
||||
}
|
||||
// Subtract the sibling visible region from the visible region of |this|.
|
||||
aResult.SubOut(siblingVisibleRegion);
|
||||
}
|
||||
@ -1362,7 +1369,7 @@ ContainerLayer::DefaultComputeEffectiveTransforms(const Matrix4x4& aTransformToS
|
||||
GetForceIsolatedGroup()) {
|
||||
useIntermediateSurface = true;
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
} else if (gfxUtils::sDumpPaintingIntermediate && !Extend3DContext()) {
|
||||
} else if (gfxEnv::DumpPaintIntermediate() && !Extend3DContext()) {
|
||||
useIntermediateSurface = true;
|
||||
#endif
|
||||
} else {
|
||||
@ -1685,9 +1692,9 @@ void
|
||||
Layer::Dump(std::stringstream& aStream, const char* aPrefix, bool aDumpHtml)
|
||||
{
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
bool dumpCompositorTexture = gfxUtils::sDumpCompositorTextures && AsLayerComposite() &&
|
||||
bool dumpCompositorTexture = gfxEnv::DumpCompositorTextures() && AsLayerComposite() &&
|
||||
AsLayerComposite()->GetCompositableHost();
|
||||
bool dumpClientTexture = gfxUtils::sDumpPainting && AsShadowableLayer() &&
|
||||
bool dumpClientTexture = gfxEnv::DumpPaint() && AsShadowableLayer() &&
|
||||
AsShadowableLayer()->GetCompositableClient();
|
||||
nsCString layerId(Name());
|
||||
layerId.Append('-');
|
||||
|
@ -1099,13 +1099,13 @@ nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent,
|
||||
break;
|
||||
}
|
||||
case MOUSE_INPUT: {
|
||||
ScrollWheelInput scrollInput = aEvent.AsScrollWheelInput();
|
||||
if (!scrollInput.TransformToLocal(aTransformToApzc)) {
|
||||
MouseInput mouseInput = aEvent.AsMouseInput();
|
||||
if (!mouseInput.TransformToLocal(aTransformToApzc)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// TODO Need to implement blocks to properly handle this.
|
||||
//rv = HandleDragEvent(scrollInput, dragMetrics);
|
||||
//rv = HandleDragEvent(mouseInput, dragMetrics);
|
||||
break;
|
||||
}
|
||||
case SCROLLWHEEL_INPUT: {
|
||||
|
@ -234,7 +234,7 @@ public:
|
||||
TextureDumpMode aCompress);
|
||||
protected:
|
||||
CompositableChild* mCompositableChild;
|
||||
CompositableForwarder* mForwarder;
|
||||
RefPtr<CompositableForwarder> mForwarder;
|
||||
// Some layers may want to enforce some flags to all their textures
|
||||
// (like disallowing tiling)
|
||||
TextureFlags mTextureFlags;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user