diff -Nru firefox-esr-115.3.1esr+build1/accessible/atk/AccessibleWrap.cpp firefox-esr-115.4.0esr+build1/accessible/atk/AccessibleWrap.cpp --- firefox-esr-115.3.1esr+build1/accessible/atk/AccessibleWrap.cpp 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/atk/AccessibleWrap.cpp 2023-10-17 05:45:43.000000000 +0000 @@ -16,8 +16,8 @@ #include "RemoteAccessible.h" #include "DocAccessibleParent.h" #include "RootAccessible.h" -#include "mozilla/a11y/TableAccessibleBase.h" -#include "mozilla/a11y/TableCellAccessibleBase.h" +#include "mozilla/a11y/TableAccessible.h" +#include "mozilla/a11y/TableCellAccessible.h" #include "nsMai.h" #include "nsMaiHyperlink.h" #include "nsString.h" @@ -1442,7 +1442,7 @@ } // static -Accessible* AccessibleWrap::GetColumnHeader(TableAccessibleBase* aAccessible, +Accessible* AccessibleWrap::GetColumnHeader(TableAccessible* aAccessible, int32_t aColIdx) { if (!aAccessible) { return nullptr; @@ -1460,7 +1460,7 @@ } // otherwise get column header for the data cell at the first row. - TableCellAccessibleBase* tableCell = cell->AsTableCellBase(); + TableCellAccessible* tableCell = cell->AsTableCell(); if (!tableCell) { return nullptr; } @@ -1475,7 +1475,7 @@ } // static -Accessible* AccessibleWrap::GetRowHeader(TableAccessibleBase* aAccessible, +Accessible* AccessibleWrap::GetRowHeader(TableAccessible* aAccessible, int32_t aRowIdx) { if (!aAccessible) { return nullptr; @@ -1493,7 +1493,7 @@ } // otherwise get row header for the data cell at the first column. - TableCellAccessibleBase* tableCell = cell->AsTableCellBase(); + TableCellAccessible* tableCell = cell->AsTableCell(); if (!tableCell) { return nullptr; } diff -Nru firefox-esr-115.3.1esr+build1/accessible/atk/AccessibleWrap.h firefox-esr-115.4.0esr+build1/accessible/atk/AccessibleWrap.h --- firefox-esr-115.3.1esr+build1/accessible/atk/AccessibleWrap.h 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/atk/AccessibleWrap.h 2023-10-17 05:45:43.000000000 +0000 @@ -73,9 +73,9 @@ static void GetKeyBinding(Accessible* aAccessible, nsAString& aResult); - static Accessible* GetColumnHeader(TableAccessibleBase* aAccessible, + static Accessible* GetColumnHeader(TableAccessible* aAccessible, int32_t aColIdx); - static Accessible* GetRowHeader(TableAccessibleBase* aAccessible, + static Accessible* GetRowHeader(TableAccessible* aAccessible, int32_t aRowIdx); protected: diff -Nru firefox-esr-115.3.1esr+build1/accessible/atk/nsMaiInterfaceTableCell.cpp firefox-esr-115.4.0esr+build1/accessible/atk/nsMaiInterfaceTableCell.cpp --- firefox-esr-115.3.1esr+build1/accessible/atk/nsMaiInterfaceTableCell.cpp 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/atk/nsMaiInterfaceTableCell.cpp 2023-10-17 05:45:43.000000000 +0000 @@ -6,8 +6,8 @@ #include "InterfaceInitFuncs.h" -#include "mozilla/a11y/TableAccessibleBase.h" -#include "mozilla/a11y/TableCellAccessibleBase.h" +#include "mozilla/a11y/TableAccessible.h" +#include "mozilla/a11y/TableCellAccessible.h" #include "nsAccessibilityService.h" #include "nsMai.h" #include "RemoteAccessible.h" @@ -24,7 +24,7 @@ if (!acc) { return 0; } - return static_cast(acc->AsTableCellBase()->ColExtent()); + return static_cast(acc->AsTableCell()->ColExtent()); } static gint GetRowSpanCB(AtkTableCell* aCell) { @@ -32,7 +32,7 @@ if (!acc) { return 0; } - return static_cast(acc->AsTableCellBase()->RowExtent()); + return static_cast(acc->AsTableCell()->RowExtent()); } static gboolean GetPositionCB(AtkTableCell* aCell, gint* aRow, gint* aCol) { @@ -40,7 +40,7 @@ if (!acc) { return false; } - TableCellAccessibleBase* cell = acc->AsTableCellBase(); + TableCellAccessible* cell = acc->AsTableCell(); if (!cell) { return false; } @@ -55,7 +55,7 @@ if (!acc) { return false; } - TableCellAccessibleBase* cellAcc = acc->AsTableCellBase(); + TableCellAccessible* cellAcc = acc->AsTableCell(); if (!cellAcc) { return false; } @@ -71,7 +71,11 @@ if (!acc) { return nullptr; } - TableAccessibleBase* table = acc->AsTableCellBase()->Table(); + TableCellAccessible* cell = acc->AsTableCell(); + if (!cell) { + return nullptr; + } + TableAccessible* table = cell->Table(); if (!table) { return nullptr; } @@ -84,8 +88,12 @@ if (!acc) { return nullptr; } + TableCellAccessible* cell = acc->AsTableCell(); + if (!cell) { + return nullptr; + } AutoTArray headers; - acc->AsTableCellBase()->ColHeaderCells(&headers); + cell->ColHeaderCells(&headers); if (headers.IsEmpty()) { return nullptr; } @@ -105,8 +113,12 @@ if (!acc) { return nullptr; } + TableCellAccessible* cell = acc->AsTableCell(); + if (!cell) { + return nullptr; + } AutoTArray headers; - acc->AsTableCellBase()->RowHeaderCells(&headers); + cell->RowHeaderCells(&headers); if (headers.IsEmpty()) { return nullptr; } diff -Nru firefox-esr-115.3.1esr+build1/accessible/atk/nsMaiInterfaceTable.cpp firefox-esr-115.4.0esr+build1/accessible/atk/nsMaiInterfaceTable.cpp --- firefox-esr-115.3.1esr+build1/accessible/atk/nsMaiInterfaceTable.cpp 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/atk/nsMaiInterfaceTable.cpp 2023-10-17 05:45:43.000000000 +0000 @@ -7,7 +7,7 @@ #include "InterfaceInitFuncs.h" #include "AccessibleWrap.h" -#include "mozilla/a11y/TableAccessibleBase.h" +#include "mozilla/a11y/TableAccessible.h" #include "nsAccessibilityService.h" #include "nsMai.h" #include "RemoteAccessible.h" @@ -29,7 +29,7 @@ if (!acc) { return nullptr; } - Accessible* cell = acc->AsTableBase()->CellAt(aRowIdx, aColIdx); + Accessible* cell = acc->AsTable()->CellAt(aRowIdx, aColIdx); if (!cell) { return nullptr; } @@ -52,7 +52,7 @@ if (!acc) { return -1; } - return static_cast(acc->AsTableBase()->CellIndexAt(aRowIdx, aColIdx)); + return static_cast(acc->AsTable()->CellIndexAt(aRowIdx, aColIdx)); } static gint getColumnAtIndexCB(AtkTable* aTable, gint aIdx) { @@ -64,7 +64,7 @@ if (!acc) { return -1; } - return static_cast(acc->AsTableBase()->ColIndexAt(aIdx)); + return static_cast(acc->AsTable()->ColIndexAt(aIdx)); } static gint getRowAtIndexCB(AtkTable* aTable, gint aIdx) { @@ -76,7 +76,7 @@ if (!acc) { return -1; } - return static_cast(acc->AsTableBase()->RowIndexAt(aIdx)); + return static_cast(acc->AsTable()->RowIndexAt(aIdx)); } static gint getColumnCountCB(AtkTable* aTable) { @@ -84,7 +84,7 @@ if (!acc) { return -1; } - return static_cast(acc->AsTableBase()->ColCount()); + return static_cast(acc->AsTable()->ColCount()); } static gint getRowCountCB(AtkTable* aTable) { @@ -92,7 +92,7 @@ if (!acc) { return -1; } - return static_cast(acc->AsTableBase()->RowCount()); + return static_cast(acc->AsTable()->RowCount()); } static gint getColumnExtentAtCB(AtkTable* aTable, gint aRowIdx, gint aColIdx) { @@ -104,7 +104,7 @@ if (!acc) { return -1; } - return static_cast(acc->AsTableBase()->ColExtentAt(aRowIdx, aColIdx)); + return static_cast(acc->AsTable()->ColExtentAt(aRowIdx, aColIdx)); } static gint getRowExtentAtCB(AtkTable* aTable, gint aRowIdx, gint aColIdx) { @@ -112,7 +112,7 @@ if (!acc) { return -1; } - return static_cast(acc->AsTableBase()->RowExtentAt(aRowIdx, aColIdx)); + return static_cast(acc->AsTable()->RowExtentAt(aRowIdx, aColIdx)); } static AtkObject* getCaptionCB(AtkTable* aTable) { @@ -120,7 +120,7 @@ if (!acc) { return nullptr; } - Accessible* caption = acc->AsTableBase()->Caption(); + Accessible* caption = acc->AsTable()->Caption(); return caption ? GetWrapperFor(caption) : nullptr; } @@ -130,7 +130,7 @@ return nullptr; } nsAutoString autoStr; - acc->AsTableBase()->ColDescription(aColumn, autoStr); + acc->AsTable()->ColDescription(aColumn, autoStr); return AccessibleWrap::ReturnString(autoStr); } @@ -139,8 +139,7 @@ if (!acc) { return nullptr; } - Accessible* header = - AccessibleWrap::GetColumnHeader(acc->AsTableBase(), aColIdx); + Accessible* header = AccessibleWrap::GetColumnHeader(acc->AsTable(), aColIdx); return header ? GetWrapperFor(header) : nullptr; } @@ -150,7 +149,7 @@ return nullptr; } nsAutoString autoStr; - acc->AsTableBase()->RowDescription(aRow, autoStr); + acc->AsTable()->RowDescription(aRow, autoStr); return AccessibleWrap::ReturnString(autoStr); } @@ -159,8 +158,7 @@ if (!acc) { return nullptr; } - Accessible* header = - AccessibleWrap::GetRowHeader(acc->AsTableBase(), aRowIdx); + Accessible* header = AccessibleWrap::GetRowHeader(acc->AsTable(), aRowIdx); return header ? GetWrapperFor(header) : nullptr; } @@ -180,7 +178,7 @@ return 0; } AutoTArray cols; - acc->AsTableBase()->SelectedColIndices(&cols); + acc->AsTable()->SelectedColIndices(&cols); if (cols.IsEmpty()) return 0; @@ -201,7 +199,7 @@ return 0; } AutoTArray rows; - acc->AsTableBase()->SelectedRowIndices(&rows); + acc->AsTable()->SelectedRowIndices(&rows); gint* atkRows = g_new(gint, rows.Length()); if (!atkRows) { @@ -219,7 +217,7 @@ if (!acc) { return FALSE; } - return static_cast(acc->AsTableBase()->IsColSelected(aColIdx)); + return static_cast(acc->AsTable()->IsColSelected(aColIdx)); } static gboolean isRowSelectedCB(AtkTable* aTable, gint aRowIdx) { @@ -227,7 +225,7 @@ if (!acc) { return FALSE; } - return static_cast(acc->AsTableBase()->IsRowSelected(aRowIdx)); + return static_cast(acc->AsTable()->IsRowSelected(aRowIdx)); } static gboolean isCellSelectedCB(AtkTable* aTable, gint aRowIdx, gint aColIdx) { @@ -236,7 +234,7 @@ return FALSE; } return static_cast( - acc->AsTableBase()->IsCellSelected(aRowIdx, aColIdx)); + acc->AsTable()->IsCellSelected(aRowIdx, aColIdx)); } } diff -Nru firefox-esr-115.3.1esr+build1/accessible/base/AccGroupInfo.cpp firefox-esr-115.4.0esr+build1/accessible/base/AccGroupInfo.cpp --- firefox-esr-115.3.1esr+build1/accessible/base/AccGroupInfo.cpp 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/base/AccGroupInfo.cpp 2023-10-17 05:45:44.000000000 +0000 @@ -4,7 +4,7 @@ #include "AccGroupInfo.h" #include "mozilla/a11y/Accessible.h" -#include "mozilla/a11y/TableAccessibleBase.h" +#include "mozilla/a11y/TableAccessible.h" #include "nsAccUtils.h" #include "nsIAccessiblePivot.h" @@ -14,8 +14,6 @@ using namespace mozilla::a11y; -static bool IsGenericContainer(role aRole); -static Accessible* GetRelevantParent(const Accessible* aAcc); static role BaseRole(role aRole); // This rule finds candidate siblings for compound widget children. @@ -37,7 +35,7 @@ // Ignore generic accessibles, but keep searching through the subtree for // siblings. - if (IsGenericContainer(accRole)) { + if (aAcc->IsGeneric()) { return nsIAccessibleTraversalRule::FILTER_IGNORE; } @@ -57,7 +55,7 @@ void AccGroupInfo::Update() { mParent = nullptr; - Accessible* parent = GetRelevantParent(mItem); + Accessible* parent = mItem->GetNonGenericParent(); if (!parent) { return; } @@ -173,7 +171,7 @@ if (mRole == roles::OUTLINEITEM) { // Find the relevant grandparent of the item. Use that parent as the root // and find the previous outline item sibling within that root. - Accessible* grandParent = GetRelevantParent(parent); + Accessible* grandParent = parent->GetNonGenericParent(); MOZ_ASSERT(grandParent); Pivot pivot{grandParent}; CompoundWidgetSiblingRule parentSiblingRule{mRole}; @@ -188,7 +186,7 @@ // the parent of the item will be a group and containing item of the group is // a conceptual parent of the item. if (mRole == roles::LISTITEM || mRole == roles::OUTLINEITEM) { - Accessible* grandParent = GetRelevantParent(parent); + Accessible* grandParent = parent->GetNonGenericParent(); if (grandParent && grandParent->Role() == mRole) { mParent = grandParent; } @@ -271,7 +269,7 @@ return *val; } } - if (TableAccessibleBase* tableAcc = aContainer->AsTableBase()) { + if (TableAccessible* tableAcc = aContainer->AsTable()) { return tableAcc->RowCount(); } break; @@ -282,7 +280,7 @@ return *val; } } - if (TableAccessibleBase* tableAcc = table->AsTableBase()) { + if (TableAccessible* tableAcc = table->AsTable()) { return tableAcc->ColCount(); } } @@ -373,23 +371,6 @@ return aAccessible->GetLevel(true); } -static bool IsGenericContainer(role aRole) { - return aRole == roles::TEXT || aRole == roles::TEXT_CONTAINER || - aRole == roles::SECTION; -} - -static Accessible* GetRelevantParent(const Accessible* aAcc) { - MOZ_ASSERT(aAcc); - - // Search through ancestors until we find a relevant parent, skipping generic - // accessibles. - Accessible* parent = aAcc->Parent(); - while (parent && IsGenericContainer(parent->Role())) { - parent = parent->Parent(); - } - return parent; -} - static role BaseRole(role aRole) { if (aRole == roles::CHECK_MENU_ITEM || aRole == roles::PARENT_MENUITEM || aRole == roles::RADIO_MENU_ITEM) { diff -Nru firefox-esr-115.3.1esr+build1/accessible/base/CachedTableAccessible.cpp firefox-esr-115.4.0esr+build1/accessible/base/CachedTableAccessible.cpp --- firefox-esr-115.3.1esr+build1/accessible/base/CachedTableAccessible.cpp 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/base/CachedTableAccessible.cpp 2023-10-17 05:45:43.000000000 +0000 @@ -8,6 +8,7 @@ #include "AccIterator.h" #include "DocAccessibleParent.h" +#include "HTMLTableAccessible.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/StaticPtr.h" #include "mozilla/UniquePtr.h" @@ -15,8 +16,6 @@ #include "nsIAccessiblePivot.h" #include "Pivot.h" #include "RemoteAccessible.h" -#include "TableAccessible.h" -#include "TableCellAccessible.h" namespace mozilla::a11y { @@ -216,44 +215,14 @@ return mCells[cellIdx].Acc(mAcc); } -void CachedTableAccessible::SelectCol(uint32_t aColIdx) { - if (LocalAccessible* localAcc = mAcc->AsLocal()) { - TableAccessible* table = localAcc->AsTable(); - table->SelectCol(aColIdx); - } - // XXX Implement support for RemoteAccessible. -} - -void CachedTableAccessible::UnselectCol(uint32_t aColIdx) { - if (LocalAccessible* localAcc = mAcc->AsLocal()) { - TableAccessible* table = localAcc->AsTable(); - table->UnselectCol(aColIdx); - } - // XXX Implement support for RemoteAccessible. -} - -void CachedTableAccessible::SelectRow(uint32_t aRowIdx) { - if (LocalAccessible* localAcc = mAcc->AsLocal()) { - TableAccessible* table = localAcc->AsTable(); - table->SelectRow(aRowIdx); - } - // XXX Implement support for RemoteAccessible. -} - -void CachedTableAccessible::UnselectRow(uint32_t aRowIdx) { - if (LocalAccessible* localAcc = mAcc->AsLocal()) { - TableAccessible* table = localAcc->AsTable(); - table->UnselectRow(aRowIdx); - } - // XXX Implement support for RemoteAccessible. -} - bool CachedTableAccessible::IsProbablyLayoutTable() { if (RemoteAccessible* remoteAcc = mAcc->AsRemote()) { return remoteAcc->TableIsProbablyForLayout(); } - TableAccessible* localTable = mAcc->AsLocal()->AsTable(); - return localTable->IsProbablyLayoutTable(); + if (auto* localTable = HTMLTableAccessible::GetFrom(mAcc->AsLocal())) { + return localTable->IsProbablyLayoutTable(); + } + return false; } /* static */ @@ -261,8 +230,7 @@ Accessible* aAcc) { MOZ_ASSERT(aAcc->IsTableCell()); for (Accessible* parent = aAcc; parent; parent = parent->Parent()) { - if (auto* table = - static_cast(parent->AsTableBase())) { + if (auto* table = static_cast(parent->AsTable())) { if (auto cellIdx = table->mAccToCellIdx.Lookup(aAcc)) { return &table->mCells[*cellIdx]; } @@ -278,12 +246,11 @@ return acc; } -TableAccessibleBase* CachedTableCellAccessible::Table() const { +TableAccessible* CachedTableCellAccessible::Table() const { for (const Accessible* acc = mAcc; acc; acc = acc->Parent()) { // Since the caller has this cell, the table is already created, so it's // okay to ignore the const restriction here. - if (TableAccessibleBase* table = - const_cast(acc)->AsTableBase()) { + if (TableAccessible* table = const_cast(acc)->AsTable()) { return table; } } @@ -299,13 +266,11 @@ return *colSpan; } } - } else if (LocalAccessible* localAcc = mAcc->AsLocal()) { + } else if (auto* cell = HTMLTableCellAccessible::GetFrom(mAcc->AsLocal())) { // For HTML table cells, we must use the HTMLTableCellAccessible // GetColExtent method rather than using the DOM attributes directly. // This is because of things like rowspan="0" which depend on knowing // about thead, tbody, etc., which is info we don't have in the a11y tree. - TableCellAccessible* cell = localAcc->AsTableCell(); - MOZ_ASSERT(cell); uint32_t colExtent = cell->ColExtent(); MOZ_ASSERT(colExtent > 0); if (colExtent > 0) { @@ -324,13 +289,11 @@ return *rowSpan; } } - } else if (LocalAccessible* localAcc = mAcc->AsLocal()) { + } else if (auto* cell = HTMLTableCellAccessible::GetFrom(mAcc->AsLocal())) { // For HTML table cells, we must use the HTMLTableCellAccessible // GetRowExtent method rather than using the DOM attributes directly. // This is because of things like rowspan="0" which depend on knowing // about thead, tbody, etc., which is info we don't have in the a11y tree. - TableCellAccessible* cell = localAcc->AsTableCell(); - MOZ_ASSERT(cell); uint32_t rowExtent = cell->RowExtent(); MOZ_ASSERT(rowExtent > 0); if (rowExtent > 0) { diff -Nru firefox-esr-115.3.1esr+build1/accessible/base/CachedTableAccessible.h firefox-esr-115.4.0esr+build1/accessible/base/CachedTableAccessible.h --- firefox-esr-115.3.1esr+build1/accessible/base/CachedTableAccessible.h 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/base/CachedTableAccessible.h 2023-10-17 05:45:43.000000000 +0000 @@ -7,8 +7,8 @@ #ifndef CACHED_TABLE_ACCESSIBLE_H #define CACHED_TABLE_ACCESSIBLE_H -#include "mozilla/a11y/TableAccessibleBase.h" -#include "mozilla/a11y/TableCellAccessibleBase.h" +#include "mozilla/a11y/TableAccessible.h" +#include "mozilla/a11y/TableCellAccessible.h" #include "mozilla/UniquePtr.h" #include "nsTHashMap.h" @@ -20,11 +20,11 @@ class CachedTableAccessible; -class CachedTableCellAccessible final : public TableCellAccessibleBase { +class CachedTableCellAccessible final : public TableCellAccessible { public: static CachedTableCellAccessible* GetFrom(Accessible* aAcc); - virtual TableAccessibleBase* Table() const override; + virtual TableAccessible* Table() const override; virtual uint32_t ColIdx() const override { return static_cast(mColIdx); @@ -76,7 +76,7 @@ /** * TableAccessible implementation which builds and queries a cache. */ -class CachedTableAccessible final : public TableAccessibleBase { +class CachedTableAccessible final : public TableAccessible { public: static CachedTableAccessible* GetFrom(Accessible* aAcc); @@ -256,11 +256,6 @@ } } - virtual void SelectCol(uint32_t aColIdx) override; - virtual void SelectRow(uint32_t aRowIdx) override; - virtual void UnselectCol(uint32_t aColIdx) override; - virtual void UnselectRow(uint32_t aRowIdx) override; - virtual Accessible* AsAccessible() override { return mAcc; } virtual bool IsProbablyLayoutTable() override; diff -Nru firefox-esr-115.3.1esr+build1/accessible/base/HTMLMarkupMap.h firefox-esr-115.4.0esr+build1/accessible/base/HTMLMarkupMap.h --- firefox-esr-115.3.1esr+build1/accessible/base/HTMLMarkupMap.h 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/base/HTMLMarkupMap.h 2023-10-17 05:45:43.000000000 +0000 @@ -349,28 +349,9 @@ MARKUPMAP( table, [](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* { - if (!aElement->GetPrimaryFrame() || - aElement->GetPrimaryFrame()->AccessibleType() != eHTMLTableType) { - return new ARIAGridAccessible(aElement, aContext->Document()); - } - - // Make sure that our children are proper layout table parts - for (nsIContent* child = aElement->GetFirstChild(); child; - child = child->GetNextSibling()) { - if (child->IsAnyOfHTMLElements(nsGkAtoms::thead, nsGkAtoms::tfoot, - nsGkAtoms::tbody, nsGkAtoms::tr)) { - // These children elements need to participate in the layout table - // and need table row(group) frames. - nsIFrame* childFrame = child->GetPrimaryFrame(); - if (childFrame && (!childFrame->IsTableRowGroupFrame() && - !childFrame->IsTableRowFrame())) { - return new ARIAGridAccessible(aElement, aContext->Document()); - } - } - } - return nullptr; + return new HTMLTableAccessible(aElement, aContext->Document()); }, - 0) + roles::TABLE) MARKUPMAP(time, New_HyperText, 0, Attr(xmlroles, time), AttrFromDOM(datetime, datetime)) @@ -380,24 +361,14 @@ MARKUPMAP( td, [](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* { - if (aContext->IsTableRow() && - aContext->GetContent() == aElement->GetParent()) { - // If HTML:td element is part of its HTML:table, which has CSS - // display style other than 'table', then create a generic table - // cell accessible, because there's no underlying table layout and - // thus native HTML table cell class doesn't work. The same is - // true if the cell itself has CSS display:block;. - if (!aContext->IsHTMLTableRow() || !aElement->GetPrimaryFrame() || - aElement->GetPrimaryFrame()->AccessibleType() != - eHTMLTableCellType) { - return new ARIAGridCellAccessible(aElement, aContext->Document()); - } - if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::scope)) { - return new HTMLTableHeaderCellAccessible(aElement, - aContext->Document()); - } + if (!aContext->IsHTMLTableRow()) { + return nullptr; } - return nullptr; + if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::scope)) { + return new HTMLTableHeaderCellAccessible(aElement, + aContext->Document()); + } + return new HTMLTableCellAccessible(aElement, aContext->Document()); }, 0) @@ -406,22 +377,10 @@ MARKUPMAP( th, [](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* { - if (aContext->IsTableRow() && - aContext->GetContent() == aElement->GetParent()) { - // If HTML:th element is part of its HTML:table, which has CSS - // display style other than 'table', then create a generic table - // cell accessible, because there's no underlying table layout and - // thus native HTML table cell class doesn't work. The same is - // true if the cell itself has CSS display:block;. - if (!aContext->IsHTMLTableRow() || !aElement->GetPrimaryFrame() || - aElement->GetPrimaryFrame()->AccessibleType() != - eHTMLTableCellType) { - return new ARIAGridCellAccessible(aElement, aContext->Document()); - } - return new HTMLTableHeaderCellAccessible(aElement, - aContext->Document()); + if (!aContext->IsHTMLTableRow()) { + return nullptr; } - return nullptr; + return new HTMLTableHeaderCellAccessible(aElement, aContext->Document()); }, 0) @@ -430,37 +389,21 @@ MARKUPMAP( tr, [](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* { - // If HTML:tr element is part of its HTML:table, which has CSS - // display style other than 'table', then create a generic table row - // accessible, because there's no underlying table layout and thus - // native HTML table row class doesn't work. Refer to - // CreateAccessibleByFrameType dual logic. - LocalAccessible* table = aContext->IsTable() ? aContext : nullptr; - if (!table && aContext->LocalParent() && - aContext->LocalParent()->IsTable()) { - table = aContext->LocalParent(); - } - if (table) { - nsIContent* parentContent = aElement->GetParent(); - nsIFrame* parentFrame = parentContent->GetPrimaryFrame(); - if (!parentFrame || !parentFrame->IsTableWrapperFrame()) { - parentContent = parentContent->GetParent(); - // parentContent can be null if this tr is at the top level of a - // shadow root (with the table outside the shadow root). - parentFrame = - parentContent ? parentContent->GetPrimaryFrame() : nullptr; - if (table->GetContent() == parentContent && - ((!parentFrame || !parentFrame->IsTableWrapperFrame()) || - !aElement->GetPrimaryFrame() || - aElement->GetPrimaryFrame()->AccessibleType() != - eHTMLTableRowType)) { - return new ARIARowAccessible(aElement, aContext->Document()); - } - } + if (aContext->IsTableRow()) { + // A within a row isn't valid. + return nullptr; + } + // Check if this is within a table. We check the grandparent because + // it might be inside a rowgroup. We don't specifically check for an HTML + // table because there are cases where there is a inside a + //
such as Monorail. + if (aContext->IsTable() || + (aContext->LocalParent() && aContext->LocalParent()->IsTable())) { + return new HTMLTableRowAccessible(aElement, aContext->Document()); } return nullptr; }, - 0) + roles::ROW) MARKUPMAP( ul, diff -Nru firefox-esr-115.3.1esr+build1/accessible/base/MathMLMarkupMap.h firefox-esr-115.4.0esr+build1/accessible/base/MathMLMarkupMap.h --- firefox-esr-115.3.1esr+build1/accessible/base/MathMLMarkupMap.h 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/base/MathMLMarkupMap.h 2023-10-17 05:45:44.000000000 +0000 @@ -59,12 +59,6 @@ MARKUPMAP( mtable_, [](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* { - // If we're not a table according to layout, use a generic accessible. - if (!aElement->GetPrimaryFrame() || - aElement->GetPrimaryFrame()->AccessibleType() != eHTMLTableType) { - return new ARIAGridAccessible(aElement, aContext->Document()); - } - return new HTMLTableAccessible(aElement, aContext->Document()); }, roles::MATHML_TABLE, AttrFromDOM(align, align), @@ -80,40 +74,16 @@ MARKUPMAP( mtr_, [](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* { - if (aContext->IsHTMLTable() && aElement->GetPrimaryFrame() && - aElement->GetPrimaryFrame()->AccessibleType() == eHTMLTableRowType) { - return new HTMLTableRowAccessible(aElement, aContext->Document()); - } - - // If the mtr element in a MathML table has a display style other than - // 'table', create a generic table row accessible, since there's no - // underlying table layout. - if (aContext->IsTable()) { - return new ARIARowAccessible(aElement, aContext->Document()); - } - - return nullptr; + return new HTMLTableRowAccessible(aElement, aContext->Document()); }, roles::MATHML_TABLE_ROW) MARKUPMAP( mtd_, [](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* { - if (aContext->IsHTMLTableRow() && aElement->GetPrimaryFrame() && - aElement->GetPrimaryFrame()->AccessibleType() == eHTMLTableCellType) { - return new HTMLTableCellAccessible(aElement, aContext->Document()); - } - - // If the mtd element in a MathML table has a display style other than - // 'table', create a generic table cell accessible, since there's no - // underlying table layout. - if (aContext->IsTableRow()) { - return new ARIAGridCellAccessible(aElement, aContext->Document()); - } - - return nullptr; + return new HTMLTableCellAccessible(aElement, aContext->Document()); }, - roles::MATHML_CELL) + 0) MARKUPMAP(maction_, New_HyperText, roles::MATHML_ACTION, AttrFromDOM(actiontype_, actiontype_), diff -Nru firefox-esr-115.3.1esr+build1/accessible/base/nsAccessibilityService.cpp firefox-esr-115.4.0esr+build1/accessible/base/nsAccessibilityService.cpp --- firefox-esr-115.3.1esr+build1/accessible/base/nsAccessibilityService.cpp 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/base/nsAccessibilityService.cpp 2023-10-17 05:45:43.000000000 +0000 @@ -29,6 +29,7 @@ #include "nsDOMTokenList.h" #include "nsCRT.h" #include "nsEventShell.h" +#include "nsGkAtoms.h" #include "nsIFrameInlines.h" #include "nsServiceManagerUtils.h" #include "nsTextFormatter.h" @@ -61,6 +62,7 @@ #include "nsTreeBodyFrame.h" #include "nsTreeColumns.h" #include "nsTreeUtils.h" +#include "mozilla/a11y/AccTypes.h" #include "mozilla/ArrayUtils.h" #include "mozilla/dom/DOMStringList.h" #include "mozilla/dom/EventTarget.h" @@ -68,6 +70,7 @@ #include "mozilla/Preferences.h" #include "mozilla/PresShell.h" #include "mozilla/ProfilerMarkers.h" +#include "mozilla/RefPtr.h" #include "mozilla/Services.h" #include "XULAlertAccessible.h" @@ -97,39 +100,38 @@ //////////////////////////////////////////////////////////////////////////////// /** - * Return true if the role map entry is an ARIA table part. + * If the element has an ARIA attribute that requires a specific Accessible + * class, create and return it. Otherwise, return null. */ -static bool IsARIATablePart(const nsRoleMapEntry* aRoleMapEntry) { - return aRoleMapEntry && - (aRoleMapEntry->accTypes & (eTableCell | eTableRow | eTable)); -} - -/** - * Create and return an Accessible for the given content depending on which - * table part we think it is. - */ -static LocalAccessible* CreateARIATablePartAcc( +static LocalAccessible* MaybeCreateSpecificARIAAccessible( const nsRoleMapEntry* aRoleMapEntry, const LocalAccessible* aContext, nsIContent* aContent, DocAccessible* aDocument) { - // In case of ARIA grid or table use table-specific classes if it's not - // native table based. - if ((aRoleMapEntry->accTypes & eTableCell)) { - if (aContext->IsTableRow()) { - return new ARIAGridCellAccessible(aContent, aDocument); + if (aRoleMapEntry && aRoleMapEntry->accTypes & eTableCell) { + if (aContent->IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th) && + aContext->IsHTMLTableRow()) { + // Don't use ARIAGridCellAccessible for a valid td/th because + // HTMLTableCellAccessible can provide additional info; e.g. row/col span + // from the layout engine. + return nullptr; + } + // A cell must be in a row. + if (aContext->Role() != roles::ROW) { + return nullptr; + } + // That row must be in a table, though there may be an intervening rowgroup. + Accessible* parent = aContext->GetNonGenericParent(); + if (!parent) { + return nullptr; + } + if (!parent->IsTable() && parent->Role() == roles::GROUPING) { + parent = parent->GetNonGenericParent(); + if (!parent) { + return nullptr; + } } - } else if (aRoleMapEntry->IsOfType(eTableRow)) { - if (aContext->IsTable() || - // There can be an Accessible between a row and its table, but it - // can only be a row group or a generic container. This is - // consistent with Filters::GetRow and CachedTableAccessible's - // TablePartRule. - ((aContext->Role() == roles::GROUPING || - (aContext->IsGenericHyperText() && !aContext->ARIARoleMap())) && - aContext->LocalParent() && aContext->LocalParent()->IsTable())) { - return new ARIARowAccessible(aContent, aDocument); + if (parent->IsTable()) { + return new ARIAGridCellAccessible(aContent, aDocument); } - } else if (aRoleMapEntry->IsOfType(eTable)) { - return new ARIAGridAccessible(aContent, aDocument); } return nullptr; } @@ -181,16 +183,27 @@ * marked presentational with role="presentation", etc. MustBeAccessible causes * an Accessible to be created as if it weren't marked presentational at all; * e.g. will expose roles::TABLE and - * support TableAccessibleBase. In contrast, this function causes a generic + * support TableAccessible. In contrast, this function causes a generic * Accessible to be created; e.g.
will expose roles::TEXT_CONTAINER and will not support - * TableAccessibleBase. This is necessary in certain cases for the + * TableAccessible. This is necessary in certain cases for the * RemoteAccessible cache. */ static bool MustBeGenericAccessible(nsIContent* aContent, DocAccessible* aDocument) { + if (aContent->IsInNativeAnonymousSubtree() || aContent->IsSVGElement()) { + // We should not force create accs for anonymous content. + // This is an issue for inputs, which have an intermediate + // container with relevant overflow styling between the input + // and its internal input content. + // We should also avoid this for SVG elements (ie. ``s + // which have default overflow:hidden styling). + return false; + } nsIFrame* frame = aContent->GetPrimaryFrame(); MOZ_ASSERT(frame); + nsAutoCString overflow; + frame->Style()->GetComputedPropertyValue(eCSSProperty_overflow, overflow); // If the frame has been transformed, and the content has any children, we // should create an Accessible so that we can account for the transform when // calculating the Accessible's bounds using the parent process cache. @@ -203,7 +216,9 @@ ((aContent->HasChildren() && frame->IsTransformed()) || frame->IsStickyPositioned() || (frame->StyleDisplay()->mPosition == StylePositionProperty::Fixed && - nsLayoutUtils::IsReallyFixedPos(frame))); + nsLayoutUtils::IsReallyFixedPos(frame)) || + overflow.Equals("auto"_ns) || overflow.Equals("scroll"_ns) || + overflow.Equals("hidden"_ns)); } /** @@ -517,12 +532,14 @@ // If the content has children and its frame has a transform, create an // Accessible so that we can account for the transform when calculating // the Accessible's bounds using the parent process cache. Ditto for - // position: fixed/sticky. + // position: fixed/sticky and content with overflow styling (hidden, auto, + // scroll) if (const nsIFrame* frame = aContent->GetPrimaryFrame()) { const auto& disp = *frame->StyleDisplay(); if (disp.HasTransform(frame) || disp.mPosition == StylePositionProperty::Fixed || - disp.mPosition == StylePositionProperty::Sticky) { + disp.mPosition == StylePositionProperty::Sticky || + disp.IsScrollableOverflow()) { document->ContentInserted(aContent, aContent->GetNextSibling()); } } @@ -1058,28 +1075,25 @@ } else if (nsCoreUtils::CanCreateAccessibleWithoutFrame(content)) { // display:contents element doesn't have a frame, but retains the // semantics. All its children are unaffected. - const MarkupMapInfo* markupMap = GetMarkupMapInfoFor(content); - RefPtr newAcc; - if (markupMap && markupMap->new_func) { - newAcc = markupMap->new_func(content->AsElement(), aContext); + const nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(content->AsElement()); + RefPtr newAcc = MaybeCreateSpecificARIAAccessible( + roleMapEntry, aContext, content, document); + const MarkupMapInfo* markupMap = nullptr; + if (!newAcc) { + markupMap = GetMarkupMapInfoFor(content); + if (markupMap && markupMap->new_func) { + newAcc = markupMap->new_func(content->AsElement(), aContext); + } } // Check whether this element has an ARIA role or attribute that requires // us to create an Accessible. - const nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(content->AsElement()); const bool hasNonPresentationalARIARole = roleMapEntry && !roleMapEntry->Is(nsGkAtoms::presentation) && !roleMapEntry->Is(nsGkAtoms::none); if (!newAcc && (hasNonPresentationalARIARole || AttributesMustBeAccessible(content, document))) { - // If this element is an ARIA table part, create the proper table part - // Accessible. Otherwise, create a generic HyperTextAccessible. - if (IsARIATablePart(roleMapEntry)) { - newAcc = - CreateARIATablePartAcc(roleMapEntry, aContext, content, document); - } else { - newAcc = new HyperTextAccessibleWrap(content, document); - } + newAcc = new HyperTextAccessibleWrap(content, document); } // If there's still no Accessible but we do have an entry in the markup @@ -1214,14 +1228,14 @@ } if (!newAcc && content->IsHTMLElement()) { // HTML accessibles - const bool isARIATablePart = IsARIATablePart(roleMapEntry); + // We should always use OuterDocAccessible for OuterDocs, even if there's a + // specific ARIA class we would otherwise use. + if (frame->AccessibleType() != eOuterDocType) { + newAcc = MaybeCreateSpecificARIAAccessible(roleMapEntry, aContext, + content, document); + } - if (!isARIATablePart || frame->AccessibleType() == eHTMLTableCellType || - frame->AccessibleType() == eHTMLTableRowType || - frame->AccessibleType() == eHTMLTableType || - // We should always use OuterDocAccessible for OuterDocs, even for - // ARIA table roles. - frame->AccessibleType() == eOuterDocType) { + if (!newAcc) { // Prefer to use markup to decide if and what kind of accessible to // create, const MarkupMapInfo* markupMap = @@ -1235,15 +1249,6 @@ } } - // In case of ARIA grid or table use table-specific classes if it's not - // native table based. - if (isARIATablePart && (!newAcc || newAcc->IsGenericHyperText())) { - if (LocalAccessible* tablePartAcc = CreateARIATablePartAcc( - roleMapEntry, aContext, content, document)) { - newAcc = tablePartAcc; - } - } - // If table has strong ARIA role then all table descendants shouldn't // expose their native roles. if (!roleMapEntry && newAcc && aContext->HasStrongARIARole()) { @@ -1595,56 +1600,14 @@ newAcc = new HTMLSpinnerAccessible(aContent, document); break; case eHTMLTableType: - if (aContent->IsHTMLElement(nsGkAtoms::table)) { - newAcc = new HTMLTableAccessible(aContent, document); - } else { - newAcc = new HyperTextAccessibleWrap(aContent, document); - } - break; case eHTMLTableCellType: - // LocalAccessible HTML table cell should be a child of accessible HTML - // table or its row (CSS HTML tables are polite to the used markup at - // certain degree). - // Otherwise create a generic text accessible to avoid text jamming - // when reading by AT. - if (aContext->IsHTMLTableRow() || aContext->IsHTMLTable()) { - newAcc = new HTMLTableCellAccessible(aContent, document); - } else { - newAcc = new HyperTextAccessibleWrap(aContent, document); - } + // We handle markup and ARIA tables elsewhere. If we reach here, this is + // a CSS table part. Just create a generic text container. + newAcc = new HyperTextAccessibleWrap(aContent, document); break; - - case eHTMLTableRowType: { - // LocalAccessible HTML table row may be a child of tbody/tfoot/thead of - // accessible HTML table or a direct child of accessible of HTML table. - LocalAccessible* table = aContext->IsTable() ? aContext : nullptr; - if (!table && aContext->LocalParent() && - aContext->LocalParent()->IsTable()) { - table = aContext->LocalParent(); - } - - if (table) { - nsIContent* parentContent = - aContent->GetParentOrShadowHostNode()->AsContent(); - nsIFrame* parentFrame = nullptr; - if (parentContent) { - parentFrame = parentContent->GetPrimaryFrame(); - if (!parentFrame || !parentFrame->IsTableWrapperFrame()) { - parentContent = - parentContent->GetParentOrShadowHostNode()->AsContent(); - if (parentContent) { - parentFrame = parentContent->GetPrimaryFrame(); - } - } - } - - if (parentFrame && parentFrame->IsTableWrapperFrame() && - table->GetContent() == parentContent) { - newAcc = new HTMLTableRowAccessible(aContent, document); - } - } + case eHTMLTableRowType: + // This is a CSS table row. Don't expose it at all. break; - } case eHTMLTextFieldType: newAcc = new HTMLTextFieldAccessible(aContent, document); break; diff -Nru firefox-esr-115.3.1esr+build1/accessible/base/nsAccUtils.h firefox-esr-115.4.0esr+build1/accessible/base/nsAccUtils.h --- firefox-esr-115.3.1esr+build1/accessible/base/nsAccUtils.h 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/base/nsAccUtils.h 2023-10-17 05:45:43.000000000 +0000 @@ -106,6 +106,10 @@ static Accessible* TableFor(Accessible* aRow); static LocalAccessible* TableFor(LocalAccessible* aRow); + static const LocalAccessible* TableFor(const LocalAccessible* aAcc) { + return TableFor(const_cast(aAcc)); + } + /** * Return true if the DOM node of a given accessible has a given attribute * with a value of "true". diff -Nru firefox-esr-115.3.1esr+build1/accessible/basetypes/Accessible.h firefox-esr-115.4.0esr+build1/accessible/basetypes/Accessible.h --- firefox-esr-115.3.1esr+build1/accessible/basetypes/Accessible.h 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/basetypes/Accessible.h 2023-10-17 05:45:43.000000000 +0000 @@ -29,8 +29,8 @@ class Relation; enum class RelationType; class RemoteAccessible; -class TableAccessibleBase; -class TableCellAccessibleBase; +class TableAccessible; +class TableCellAccessible; /** * Name type flags. @@ -475,20 +475,19 @@ bool IsDoc() const { return HasGenericType(eDocument); } - /** - * Note: The eTable* types defined in the ARIA map are used in - * nsAccessibilityService::CreateAccessible to determine which ARIAGrid* - * classes to use for accessible object creation. However, an invalid table - * structure might cause these classes not to be used after all. - * - * To make sure we're really dealing with a table/row/cell, only check the - * generic type defined by the class, not the type defined in the ARIA map. - */ - bool IsTableRow() const { return mGenericTypes & eTableRow; } + bool IsTableRow() const { return HasGenericType(eTableRow); } - bool IsTableCell() const { return mGenericTypes & eTableCell; } + bool IsTableCell() const { + // The eTableCell type defined in the ARIA map is used in + // nsAccessibilityService::CreateAccessible to specify when + // ARIAGridCellAccessible should be used for object creation. However, an + // invalid table structure might cause this class not to be used after all. + // To make sure we're really dealing with a cell, only check the generic + // type defined by the class, not the type defined in the ARIA map. + return mGenericTypes & eTableCell; + } - bool IsTable() const { return mGenericTypes & eTable; } + bool IsTable() const { return HasGenericType(eTable); } bool IsHyperText() const { return HasGenericType(eHyperText); } @@ -532,6 +531,7 @@ bool IsHTMLRadioButton() const { return mType == eHTMLRadioButtonType; } bool IsHTMLTable() const { return mType == eHTMLTableType; } + bool IsHTMLTableCell() const { return mType == eHTMLTableCellType; } bool IsHTMLTableRow() const { return mType == eHTMLTableRowType; } bool IsImageMap() const { return mType == eImageMapType; } @@ -579,6 +579,28 @@ virtual bool HasNumericValue() const = 0; /** + * Returns true if this is a generic container element that has no meaning on + * its own. + */ + bool IsGeneric() const { + role accRole = Role(); + return accRole == roles::TEXT || accRole == roles::TEXT_CONTAINER || + accRole == roles::SECTION; + } + + /** + * Returns the nearest ancestor which is not a generic element. + */ + Accessible* GetNonGenericParent() const { + for (Accessible* parent = Parent(); parent; parent = parent->Parent()) { + if (!parent->IsGeneric()) { + return parent; + } + } + return nullptr; + } + + /** * Return true if the link is valid (e. g. points to a valid URL). */ bool IsLinkValid(); @@ -608,8 +630,8 @@ virtual HyperTextAccessibleBase* AsHyperTextBase() { return nullptr; } - virtual TableAccessibleBase* AsTableBase() { return nullptr; } - virtual TableCellAccessibleBase* AsTableCellBase() { return nullptr; } + virtual TableAccessible* AsTable() { return nullptr; } + virtual TableCellAccessible* AsTableCell() { return nullptr; } #ifdef A11Y_LOG /** diff -Nru firefox-esr-115.3.1esr+build1/accessible/basetypes/HyperTextAccessibleBase.cpp firefox-esr-115.4.0esr+build1/accessible/basetypes/HyperTextAccessibleBase.cpp --- firefox-esr-115.3.1esr+build1/accessible/basetypes/HyperTextAccessibleBase.cpp 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/basetypes/HyperTextAccessibleBase.cpp 2023-10-17 05:45:43.000000000 +0000 @@ -292,6 +292,19 @@ nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordType, thisAcc); if (!thisAcc->Bounds().Contains(coords.x, coords.y)) { // The requested point does not exist in this accessible. + // Check if we used fuzzy hittesting to get here and, if + // so, return 0 to indicate this text leaf is a valid match. + LayoutDeviceIntPoint p(aX, aY); + if (aCoordType != nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE) { + p = nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordType, thisAcc); + } + if (Accessible* doc = nsAccUtils::DocumentFor(thisAcc)) { + Accessible* hittestMatch = doc->ChildAtPoint( + p.x, p.y, Accessible::EWhichChildAtPoint::DeepestChild); + if (hittestMatch && thisAcc == hittestMatch->Parent()) { + return 0; + } + } return -1; } diff -Nru firefox-esr-115.3.1esr+build1/accessible/basetypes/moz.build firefox-esr-115.4.0esr+build1/accessible/basetypes/moz.build --- firefox-esr-115.3.1esr+build1/accessible/basetypes/moz.build 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/basetypes/moz.build 2023-10-17 05:45:44.000000000 +0000 @@ -7,8 +7,8 @@ EXPORTS.mozilla.a11y += [ "Accessible.h", "HyperTextAccessibleBase.h", - "TableAccessibleBase.h", - "TableCellAccessibleBase.h", + "TableAccessible.h", + "TableCellAccessible.h", ] UNIFIED_SOURCES += [ diff -Nru firefox-esr-115.3.1esr+build1/accessible/basetypes/TableAccessibleBase.h firefox-esr-115.4.0esr+build1/accessible/basetypes/TableAccessibleBase.h --- firefox-esr-115.3.1esr+build1/accessible/basetypes/TableAccessibleBase.h 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/basetypes/TableAccessibleBase.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,192 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef TABLE_ACCESSIBLE_BASE_H -#define TABLE_ACCESSIBLE_BASE_H - -#include "nsString.h" -#include "nsTArray.h" - -namespace mozilla { -namespace a11y { - -class Accessible; - -/** - * Accessible table interface. - */ -class TableAccessibleBase { - public: - /** - * Return the caption accessible if any for this table. - */ - virtual Accessible* Caption() const { return nullptr; } - - /** - * Get the summary for this table. - */ - virtual void Summary(nsString& aSummary) { aSummary.Truncate(); } - - /** - * Return the number of columns in the table. - */ - virtual uint32_t ColCount() const { return 0; } - - /** - * Return the number of rows in the table. - */ - virtual uint32_t RowCount() { return 0; } - - /** - * Return the accessible for the cell at the given row and column indices. - */ - virtual Accessible* CellAt(uint32_t aRowIdx, uint32_t aColIdx) { - return nullptr; - } - - /** - * Return the index of the cell at the given row and column. - */ - virtual int32_t CellIndexAt(uint32_t aRowIdx, uint32_t aColIdx) { return -1; } - - /** - * Return the column index of the cell with the given index. - * This returns -1 if the column count is 0 or an invalid index is being - * passed in. - */ - virtual int32_t ColIndexAt(uint32_t aCellIdx) { return -1; } - - /** - * Return the row index of the cell with the given index. - * This returns -1 if the column count is 0 or an invalid index is being - * passed in. - */ - virtual int32_t RowIndexAt(uint32_t aCellIdx) { return -1; } - - /** - * Get the row and column indices for the cell at the given index. - * This returns -1 for both output parameters if the column count is 0 or an - * invalid index is being passed in. - */ - virtual void RowAndColIndicesAt(uint32_t aCellIdx, int32_t* aRowIdx, - int32_t* aColIdx) { - *aRowIdx = -1; - *aColIdx = -1; - } - - /** - * Return the number of columns occupied by the cell at the given row and - * column indices. - */ - virtual uint32_t ColExtentAt(uint32_t aRowIdx, uint32_t aColIdx) { return 1; } - - /** - * Return the number of rows occupied by the cell at the given row and column - * indices. - */ - virtual uint32_t RowExtentAt(uint32_t aRowIdx, uint32_t aColIdx) { return 1; } - - /** - * Get the description of the given column. - */ - virtual void ColDescription(uint32_t aColIdx, nsString& aDescription) { - aDescription.Truncate(); - } - - /** - * Get the description for the given row. - */ - virtual void RowDescription(uint32_t aRowIdx, nsString& aDescription) { - aDescription.Truncate(); - } - - /** - * Return true if the given column is selected. - */ - virtual bool IsColSelected(uint32_t aColIdx) { return false; } - - /** - * Return true if the given row is selected. - */ - virtual bool IsRowSelected(uint32_t aRowIdx) { return false; } - - /** - * Return true if the given cell is selected. - */ - virtual bool IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx) { - return false; - } - - /** - * Return the number of selected cells. - */ - virtual uint32_t SelectedCellCount() { return 0; } - - /** - * Return the number of selected columns. - */ - virtual uint32_t SelectedColCount() { return 0; } - - /** - * Return the number of selected rows. - */ - virtual uint32_t SelectedRowCount() { return 0; } - - /** - * Get the set of selected cells. - */ - virtual void SelectedCells(nsTArray* aCells) {} - - /** - * Get the set of selected cell indices. - */ - virtual void SelectedCellIndices(nsTArray* aCells) {} - - /** - * Get the set of selected column indices. - */ - virtual void SelectedColIndices(nsTArray* aCols) {} - - /** - * Get the set of selected row indices. - */ - virtual void SelectedRowIndices(nsTArray* aRows) {} - - /** - * Select the given column unselecting any other selected columns. - */ - virtual void SelectCol(uint32_t aColIdx) {} - - /** - * Select the given row unselecting all other previously selected rows. - */ - virtual void SelectRow(uint32_t aRowIdx) {} - - /** - * Unselect the given column leaving other selected columns selected. - */ - virtual void UnselectCol(uint32_t aColIdx) {} - - /** - * Unselect the given row leaving other selected rows selected. - */ - virtual void UnselectRow(uint32_t aRowIdx) {} - - /** - * Return true if the table is probably for layout. - */ - virtual bool IsProbablyLayoutTable() { return false; } - - /** - * Convert the table to an Accessible*. - */ - virtual Accessible* AsAccessible() = 0; -}; - -} // namespace a11y -} // namespace mozilla - -#endif diff -Nru firefox-esr-115.3.1esr+build1/accessible/basetypes/TableAccessible.h firefox-esr-115.4.0esr+build1/accessible/basetypes/TableAccessible.h --- firefox-esr-115.3.1esr+build1/accessible/basetypes/TableAccessible.h 1970-01-01 00:00:00.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/basetypes/TableAccessible.h 2023-10-17 05:45:44.000000000 +0000 @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef TABLE_ACCESSIBLE_H +#define TABLE_ACCESSIBLE_H + +#include "nsString.h" +#include "nsTArray.h" + +namespace mozilla { +namespace a11y { + +class Accessible; + +/** + * Accessible table interface. + */ +class TableAccessible { + public: + /** + * Return the caption accessible if any for this table. + */ + virtual Accessible* Caption() const { return nullptr; } + + /** + * Get the summary for this table. + */ + virtual void Summary(nsString& aSummary) { aSummary.Truncate(); } + + /** + * Return the number of columns in the table. + */ + virtual uint32_t ColCount() const { return 0; } + + /** + * Return the number of rows in the table. + */ + virtual uint32_t RowCount() { return 0; } + + /** + * Return the accessible for the cell at the given row and column indices. + */ + virtual Accessible* CellAt(uint32_t aRowIdx, uint32_t aColIdx) { + return nullptr; + } + + /** + * Return the index of the cell at the given row and column. + */ + virtual int32_t CellIndexAt(uint32_t aRowIdx, uint32_t aColIdx) { return -1; } + + /** + * Return the column index of the cell with the given index. + * This returns -1 if the column count is 0 or an invalid index is being + * passed in. + */ + virtual int32_t ColIndexAt(uint32_t aCellIdx) { return -1; } + + /** + * Return the row index of the cell with the given index. + * This returns -1 if the column count is 0 or an invalid index is being + * passed in. + */ + virtual int32_t RowIndexAt(uint32_t aCellIdx) { return -1; } + + /** + * Get the row and column indices for the cell at the given index. + * This returns -1 for both output parameters if the column count is 0 or an + * invalid index is being passed in. + */ + virtual void RowAndColIndicesAt(uint32_t aCellIdx, int32_t* aRowIdx, + int32_t* aColIdx) { + *aRowIdx = -1; + *aColIdx = -1; + } + + /** + * Return the number of columns occupied by the cell at the given row and + * column indices. + */ + virtual uint32_t ColExtentAt(uint32_t aRowIdx, uint32_t aColIdx) { return 1; } + + /** + * Return the number of rows occupied by the cell at the given row and column + * indices. + */ + virtual uint32_t RowExtentAt(uint32_t aRowIdx, uint32_t aColIdx) { return 1; } + + /** + * Get the description of the given column. + */ + virtual void ColDescription(uint32_t aColIdx, nsString& aDescription) { + aDescription.Truncate(); + } + + /** + * Get the description for the given row. + */ + virtual void RowDescription(uint32_t aRowIdx, nsString& aDescription) { + aDescription.Truncate(); + } + + /** + * Return true if the given column is selected. + */ + virtual bool IsColSelected(uint32_t aColIdx) { return false; } + + /** + * Return true if the given row is selected. + */ + virtual bool IsRowSelected(uint32_t aRowIdx) { return false; } + + /** + * Return true if the given cell is selected. + */ + virtual bool IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx) { + return false; + } + + /** + * Return the number of selected cells. + */ + virtual uint32_t SelectedCellCount() { return 0; } + + /** + * Return the number of selected columns. + */ + virtual uint32_t SelectedColCount() { return 0; } + + /** + * Return the number of selected rows. + */ + virtual uint32_t SelectedRowCount() { return 0; } + + /** + * Get the set of selected cells. + */ + virtual void SelectedCells(nsTArray* aCells) {} + + /** + * Get the set of selected cell indices. + */ + virtual void SelectedCellIndices(nsTArray* aCells) {} + + /** + * Get the set of selected column indices. + */ + virtual void SelectedColIndices(nsTArray* aCols) {} + + /** + * Get the set of selected row indices. + */ + virtual void SelectedRowIndices(nsTArray* aRows) {} + + /** + * Return true if the table is probably for layout. + */ + virtual bool IsProbablyLayoutTable() { return false; } + + /** + * Convert the table to an Accessible*. + */ + virtual Accessible* AsAccessible() = 0; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff -Nru firefox-esr-115.3.1esr+build1/accessible/basetypes/TableCellAccessibleBase.h firefox-esr-115.4.0esr+build1/accessible/basetypes/TableCellAccessibleBase.h --- firefox-esr-115.3.1esr+build1/accessible/basetypes/TableCellAccessibleBase.h 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/basetypes/TableCellAccessibleBase.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,68 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_a11y_TableCellAccessibleBase_h__ -#define mozilla_a11y_TableCellAccessibleBase_h__ - -#include "nsTArray.h" -#include - -namespace mozilla { -namespace a11y { - -class Accessible; -class TableAccessibleBase; - -/** - * Abstract interface implemented by table cell accessibles. - */ -class TableCellAccessibleBase { - public: - /** - * Return the table this cell is in. - */ - virtual TableAccessibleBase* Table() const = 0; - - /** - * Return the column of the table this cell is in. - */ - virtual uint32_t ColIdx() const = 0; - - /** - * Return the row of the table this cell is in. - */ - virtual uint32_t RowIdx() const = 0; - - /** - * Return the column extent of this cell. - */ - virtual uint32_t ColExtent() const { return 1; } - - /** - * Return the row extent of this cell. - */ - virtual uint32_t RowExtent() const { return 1; } - - /** - * Return the column header cells for this cell. - */ - virtual void ColHeaderCells(nsTArray* aCells) = 0; - - /** - * Return the row header cells for this cell. - */ - virtual void RowHeaderCells(nsTArray* aCells) = 0; - - /** - * Returns true if this cell is selected. - */ - virtual bool Selected() = 0; -}; - -} // namespace a11y -} // namespace mozilla - -#endif // mozilla_a11y_TableCellAccessibleBase_h__ diff -Nru firefox-esr-115.3.1esr+build1/accessible/basetypes/TableCellAccessible.h firefox-esr-115.4.0esr+build1/accessible/basetypes/TableCellAccessible.h --- firefox-esr-115.3.1esr+build1/accessible/basetypes/TableCellAccessible.h 1970-01-01 00:00:00.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/basetypes/TableCellAccessible.h 2023-10-17 05:45:44.000000000 +0000 @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_a11y_TableCellAccessible_h__ +#define mozilla_a11y_TableCellAccessible_h__ + +#include "nsTArray.h" +#include + +namespace mozilla { +namespace a11y { + +class Accessible; +class TableAccessible; + +/** + * Abstract interface implemented by table cell accessibles. + */ +class TableCellAccessible { + public: + /** + * Return the table this cell is in. + */ + virtual TableAccessible* Table() const = 0; + + /** + * Return the column of the table this cell is in. + */ + virtual uint32_t ColIdx() const = 0; + + /** + * Return the row of the table this cell is in. + */ + virtual uint32_t RowIdx() const = 0; + + /** + * Return the column extent of this cell. + */ + virtual uint32_t ColExtent() const { return 1; } + + /** + * Return the row extent of this cell. + */ + virtual uint32_t RowExtent() const { return 1; } + + /** + * Return the column header cells for this cell. + */ + virtual void ColHeaderCells(nsTArray* aCells) = 0; + + /** + * Return the row header cells for this cell. + */ + virtual void RowHeaderCells(nsTArray* aCells) = 0; + + /** + * Returns true if this cell is selected. + */ + virtual bool Selected() = 0; +}; + +} // namespace a11y +} // namespace mozilla + +#endif // mozilla_a11y_TableCellAccessible_h__ diff -Nru firefox-esr-115.3.1esr+build1/accessible/generic/ARIAGridAccessible.cpp firefox-esr-115.4.0esr+build1/accessible/generic/ARIAGridAccessible.cpp --- firefox-esr-115.3.1esr+build1/accessible/generic/ARIAGridAccessible.cpp 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/generic/ARIAGridAccessible.cpp 2023-10-17 05:45:43.000000000 +0000 @@ -3,509 +3,24 @@ * 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 "ARIAGridAccessible-inl.h" +#include "ARIAGridAccessible.h" +#include #include "LocalAccessible-inl.h" #include "AccAttributes.h" #include "AccIterator.h" +#include "mozilla/a11y/TableAccessible.h" +#include "mozilla/a11y/TableCellAccessible.h" +#include "nsAccessibilityService.h" #include "nsAccUtils.h" +#include "nsGkAtoms.h" #include "Role.h" #include "States.h" -#include "mozilla/dom/Element.h" -#include "nsComponentManagerUtils.h" - using namespace mozilla; using namespace mozilla::a11y; //////////////////////////////////////////////////////////////////////////////// -// ARIAGridAccessible -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -// Constructor - -ARIAGridAccessible::ARIAGridAccessible(nsIContent* aContent, - DocAccessible* aDoc) - : HyperTextAccessibleWrap(aContent, aDoc) { - mGenericTypes |= eTable; -} - -role ARIAGridAccessible::NativeRole() const { - a11y::role r = GetAccService()->MarkupRole(mContent); - return r != roles::NOTHING ? r : roles::TABLE; -} - -already_AddRefed ARIAGridAccessible::NativeAttributes() { - RefPtr attributes = AccessibleWrap::NativeAttributes(); - - if (IsProbablyLayoutTable()) { - attributes->SetAttribute(nsGkAtoms::layout_guess, true); - } - - return attributes.forget(); -} - -//////////////////////////////////////////////////////////////////////////////// -// Table - -uint32_t ARIAGridAccessible::ColCount() const { - AccIterator rowIter(this, filters::GetRow); - LocalAccessible* row = rowIter.Next(); - if (!row) return 0; - - AccIterator cellIter(row, filters::GetCell); - LocalAccessible* cell = nullptr; - - uint32_t colCount = 0; - while ((cell = cellIter.Next())) { - MOZ_ASSERT(cell->IsTableCell(), "No table or grid cell!"); - colCount += cell->AsTableCell()->ColExtent(); - } - - return colCount; -} - -uint32_t ARIAGridAccessible::RowCount() { - uint32_t rowCount = 0; - AccIterator rowIter(this, filters::GetRow); - while (rowIter.Next()) rowCount++; - - return rowCount; -} - -LocalAccessible* ARIAGridAccessible::CellAt(uint32_t aRowIndex, - uint32_t aColumnIndex) { - LocalAccessible* row = RowAt(aRowIndex); - if (!row) return nullptr; - - return CellInRowAt(row, aColumnIndex); -} - -bool ARIAGridAccessible::IsColSelected(uint32_t aColIdx) { - if (IsARIARole(nsGkAtoms::table)) return false; - - AccIterator rowIter(this, filters::GetRow); - LocalAccessible* row = rowIter.Next(); - if (!row) return false; - - do { - if (!nsAccUtils::IsARIASelected(row)) { - LocalAccessible* cell = CellInRowAt(row, aColIdx); - if (!cell || !nsAccUtils::IsARIASelected(cell)) return false; - } - } while ((row = rowIter.Next())); - - return true; -} - -bool ARIAGridAccessible::IsRowSelected(uint32_t aRowIdx) { - if (IsARIARole(nsGkAtoms::table)) return false; - - LocalAccessible* row = RowAt(aRowIdx); - if (!row) return false; - - if (!nsAccUtils::IsARIASelected(row)) { - AccIterator cellIter(row, filters::GetCell); - LocalAccessible* cell = nullptr; - while ((cell = cellIter.Next())) { - if (!nsAccUtils::IsARIASelected(cell)) return false; - } - } - - return true; -} - -bool ARIAGridAccessible::IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx) { - if (IsARIARole(nsGkAtoms::table)) return false; - - LocalAccessible* row = RowAt(aRowIdx); - if (!row) return false; - - if (!nsAccUtils::IsARIASelected(row)) { - LocalAccessible* cell = CellInRowAt(row, aColIdx); - if (!cell || !nsAccUtils::IsARIASelected(cell)) return false; - } - - return true; -} - -uint32_t ARIAGridAccessible::SelectedCellCount() { - if (IsARIARole(nsGkAtoms::table)) return 0; - - uint32_t count = 0, colCount = ColCount(); - - AccIterator rowIter(this, filters::GetRow); - LocalAccessible* row = nullptr; - - while ((row = rowIter.Next())) { - if (nsAccUtils::IsARIASelected(row)) { - count += colCount; - continue; - } - - AccIterator cellIter(row, filters::GetCell); - LocalAccessible* cell = nullptr; - - while ((cell = cellIter.Next())) { - if (nsAccUtils::IsARIASelected(cell)) count++; - } - } - - return count; -} - -uint32_t ARIAGridAccessible::SelectedColCount() { - if (IsARIARole(nsGkAtoms::table)) return 0; - - uint32_t colCount = ColCount(); - if (!colCount) return 0; - - AccIterator rowIter(this, filters::GetRow); - LocalAccessible* row = rowIter.Next(); - if (!row) return 0; - - nsTArray isColSelArray(colCount); - isColSelArray.AppendElements(colCount); - memset(isColSelArray.Elements(), true, colCount * sizeof(bool)); - - uint32_t selColCount = colCount; - do { - if (nsAccUtils::IsARIASelected(row)) continue; - - AccIterator cellIter(row, filters::GetCell); - LocalAccessible* cell = nullptr; - for (uint32_t colIdx = 0; (cell = cellIter.Next()) && colIdx < colCount; - colIdx++) { - if (isColSelArray[colIdx] && !nsAccUtils::IsARIASelected(cell)) { - isColSelArray[colIdx] = false; - selColCount--; - } - } - } while ((row = rowIter.Next())); - - return selColCount; -} - -uint32_t ARIAGridAccessible::SelectedRowCount() { - if (IsARIARole(nsGkAtoms::table)) return 0; - - uint32_t count = 0; - - AccIterator rowIter(this, filters::GetRow); - LocalAccessible* row = nullptr; - - while ((row = rowIter.Next())) { - if (nsAccUtils::IsARIASelected(row)) { - count++; - continue; - } - - AccIterator cellIter(row, filters::GetCell); - LocalAccessible* cell = cellIter.Next(); - if (!cell) continue; - - bool isRowSelected = true; - do { - if (!nsAccUtils::IsARIASelected(cell)) { - isRowSelected = false; - break; - } - } while ((cell = cellIter.Next())); - - if (isRowSelected) count++; - } - - return count; -} - -void ARIAGridAccessible::SelectedCells(nsTArray* aCells) { - if (IsARIARole(nsGkAtoms::table)) return; - - AccIterator rowIter(this, filters::GetRow); - - LocalAccessible* row = nullptr; - while ((row = rowIter.Next())) { - AccIterator cellIter(row, filters::GetCell); - LocalAccessible* cell = nullptr; - - if (nsAccUtils::IsARIASelected(row)) { - while ((cell = cellIter.Next())) aCells->AppendElement(cell); - - continue; - } - - while ((cell = cellIter.Next())) { - if (nsAccUtils::IsARIASelected(cell)) aCells->AppendElement(cell); - } - } -} - -void ARIAGridAccessible::SelectedCellIndices(nsTArray* aCells) { - if (IsARIARole(nsGkAtoms::table)) return; - - uint32_t colCount = ColCount(); - - AccIterator rowIter(this, filters::GetRow); - LocalAccessible* row = nullptr; - for (uint32_t rowIdx = 0; (row = rowIter.Next()); rowIdx++) { - if (nsAccUtils::IsARIASelected(row)) { - for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) { - aCells->AppendElement(rowIdx * colCount + colIdx); - } - - continue; - } - - AccIterator cellIter(row, filters::GetCell); - LocalAccessible* cell = nullptr; - for (uint32_t colIdx = 0; (cell = cellIter.Next()); colIdx++) { - if (nsAccUtils::IsARIASelected(cell)) { - aCells->AppendElement(rowIdx * colCount + colIdx); - } - } - } -} - -void ARIAGridAccessible::SelectedColIndices(nsTArray* aCols) { - if (IsARIARole(nsGkAtoms::table)) return; - - uint32_t colCount = ColCount(); - if (!colCount) return; - - AccIterator rowIter(this, filters::GetRow); - LocalAccessible* row = rowIter.Next(); - if (!row) return; - - nsTArray isColSelArray(colCount); - isColSelArray.AppendElements(colCount); - memset(isColSelArray.Elements(), true, colCount * sizeof(bool)); - - do { - if (nsAccUtils::IsARIASelected(row)) continue; - - AccIterator cellIter(row, filters::GetCell); - LocalAccessible* cell = nullptr; - for (uint32_t colIdx = 0; (cell = cellIter.Next()) && colIdx < colCount; - colIdx++) { - if (isColSelArray[colIdx] && !nsAccUtils::IsARIASelected(cell)) { - isColSelArray[colIdx] = false; - } - } - } while ((row = rowIter.Next())); - - for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) { - if (isColSelArray[colIdx]) aCols->AppendElement(colIdx); - } -} - -void ARIAGridAccessible::SelectedRowIndices(nsTArray* aRows) { - if (IsARIARole(nsGkAtoms::table)) return; - - AccIterator rowIter(this, filters::GetRow); - LocalAccessible* row = nullptr; - for (uint32_t rowIdx = 0; (row = rowIter.Next()); rowIdx++) { - if (nsAccUtils::IsARIASelected(row)) { - aRows->AppendElement(rowIdx); - continue; - } - - AccIterator cellIter(row, filters::GetCell); - LocalAccessible* cell = cellIter.Next(); - if (!cell) continue; - - bool isRowSelected = true; - do { - if (!nsAccUtils::IsARIASelected(cell)) { - isRowSelected = false; - break; - } - } while ((cell = cellIter.Next())); - - if (isRowSelected) aRows->AppendElement(rowIdx); - } -} - -void ARIAGridAccessible::SelectRow(uint32_t aRowIdx) { - if (IsARIARole(nsGkAtoms::table)) return; - - AccIterator rowIter(this, filters::GetRow); - - LocalAccessible* row = nullptr; - for (uint32_t rowIdx = 0; (row = rowIter.Next()); rowIdx++) { - DebugOnly rv = SetARIASelected(row, rowIdx == aRowIdx); - NS_ASSERTION(NS_SUCCEEDED(rv), "SetARIASelected() Shouldn't fail!"); - } -} - -void ARIAGridAccessible::SelectCol(uint32_t aColIdx) { - if (IsARIARole(nsGkAtoms::table)) return; - - AccIterator rowIter(this, filters::GetRow); - - LocalAccessible* row = nullptr; - while ((row = rowIter.Next())) { - // Unselect all cells in the row. - DebugOnly rv = SetARIASelected(row, false); - NS_ASSERTION(NS_SUCCEEDED(rv), "SetARIASelected() Shouldn't fail!"); - - // Select cell at the column index. - LocalAccessible* cell = CellInRowAt(row, aColIdx); - if (cell) SetARIASelected(cell, true); - } -} - -void ARIAGridAccessible::UnselectRow(uint32_t aRowIdx) { - if (IsARIARole(nsGkAtoms::table)) return; - - LocalAccessible* row = RowAt(aRowIdx); - if (row) SetARIASelected(row, false); -} - -void ARIAGridAccessible::UnselectCol(uint32_t aColIdx) { - if (IsARIARole(nsGkAtoms::table)) return; - - AccIterator rowIter(this, filters::GetRow); - - LocalAccessible* row = nullptr; - while ((row = rowIter.Next())) { - LocalAccessible* cell = CellInRowAt(row, aColIdx); - if (cell) SetARIASelected(cell, false); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Protected - -nsresult ARIAGridAccessible::SetARIASelected(LocalAccessible* aAccessible, - bool aIsSelected, bool aNotify) { - if (IsARIARole(nsGkAtoms::table)) return NS_OK; - - nsIContent* content = aAccessible->GetContent(); - NS_ENSURE_STATE(content); - - nsresult rv = NS_OK; - if (content->IsElement()) { - if (aIsSelected) { - rv = content->AsElement()->SetAttr( - kNameSpaceID_None, nsGkAtoms::aria_selected, u"true"_ns, aNotify); - } else { - rv = content->AsElement()->SetAttr( - kNameSpaceID_None, nsGkAtoms::aria_selected, u"false"_ns, aNotify); - } - } - - NS_ENSURE_SUCCESS(rv, rv); - - // No "smart" select/unselect for internal call. - if (!aNotify) return NS_OK; - - // If row or cell accessible was selected then we're able to not bother about - // selection of its cells or its row because our algorithm is row oriented, - // i.e. we check selection on row firstly and then on cells. - if (aIsSelected) return NS_OK; - - roles::Role role = aAccessible->Role(); - - // If the given accessible is row that was unselected then remove - // aria-selected from cell accessible. - if (role == roles::ROW) { - AccIterator cellIter(aAccessible, filters::GetCell); - LocalAccessible* cell = nullptr; - - while ((cell = cellIter.Next())) { - rv = SetARIASelected(cell, false, false); - NS_ENSURE_SUCCESS(rv, rv); - } - return NS_OK; - } - - // If the given accessible is cell that was unselected and its row is selected - // then remove aria-selected from row and put aria-selected on - // siblings cells. - if (role == roles::GRID_CELL || role == roles::ROWHEADER || - role == roles::COLUMNHEADER) { - LocalAccessible* row = aAccessible->LocalParent(); - - if (row && row->Role() == roles::ROW && nsAccUtils::IsARIASelected(row)) { - rv = SetARIASelected(row, false, false); - NS_ENSURE_SUCCESS(rv, rv); - - AccIterator cellIter(row, filters::GetCell); - LocalAccessible* cell = nullptr; - while ((cell = cellIter.Next())) { - if (cell != aAccessible) { - rv = SetARIASelected(cell, true, false); - NS_ENSURE_SUCCESS(rv, rv); - } - } - } - } - - return NS_OK; -} - -//////////////////////////////////////////////////////////////////////////////// -// ARIARowAccessible -//////////////////////////////////////////////////////////////////////////////// - -ARIARowAccessible::ARIARowAccessible(nsIContent* aContent, DocAccessible* aDoc) - : HyperTextAccessibleWrap(aContent, aDoc) { - mGenericTypes |= eTableRow; -} - -role ARIARowAccessible::NativeRole() const { - a11y::role r = GetAccService()->MarkupRole(mContent); - return r != roles::NOTHING ? r : roles::ROW; -} - -GroupPos ARIARowAccessible::GroupPosition() { - int32_t count = 0, index = 0; - LocalAccessible* table = nsAccUtils::TableFor(this); - if (table) { - if (nsCoreUtils::GetUIntAttr(table->GetContent(), nsGkAtoms::aria_rowcount, - &count) && - nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_rowindex, &index)) { - return GroupPos(0, index, count); - } - - // Deal with the special case here that tables and grids can have rows - // which are wrapped in generic text container elements. Exclude tree grids - // because these are dealt with elsewhere. - if (table->Role() == roles::TABLE) { - LocalAccessible* row = nullptr; - AccIterator rowIter(table, filters::GetRow); - while ((row = rowIter.Next())) { - index++; - if (row == this) { - break; - } - } - - if (row) { - count = table->AsTable()->RowCount(); - return GroupPos(0, index, count); - } - } - } - - return AccessibleWrap::GroupPosition(); -} - -// LocalAccessible protected -ENameValueFlag ARIARowAccessible::NativeName(nsString& aName) const { - // We want to calculate the name from content only if an ARIA role is - // present. ARIARowAccessible might also be used by tables with - // display:block; styling, in which case we do not want the name from - // content. - if (HasStrongARIARole()) { - return AccessibleWrap::NativeName(aName); - } - - return eNameOK; -} - -//////////////////////////////////////////////////////////////////////////////// // ARIAGridCellAccessible //////////////////////////////////////////////////////////////////////////////// @@ -518,53 +33,6 @@ mGenericTypes |= eTableCell; } -role ARIAGridCellAccessible::NativeRole() const { - const a11y::role r = GetAccService()->MarkupRole(mContent); - if (r != role::NOTHING) { - return r; - } - - // Special case to handle th elements mapped to ARIA grid cells. - if (GetContent() && GetContent()->IsHTMLElement(nsGkAtoms::th)) { - return GetHeaderCellRole(this); - } - - return role::CELL; -} - -//////////////////////////////////////////////////////////////////////////////// -// TableCell - -TableAccessible* ARIAGridCellAccessible::Table() const { - LocalAccessible* table = nsAccUtils::TableFor(Row()); - return table ? table->AsTable() : nullptr; -} - -uint32_t ARIAGridCellAccessible::ColIdx() const { - LocalAccessible* row = Row(); - if (!row) return 0; - - int32_t indexInRow = IndexInParent(); - uint32_t colIdx = 0; - for (int32_t idx = 0; idx < indexInRow; idx++) { - LocalAccessible* cell = row->LocalChildAt(idx); - if (cell->IsTableCell()) { - colIdx += cell->AsTableCell()->ColExtent(); - } - } - - return colIdx; -} - -uint32_t ARIAGridCellAccessible::RowIdx() const { return RowIndexFor(Row()); } - -bool ARIAGridCellAccessible::Selected() { - LocalAccessible* row = Row(); - if (!row) return false; - - return nsAccUtils::IsARIASelected(row) || nsAccUtils::IsARIASelected(this); -} - //////////////////////////////////////////////////////////////////////////////// // LocalAccessible @@ -591,35 +59,21 @@ RefPtr attributes = HyperTextAccessibleWrap::NativeAttributes(); - // Expose "table-cell-index" attribute. - LocalAccessible* thisRow = Row(); - if (!thisRow) return attributes.forget(); - - int32_t rowIdx = RowIndexFor(thisRow); - if (rowIdx == -1) { // error - return attributes.forget(); - } - - int32_t colIdx = 0, colCount = 0; - uint32_t childCount = thisRow->ChildCount(); - for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) { - LocalAccessible* child = thisRow->LocalChildAt(childIdx); - if (child == this) colIdx = colCount; - - roles::Role role = child->Role(); - if (role == roles::CELL || role == roles::GRID_CELL || - role == roles::ROWHEADER || role == roles::COLUMNHEADER) { - colCount++; + // We only need to expose table-cell-index to clients. If we're in the content + // process, we don't need this, so building a CachedTableAccessible is very + // wasteful. This will be exposed by RemoteAccessible in the parent process + // instead. + if (!IPCAccessibilityActive()) { + if (const TableCellAccessible* cell = AsTableCell()) { + TableAccessible* table = cell->Table(); + const uint32_t row = cell->RowIdx(); + const uint32_t col = cell->ColIdx(); + const int32_t cellIdx = table->CellIndexAt(row, col); + if (cellIdx != -1) { + attributes->SetAttribute(nsGkAtoms::tableCellIndex, cellIdx); + } } } - attributes->SetAttribute(nsGkAtoms::tableCellIndex, - rowIdx * colCount + colIdx); - -#ifdef DEBUG - RefPtr cppClass = NS_Atomize(u"cppclass"_ns); - attributes->SetAttributeStringCopy(cppClass, u"ARIAGridCellAccessible"_ns); -#endif - return attributes.forget(); } diff -Nru firefox-esr-115.3.1esr+build1/accessible/generic/ARIAGridAccessible.h firefox-esr-115.4.0esr+build1/accessible/generic/ARIAGridAccessible.h --- firefox-esr-115.3.1esr+build1/accessible/generic/ARIAGridAccessible.h 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/generic/ARIAGridAccessible.h 2023-10-17 05:45:43.000000000 +0000 @@ -7,90 +7,14 @@ #define MOZILLA_A11Y_ARIAGridAccessible_h_ #include "HyperTextAccessibleWrap.h" -#include "TableAccessible.h" -#include "TableCellAccessible.h" namespace mozilla { namespace a11y { /** - * Accessible for ARIA grid and treegrid. - */ -class ARIAGridAccessible : public HyperTextAccessibleWrap, - public TableAccessible { - public: - ARIAGridAccessible(nsIContent* aContent, DocAccessible* aDoc); - - NS_INLINE_DECL_REFCOUNTING_INHERITED(ARIAGridAccessible, - HyperTextAccessibleWrap) - - // LocalAccessible - virtual a11y::role NativeRole() const override; - virtual already_AddRefed NativeAttributes() override; - virtual TableAccessible* AsTable() override { return this; } - - // TableAccessible - virtual uint32_t ColCount() const override; - virtual uint32_t RowCount() override; - virtual LocalAccessible* CellAt(uint32_t aRowIndex, - uint32_t aColumnIndex) override; - virtual bool IsColSelected(uint32_t aColIdx) override; - virtual bool IsRowSelected(uint32_t aRowIdx) override; - virtual bool IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx) override; - virtual uint32_t SelectedCellCount() override; - virtual uint32_t SelectedColCount() override; - virtual uint32_t SelectedRowCount() override; - virtual void SelectedCells(nsTArray* aCells) override; - virtual void SelectedCellIndices(nsTArray* aCells) override; - virtual void SelectedColIndices(nsTArray* aCols) override; - virtual void SelectedRowIndices(nsTArray* aRows) override; - virtual void SelectCol(uint32_t aColIdx) override; - virtual void SelectRow(uint32_t aRowIdx) override; - virtual void UnselectCol(uint32_t aColIdx) override; - virtual void UnselectRow(uint32_t aRowIdx) override; - virtual LocalAccessible* AsAccessible() override { return this; } - - protected: - virtual ~ARIAGridAccessible() {} - - /** - * Set aria-selected attribute value on DOM node of the given accessible. - * - * @param aAccessible [in] accessible - * @param aIsSelected [in] new value of aria-selected attribute - * @param aNotify [in, optional] specifies if DOM should be notified - * about attribute change (used internally). - */ - nsresult SetARIASelected(LocalAccessible* aAccessible, bool aIsSelected, - bool aNotify = true); -}; - -/** - * Accessible for ARIA row. - */ -class ARIARowAccessible : public HyperTextAccessibleWrap { - public: - ARIARowAccessible(nsIContent* aContent, DocAccessible* aDoc); - - NS_INLINE_DECL_REFCOUNTING_INHERITED(ARIARowAccessible, - HyperTextAccessibleWrap) - - // LocalAccessible - virtual a11y::role NativeRole() const override; - virtual mozilla::a11y::GroupPos GroupPosition() override; - - protected: - virtual ~ARIARowAccessible() {} - - // LocalAccessible - virtual ENameValueFlag NativeName(nsString& aName) const override; -}; - -/** * Accessible for ARIA gridcell and rowheader/columnheader. */ -class ARIAGridCellAccessible : public HyperTextAccessibleWrap, - public TableCellAccessible { +class ARIAGridCellAccessible : public HyperTextAccessibleWrap { public: ARIAGridCellAccessible(nsIContent* aContent, DocAccessible* aDoc); @@ -98,33 +22,11 @@ HyperTextAccessibleWrap) // LocalAccessible - virtual a11y::role NativeRole() const override; - virtual TableCellAccessible* AsTableCell() override { return this; } virtual void ApplyARIAState(uint64_t* aState) const override; virtual already_AddRefed NativeAttributes() override; protected: virtual ~ARIAGridCellAccessible() {} - - /** - * Return a containing row. - */ - LocalAccessible* Row() const { - LocalAccessible* row = LocalParent(); - return row && row->IsTableRow() ? row : nullptr; - } - - /** - * Return index of the given row. - * Returns -1 upon error. - */ - int32_t RowIndexFor(LocalAccessible* aRow) const; - - // TableCellAccessible - virtual TableAccessible* Table() const override; - virtual uint32_t ColIdx() const override; - virtual uint32_t RowIdx() const override; - virtual bool Selected() override; }; } // namespace a11y diff -Nru firefox-esr-115.3.1esr+build1/accessible/generic/ARIAGridAccessible-inl.h firefox-esr-115.4.0esr+build1/accessible/generic/ARIAGridAccessible-inl.h --- firefox-esr-115.3.1esr+build1/accessible/generic/ARIAGridAccessible-inl.h 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/generic/ARIAGridAccessible-inl.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,36 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_a11y_ARIAGridAccessible_inl_h__ -#define mozilla_a11y_ARIAGridAccessible_inl_h__ - -#include "ARIAGridAccessible.h" - -#include "AccIterator.h" -#include "nsAccUtils.h" - -namespace mozilla { -namespace a11y { - -inline int32_t ARIAGridCellAccessible::RowIndexFor( - LocalAccessible* aRow) const { - LocalAccessible* table = nsAccUtils::TableFor(aRow); - if (table) { - int32_t rowIdx = 0; - LocalAccessible* row = nullptr; - AccIterator rowIter(table, filters::GetRow); - while ((row = rowIter.Next()) && row != aRow) rowIdx++; - - if (row) return rowIdx; - } - - return -1; -} - -} // namespace a11y -} // namespace mozilla - -#endif diff -Nru firefox-esr-115.3.1esr+build1/accessible/generic/LocalAccessible.cpp firefox-esr-115.4.0esr+build1/accessible/generic/LocalAccessible.cpp --- firefox-esr-115.3.1esr+build1/accessible/generic/LocalAccessible.cpp 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/generic/LocalAccessible.cpp 2023-10-17 05:45:44.000000000 +0000 @@ -6,6 +6,7 @@ #include "AccEvent.h" #include "LocalAccessible-inl.h" +#include #include "EmbeddedObjCollector.h" #include "AccAttributes.h" #include "AccGroupInfo.h" @@ -14,6 +15,8 @@ #include "CachedTableAccessible.h" #include "DocAccessible-inl.h" #include "mozilla/a11y/AccAttributes.h" +#include "mozilla/a11y/TableAccessible.h" +#include "mozilla/a11y/TableCellAccessible.h" #include "nsAccUtils.h" #include "nsAccessibilityService.h" #include "ApplicationAccessible.h" @@ -33,14 +36,14 @@ #include "StyleInfo.h" #include "TextLeafRange.h" #include "TextRange.h" -#include "TableAccessible.h" -#include "TableCellAccessible.h" #include "TreeWalker.h" #include "HTMLElementAccessibles.h" #include "HTMLSelectAccessible.h" +#include "HTMLTableAccessible.h" #include "ImageAccessible.h" #include "nsComputedDOMStyle.h" +#include "nsGkAtoms.h" #include "nsIDOMXULButtonElement.h" #include "nsIDOMXULSelectCntrlEl.h" #include "nsIDOMXULSelectCntrlItemEl.h" @@ -1624,16 +1627,15 @@ if ((roleMapEntry->Is(nsGkAtoms::gridcell) || roleMapEntry->Is(nsGkAtoms::columnheader) || roleMapEntry->Is(nsGkAtoms::rowheader)) && + // Don't recurse infinitely for an authoring error like + //
. Without this check, we'd call TableFor(this) + // below, which would return this. + !IsTable() && !nsAccUtils::HasDefinedARIAToken(mContent, nsGkAtoms::aria_readonly)) { - const TableCellAccessible* cell = AsTableCell(); - if (cell) { - TableAccessible* table = cell->Table(); - if (table) { - LocalAccessible* grid = table->AsAccessible(); - uint64_t gridState = 0; - grid->ApplyARIAState(&gridState); - *aState |= gridState & states::READONLY; - } + if (const LocalAccessible* grid = nsAccUtils::TableFor(this)) { + uint64_t gridState = 0; + grid->ApplyARIAState(&gridState); + *aState |= gridState & states::READONLY; } } } @@ -1820,12 +1822,9 @@ // A cell inside an ancestor table element that has a grid role needs a // gridcell role // (https://www.w3.org/TR/html-aam-1.0/#html-element-role-mappings). - const TableCellAccessible* cell = AsTableCell(); - if (cell) { - TableAccessible* table = cell->Table(); - if (table && table->AsAccessible()->IsARIARole(nsGkAtoms::grid)) { - return roles::GRID_CELL; - } + const LocalAccessible* table = nsAccUtils::TableFor(this); + if (table && table->IsARIARole(nsGkAtoms::grid)) { + return roles::GRID_CELL; } } @@ -3325,6 +3324,7 @@ } bool boundsChanged = false; + nsIFrame* frame = GetFrame(); if (aCacheDomain & CacheDomain::Bounds) { nsRect newBoundsRect = ParentRelativeBounds(); @@ -3374,6 +3374,12 @@ fields->SetAttribute(nsGkAtoms::relativeBounds, std::move(boundsArray)); } + + if (frame && frame->ScrollableOverflowRect().IsEmpty()) { + fields->SetAttribute(nsGkAtoms::clip_rule, true); + } else if (aUpdateType != CacheUpdateType::Initial) { + fields->SetAttribute(nsGkAtoms::clip_rule, DeleteEntry()); + } } if (aCacheDomain & CacheDomain::Text) { @@ -3407,7 +3413,6 @@ } } - nsIFrame* frame = GetFrame(); if (aCacheDomain & (CacheDomain::Text | CacheDomain::Bounds) && !HasChildren()) { // We cache line start offsets for both text and non-text leaf Accessibles @@ -3630,16 +3635,27 @@ } else if (aUpdateType != CacheUpdateType::Initial) { fields->SetAttribute(nsGkAtoms::position, DeleteEntry()); } + + if (frame) { + nsAutoCString overflow; + frame->Style()->GetComputedPropertyValue(eCSSProperty_overflow, overflow); + RefPtr overflowAtom = NS_Atomize(overflow); + if (overflowAtom == nsGkAtoms::hidden) { + fields->SetAttribute(nsGkAtoms::overflow, nsGkAtoms::hidden); + } else if (aUpdateType != CacheUpdateType::Initial) { + fields->SetAttribute(nsGkAtoms::overflow, DeleteEntry()); + } + } } if (aCacheDomain & CacheDomain::Table) { - if (TableAccessible* table = AsTable()) { + if (auto* table = HTMLTableAccessible::GetFrom(this)) { if (table->IsProbablyLayoutTable()) { fields->SetAttribute(nsGkAtoms::layout_guess, true); } else if (aUpdateType == CacheUpdateType::Update) { fields->SetAttribute(nsGkAtoms::layout_guess, DeleteEntry()); } - } else if (TableCellAccessible* cell = AsTableCell()) { + } else if (auto* cell = HTMLTableCellAccessible::GetFrom(this)) { // For HTML table cells, we must use the HTMLTableCellAccessible // GetRow/ColExtent methods rather than using the DOM attributes directly. // This is because of things like rowspan="0" which depend on knowing @@ -3836,6 +3852,19 @@ if (nsIFrame* frame = GetFrame()) { const ComputedStyle* newStyle = frame->Style(); + nsAutoCString oldOverflow, newOverflow; + mOldComputedStyle->GetComputedPropertyValue(eCSSProperty_overflow, + oldOverflow); + newStyle->GetComputedPropertyValue(eCSSProperty_overflow, newOverflow); + + if (oldOverflow != newOverflow) { + RefPtr oldAtom = NS_Atomize(oldOverflow); + RefPtr newAtom = NS_Atomize(newOverflow); + if (oldAtom == nsGkAtoms::hidden || newAtom == nsGkAtoms::hidden) { + mDoc->QueueCacheUpdate(this, CacheDomain::Style); + } + } + nsAutoCString oldDisplay, newDisplay; mOldComputedStyle->GetComputedPropertyValue(eCSSProperty_display, oldDisplay); @@ -4011,18 +4040,18 @@ "LocalAccessible::mContextFlags was oversized by eLastContextFlag!"); } -TableAccessibleBase* LocalAccessible::AsTableBase() { +TableAccessible* LocalAccessible::AsTable() { if (IsTable() && !mContent->IsXULElement()) { return CachedTableAccessible::GetFrom(this); } - return AsTable(); + return nullptr; } -TableCellAccessibleBase* LocalAccessible::AsTableCellBase() { +TableCellAccessible* LocalAccessible::AsTableCell() { if (IsTableCell() && !mContent->IsXULElement()) { return CachedTableCellAccessible::GetFrom(this); } - return AsTableCell(); + return nullptr; } Maybe LocalAccessible::GetIntARIAAttr(nsAtom* aAttrName) const { diff -Nru firefox-esr-115.3.1esr+build1/accessible/generic/LocalAccessible.h firefox-esr-115.4.0esr+build1/accessible/generic/LocalAccessible.h --- firefox-esr-115.3.1esr+build1/accessible/generic/LocalAccessible.h 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/generic/LocalAccessible.h 2023-10-17 05:45:44.000000000 +0000 @@ -53,9 +53,7 @@ class Relation; class RootAccessible; class TableAccessible; -class TableAccessibleBase; class TableCellAccessible; -class TableCellAccessibleBase; class TextLeafAccessible; class XULLabelAccessible; class XULTreeAccessible; @@ -464,15 +462,8 @@ a11y::RootAccessible* AsRoot(); - virtual TableAccessible* AsTable() { return nullptr; } - - virtual TableCellAccessible* AsTableCell() { return nullptr; } - const TableCellAccessible* AsTableCell() const { - return const_cast(this)->AsTableCell(); - } - - virtual TableAccessibleBase* AsTableBase() override; - virtual TableCellAccessibleBase* AsTableCellBase() override; + virtual TableAccessible* AsTable() override; + virtual TableCellAccessible* AsTableCell() override; TextLeafAccessible* AsTextLeaf(); diff -Nru firefox-esr-115.3.1esr+build1/accessible/generic/moz.build firefox-esr-115.4.0esr+build1/accessible/generic/moz.build --- firefox-esr-115.3.1esr+build1/accessible/generic/moz.build 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/generic/moz.build 2023-10-17 05:45:43.000000000 +0000 @@ -22,8 +22,6 @@ "LocalAccessible.cpp", "OuterDocAccessible.cpp", "RootAccessible.cpp", - "TableAccessible.cpp", - "TableCellAccessible.cpp", "TextLeafAccessible.cpp", ] diff -Nru firefox-esr-115.3.1esr+build1/accessible/generic/TableAccessible.cpp firefox-esr-115.4.0esr+build1/accessible/generic/TableAccessible.cpp --- firefox-esr-115.3.1esr+build1/accessible/generic/TableAccessible.cpp 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/generic/TableAccessible.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,316 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "TableAccessible.h" - -#include "LocalAccessible-inl.h" -#include "AccIterator.h" - -#include "nsTableCellFrame.h" -#include "nsTableWrapperFrame.h" -#include "TableCellAccessible.h" - -using namespace mozilla; -using namespace mozilla::a11y; - -bool TableAccessible::IsProbablyLayoutTable() { - // Implement a heuristic to determine if table is most likely used for layout. - - // XXX do we want to look for rowspan or colspan, especialy that span all but - // a couple cells at the beginning or end of a row/col, and especially when - // they occur at the edge of a table? - - // XXX For now debugging descriptions are always on via SHOW_LAYOUT_HEURISTIC - // This will allow release trunk builds to be used by testers to refine - // the algorithm. Integrate it into Logging. - // Change to |#define SHOW_LAYOUT_HEURISTIC DEBUG| before final release -#ifdef SHOW_LAYOUT_HEURISTIC -# define RETURN_LAYOUT_ANSWER(isLayout, heuristic) \ - { \ - mLayoutHeuristic = isLayout \ - ? nsLiteralString(u"layout table: " heuristic) \ - : nsLiteralString(u"data table: " heuristic); \ - return isLayout; \ - } -#else -# define RETURN_LAYOUT_ANSWER(isLayout, heuristic) \ - { return isLayout; } -#endif - - LocalAccessible* thisacc = AsAccessible(); - - MOZ_ASSERT(!thisacc->IsDefunct(), "Table accessible should not be defunct"); - - // Need to see all elements while document is being edited. - if (thisacc->Document()->State() & states::EDITABLE) { - RETURN_LAYOUT_ANSWER(false, "In editable document"); - } - - // Check to see if an ARIA role overrides the role from native markup, - // but for which we still expose table semantics (treegrid, for example). - if (thisacc->HasARIARole()) { - RETURN_LAYOUT_ANSWER(false, "Has role attribute"); - } - - dom::Element* el = thisacc->Elm(); - if (el->IsMathMLElement(nsGkAtoms::mtable_)) { - RETURN_LAYOUT_ANSWER(false, "MathML matrix"); - } - - MOZ_ASSERT(el->IsHTMLElement(nsGkAtoms::table), - "Table should not be built by CSS display:table style"); - - // Check if datatable attribute has "0" value. - if (el->AttrValueIs(kNameSpaceID_None, nsGkAtoms::datatable, u"0"_ns, - eCaseMatters)) { - RETURN_LAYOUT_ANSWER(true, "Has datatable = 0 attribute, it's for layout"); - } - - // Check for legitimate data table attributes. - if (el->Element::HasNonEmptyAttr(nsGkAtoms::summary)) { - RETURN_LAYOUT_ANSWER(false, "Has summary -- legitimate table structures"); - } - - // Check for legitimate data table elements. - LocalAccessible* caption = thisacc->LocalFirstChild(); - if (caption && caption->IsHTMLCaption() && caption->HasChildren()) { - RETURN_LAYOUT_ANSWER(false, - "Not empty caption -- legitimate table structures"); - } - - for (nsIContent* childElm = el->GetFirstChild(); childElm; - childElm = childElm->GetNextSibling()) { - if (!childElm->IsHTMLElement()) continue; - - if (childElm->IsAnyOfHTMLElements(nsGkAtoms::col, nsGkAtoms::colgroup, - nsGkAtoms::tfoot, nsGkAtoms::thead)) { - RETURN_LAYOUT_ANSWER( - false, - "Has col, colgroup, tfoot or thead -- legitimate table structures"); - } - - if (childElm->IsHTMLElement(nsGkAtoms::tbody)) { - for (nsIContent* rowElm = childElm->GetFirstChild(); rowElm; - rowElm = rowElm->GetNextSibling()) { - if (rowElm->IsHTMLElement(nsGkAtoms::tr)) { - if (LocalAccessible* row = - thisacc->Document()->GetAccessible(rowElm)) { - if (const nsRoleMapEntry* roleMapEntry = row->ARIARoleMap()) { - if (roleMapEntry->role != roles::ROW) { - RETURN_LAYOUT_ANSWER(true, "Repurposed tr with different role"); - } - } - } - - for (nsIContent* cellElm = rowElm->GetFirstChild(); cellElm; - cellElm = cellElm->GetNextSibling()) { - if (cellElm->IsHTMLElement()) { - if (cellElm->NodeInfo()->Equals(nsGkAtoms::th)) { - RETURN_LAYOUT_ANSWER(false, - "Has th -- legitimate table structures"); - } - - if (cellElm->AsElement()->HasAttr(kNameSpaceID_None, - nsGkAtoms::headers) || - cellElm->AsElement()->HasAttr(kNameSpaceID_None, - nsGkAtoms::scope) || - cellElm->AsElement()->HasAttr(kNameSpaceID_None, - nsGkAtoms::abbr)) { - RETURN_LAYOUT_ANSWER(false, - "Has headers, scope, or abbr attribute -- " - "legitimate table structures"); - } - - if (LocalAccessible* cell = - thisacc->Document()->GetAccessible(cellElm)) { - if (const nsRoleMapEntry* roleMapEntry = cell->ARIARoleMap()) { - if (roleMapEntry->role != roles::CELL && - roleMapEntry->role != roles::COLUMNHEADER && - roleMapEntry->role != roles::ROWHEADER && - roleMapEntry->role != roles::GRID_CELL) { - RETURN_LAYOUT_ANSWER(true, - "Repurposed cell with different role"); - } - } - if (cell->ChildCount() == 1 && - cell->LocalFirstChild()->IsAbbreviation()) { - RETURN_LAYOUT_ANSWER( - false, "has abbr -- legitimate table structures"); - } - } - } - } - } - } - } - } - - // Check for nested tables. - nsCOMPtr nestedTables = - el->GetElementsByTagName(u"table"_ns); - if (nestedTables->Length() > 0) { - RETURN_LAYOUT_ANSWER(true, "Has a nested table within it"); - } - - // If only 1 column or only 1 row, it's for layout. - auto colCount = ColCount(); - if (colCount <= 1) { - RETURN_LAYOUT_ANSWER(true, "Has only 1 column"); - } - auto rowCount = RowCount(); - if (rowCount <= 1) { - RETURN_LAYOUT_ANSWER(true, "Has only 1 row"); - } - - // Check for many columns. - if (colCount >= 5) { - RETURN_LAYOUT_ANSWER(false, ">=5 columns"); - } - - // Now we know there are 2-4 columns and 2 or more rows. Check to see if - // there are visible borders on the cells. - // XXX currently, we just check the first cell -- do we really need to do - // more? - nsTableWrapperFrame* tableFrame = do_QueryFrame(el->GetPrimaryFrame()); - if (!tableFrame) { - RETURN_LAYOUT_ANSWER(false, "table with no frame!"); - } - - nsIFrame* cellFrame = tableFrame->GetCellFrameAt(0, 0); - if (!cellFrame) { - RETURN_LAYOUT_ANSWER(false, "table's first cell has no frame!"); - } - - nsMargin border = cellFrame->StyleBorder()->GetComputedBorder(); - if (border.top && border.bottom && border.left && border.right) { - RETURN_LAYOUT_ANSWER(false, "Has nonzero border-width on table cell"); - } - - // Rules for non-bordered tables with 2-4 columns and 2+ rows from here on - // forward. - - // Check for styled background color across rows (alternating background - // color is a common feature for data tables). - auto childCount = thisacc->ChildCount(); - nscolor rowColor = 0; - nscolor prevRowColor; - for (auto childIdx = 0U; childIdx < childCount; childIdx++) { - LocalAccessible* child = thisacc->LocalChildAt(childIdx); - if (child->IsHTMLTableRow()) { - prevRowColor = rowColor; - nsIFrame* rowFrame = child->GetFrame(); - MOZ_ASSERT(rowFrame, "Table hierarchy got screwed up"); - if (!rowFrame) { - RETURN_LAYOUT_ANSWER(false, "Unexpected table hierarchy"); - } - - rowColor = rowFrame->StyleBackground()->BackgroundColor(rowFrame); - - if (childIdx > 0 && prevRowColor != rowColor) { - RETURN_LAYOUT_ANSWER(false, - "2 styles of row background color, non-bordered"); - } - } - } - - // Check for many rows. - const uint32_t kMaxLayoutRows = 20; - if (rowCount > kMaxLayoutRows) { // A ton of rows, this is probably for data - RETURN_LAYOUT_ANSWER(false, ">= kMaxLayoutRows (20) and non-bordered"); - } - - // Check for very wide table. - nsIFrame* documentFrame = thisacc->Document()->GetFrame(); - nsSize documentSize = documentFrame->GetSize(); - if (documentSize.width > 0) { - nsSize tableSize = thisacc->GetFrame()->GetSize(); - int32_t percentageOfDocWidth = (100 * tableSize.width) / documentSize.width; - if (percentageOfDocWidth > 95) { - // 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width - // Probably for layout - RETURN_LAYOUT_ANSWER( - true, "<= 4 columns, table width is 95% of document width"); - } - } - - // Two column rules. - if (rowCount * colCount <= 10) { - RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered"); - } - - static const nsLiteralString tags[] = {u"embed"_ns, u"object"_ns, - u"iframe"_ns}; - for (auto& tag : tags) { - nsCOMPtr descendants = el->GetElementsByTagName(tag); - if (descendants->Length() > 0) { - RETURN_LAYOUT_ANSWER(true, - "Has no borders, and has iframe, object or embed, " - "typical of advertisements"); - } - } - - RETURN_LAYOUT_ANSWER(false, - "No layout factor strong enough, so will guess data"); -} - -LocalAccessible* TableAccessible::RowAt(int32_t aRow) { - int32_t rowIdx = aRow; - - AccIterator rowIter(this->AsAccessible(), filters::GetRow); - - LocalAccessible* row = rowIter.Next(); - while (rowIdx != 0 && (row = rowIter.Next())) { - rowIdx--; - } - - return row; -} - -LocalAccessible* TableAccessible::CellInRowAt(LocalAccessible* aRow, - int32_t aColumn) { - int32_t colIdx = aColumn; - - AccIterator cellIter(aRow, filters::GetCell); - LocalAccessible* cell = nullptr; - - while (colIdx >= 0 && (cell = cellIter.Next())) { - MOZ_ASSERT(cell->IsTableCell(), "No table or grid cell!"); - colIdx -= cell->AsTableCell()->ColExtent(); - } - - return cell; -} - -int32_t TableAccessible::ColIndexAt(uint32_t aCellIdx) { - uint32_t colCount = ColCount(); - if (colCount < 1 || aCellIdx >= colCount * RowCount()) { - return -1; // Error: column count is 0 or index out of bounds. - } - - return aCellIdx % colCount; -} - -int32_t TableAccessible::RowIndexAt(uint32_t aCellIdx) { - uint32_t colCount = ColCount(); - if (colCount < 1 || aCellIdx >= colCount * RowCount()) { - return -1; // Error: column count is 0 or index out of bounds. - } - - return aCellIdx / colCount; -} - -void TableAccessible::RowAndColIndicesAt(uint32_t aCellIdx, int32_t* aRowIdx, - int32_t* aColIdx) { - uint32_t colCount = ColCount(); - if (colCount < 1 || aCellIdx >= colCount * RowCount()) { - *aRowIdx = -1; - *aColIdx = -1; - return; // Error: column count is 0 or index out of bounds. - } - - *aRowIdx = aCellIdx / colCount; - *aColIdx = aCellIdx % colCount; -} diff -Nru firefox-esr-115.3.1esr+build1/accessible/generic/TableAccessible.h firefox-esr-115.4.0esr+build1/accessible/generic/TableAccessible.h --- firefox-esr-115.3.1esr+build1/accessible/generic/TableAccessible.h 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/generic/TableAccessible.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,72 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef TABLE_ACCESSIBLE_H -#define TABLE_ACCESSIBLE_H - -#include "LocalAccessible.h" -#include "mozilla/a11y/TableAccessibleBase.h" -#include "mozilla/a11y/TableCellAccessibleBase.h" -#include "nsPointerHashKeys.h" -#include "nsRefPtrHashtable.h" - -namespace mozilla { -namespace a11y { - -/** - * Base class for LocalAccessible table implementations. - */ -class TableAccessible : public TableAccessibleBase { - public: - virtual LocalAccessible* Caption() const override { return nullptr; } - - virtual LocalAccessible* CellAt(uint32_t aRowIdx, uint32_t aColIdx) override { - return nullptr; - } - - virtual int32_t CellIndexAt(uint32_t aRowIdx, uint32_t aColIdx) override { - return ColCount() * aRowIdx + aColIdx; - } - - virtual int32_t ColIndexAt(uint32_t aCellIdx) override; - virtual int32_t RowIndexAt(uint32_t aCellIdx) override; - virtual void RowAndColIndicesAt(uint32_t aCellIdx, int32_t* aRowIdx, - int32_t* aColIdx) override; - virtual bool IsProbablyLayoutTable() override; - virtual LocalAccessible* AsAccessible() override = 0; - - using HeaderCache = - nsRefPtrHashtable, - LocalAccessible>; - - /** - * Get the header cache, which maps a TableCellAccessible to its previous - * header. - * Although this data is only used in TableCellAccessible, it is stored on - * TableAccessible so the cache can be easily invalidated when the table - * is mutated. - */ - HeaderCache& GetHeaderCache() { return mHeaderCache; } - - protected: - /** - * Return row accessible at the given row index. - */ - LocalAccessible* RowAt(int32_t aRow); - - /** - * Return cell accessible at the given column index in the row. - */ - LocalAccessible* CellInRowAt(LocalAccessible* aRow, int32_t aColumn); - - private: - HeaderCache mHeaderCache; -}; - -} // namespace a11y -} // namespace mozilla - -#endif diff -Nru firefox-esr-115.3.1esr+build1/accessible/generic/TableCellAccessible.cpp firefox-esr-115.4.0esr+build1/accessible/generic/TableCellAccessible.cpp --- firefox-esr-115.3.1esr+build1/accessible/generic/TableCellAccessible.cpp 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/generic/TableCellAccessible.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,165 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "TableCellAccessible.h" - -#include "LocalAccessible-inl.h" -#include "TableAccessible.h" - -using namespace mozilla; -using namespace mozilla::a11y; - -void TableCellAccessible::RowHeaderCells(nsTArray* aCells) { - uint32_t rowIdx = RowIdx(), colIdx = ColIdx(); - TableAccessible* table = Table(); - if (!table) return; - - // Move to the left to find row header cells - for (uint32_t curColIdx = colIdx - 1; curColIdx < colIdx; curColIdx--) { - LocalAccessible* cell = table->CellAt(rowIdx, curColIdx); - if (!cell) continue; - - // CellAt should always return a TableCellAccessible (XXX Bug 587529) - TableCellAccessible* tableCell = cell->AsTableCell(); - NS_ASSERTION(tableCell, "cell should be a table cell!"); - if (!tableCell) continue; - - // Avoid addding cells multiple times, if this cell spans more columns - // we'll get it later. - if (tableCell->ColIdx() == curColIdx && cell->Role() == roles::ROWHEADER) { - aCells->AppendElement(cell); - } - } -} - -LocalAccessible* TableCellAccessible::PrevColHeader() { - TableAccessible* table = Table(); - if (!table) { - return nullptr; - } - - TableAccessible::HeaderCache& cache = table->GetHeaderCache(); - bool inCache = false; - LocalAccessible* cachedHeader = cache.GetWeak(this, &inCache); - if (inCache) { - // Cached but null means we know there is no previous column header. - // if defunct, the cell was removed, so behave as if there is no cached - // value. - if (!cachedHeader || !cachedHeader->IsDefunct()) { - return cachedHeader; - } - } - - uint32_t rowIdx = RowIdx(), colIdx = ColIdx(); - for (uint32_t curRowIdx = rowIdx - 1; curRowIdx < rowIdx; curRowIdx--) { - LocalAccessible* cell = table->CellAt(curRowIdx, colIdx); - if (!cell) { - continue; - } - // CellAt should always return a TableCellAccessible (XXX Bug 587529) - TableCellAccessible* tableCell = cell->AsTableCell(); - MOZ_ASSERT(tableCell, "cell should be a table cell!"); - if (!tableCell) { - continue; - } - - // Check whether the previous table cell has a cached value. - cachedHeader = cache.GetWeak(tableCell, &inCache); - if ( - // We check the cache first because even though we might not use it, - // it's faster than the other conditions. - inCache && - // Only use the cached value if: - // 1. cell is a table cell which is not a column header. In that case, - // cell is the previous header and cachedHeader is the one before that. - // We will return cell later. - cell->Role() != roles::COLUMNHEADER && - // 2. cell starts in this column. If it starts in a previous column and - // extends into this one, its header will be for the starting column, - // which is wrong for this cell. - // ColExtent is faster than ColIdx, so check that first. - (tableCell->ColExtent() == 1 || tableCell->ColIdx() == colIdx)) { - if (!cachedHeader || !cachedHeader->IsDefunct()) { - // Cache it for this cell. - cache.InsertOrUpdate(this, RefPtr(cachedHeader)); - return cachedHeader; - } - } - - // Avoid addding cells multiple times, if this cell spans more rows - // we'll get it later. - if (cell->Role() != roles::COLUMNHEADER || - tableCell->RowIdx() != curRowIdx) { - continue; - } - - // Cache the header we found. - cache.InsertOrUpdate(this, RefPtr(cell)); - return cell; - } - - // There's no header, so cache that fact. - cache.InsertOrUpdate(this, RefPtr(nullptr)); - return nullptr; -} - -void TableCellAccessible::ColHeaderCells(nsTArray* aCells) { - for (LocalAccessible* cell = PrevColHeader(); cell; - cell = cell->AsTableCell()->PrevColHeader()) { - aCells->AppendElement(cell); - } -} - -a11y::role TableCellAccessible::GetHeaderCellRole( - const LocalAccessible* aAcc) const { - if (!aAcc || !aAcc->GetContent() || !aAcc->GetContent()->IsElement()) { - return roles::NOTHING; - } - - MOZ_ASSERT(aAcc->IsTableCell()); - - // Check value of @scope attribute. - static mozilla::dom::Element::AttrValuesArray scopeValues[] = { - nsGkAtoms::col, nsGkAtoms::colgroup, nsGkAtoms::row, nsGkAtoms::rowgroup, - nullptr}; - int32_t valueIdx = aAcc->GetContent()->AsElement()->FindAttrValueIn( - kNameSpaceID_None, nsGkAtoms::scope, scopeValues, eCaseMatters); - - switch (valueIdx) { - case 0: - case 1: - return roles::COLUMNHEADER; - case 2: - case 3: - return roles::ROWHEADER; - } - - TableAccessible* table = Table(); - if (!table) { - return roles::NOTHING; - } - - // If the cell next to this one is not a header cell then assume this cell is - // a row header for it. - uint32_t rowIdx = RowIdx(), colIdx = ColIdx(); - LocalAccessible* cell = table->CellAt(rowIdx, colIdx + ColExtent()); - if (cell && !nsCoreUtils::IsHTMLTableHeader(cell->GetContent())) { - return roles::ROWHEADER; - } - - // If the cell below this one is not a header cell then assume this cell is - // a column header for it. - uint32_t rowExtent = RowExtent(); - cell = table->CellAt(rowIdx + rowExtent, colIdx); - if (cell && !nsCoreUtils::IsHTMLTableHeader(cell->GetContent())) { - return roles::COLUMNHEADER; - } - - // Otherwise if this cell is surrounded by header cells only then make a guess - // based on its cell spanning. In other words if it is row spanned then assume - // it's a row header, otherwise it's a column header. - return rowExtent > 1 ? roles::ROWHEADER : roles::COLUMNHEADER; -} diff -Nru firefox-esr-115.3.1esr+build1/accessible/generic/TableCellAccessible.h firefox-esr-115.4.0esr+build1/accessible/generic/TableCellAccessible.h --- firefox-esr-115.3.1esr+build1/accessible/generic/TableCellAccessible.h 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/generic/TableCellAccessible.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_a11y_TableCellAccessible_h__ -#define mozilla_a11y_TableCellAccessible_h__ - -#include "mozilla/a11y/TableCellAccessibleBase.h" -#include "TableAccessible.h" - -namespace mozilla { -namespace a11y { - -class LocalAccessible; - -/** - * Base class for LocalAccessible table cell implementations. - */ -class TableCellAccessible : public TableCellAccessibleBase { - public: - virtual TableAccessible* Table() const override = 0; - virtual void ColHeaderCells(nsTArray* aCells) override; - virtual void RowHeaderCells(nsTArray* aCells) override; - - protected: - // Get the proper role for the given header cell accessible. The given acc - // must be either an ARIA grid cell accessible for a th element or a true - // table header cell accessible for the result to be valid. - a11y::role GetHeaderCellRole(const LocalAccessible* aAcc) const; - - private: - LocalAccessible* PrevColHeader(); -}; - -} // namespace a11y -} // namespace mozilla - -#endif // mozilla_a11y_TableCellAccessible_h__ diff -Nru firefox-esr-115.3.1esr+build1/accessible/html/HTMLTableAccessible.cpp firefox-esr-115.4.0esr+build1/accessible/html/HTMLTableAccessible.cpp --- firefox-esr-115.3.1esr+build1/accessible/html/HTMLTableAccessible.cpp 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/html/HTMLTableAccessible.cpp 2023-10-17 05:45:44.000000000 +0000 @@ -5,11 +5,12 @@ #include "HTMLTableAccessible.h" +#include #include "mozilla/DebugOnly.h" #include "nsAccessibilityService.h" -#include "nsAccUtils.h" #include "AccAttributes.h" +#include "ARIAMap.h" #include "CacheConstants.h" #include "DocAccessible.h" #include "LocalAccessible-inl.h" @@ -17,18 +18,29 @@ #include "Relation.h" #include "Role.h" #include "States.h" -#include "TreeWalker.h" #include "mozilla/PresShell.h" +#include "mozilla/a11y/TableAccessible.h" +#include "mozilla/a11y/TableCellAccessible.h" +#include "mozilla/Assertions.h" +#include "mozilla/dom/Element.h" #include "mozilla/dom/HTMLTableElement.h" +#include "mozilla/dom/NameSpaceConstants.h" +#include "nsCaseTreatment.h" +#include "nsColor.h" +#include "nsCOMPtr.h" +#include "nsCoreUtils.h" +#include "nsDebug.h" #include "nsIHTMLCollection.h" -#include "mozilla/dom/Document.h" #include "nsITableCellLayout.h" #include "nsFrameSelection.h" #include "nsError.h" -#include "nsArrayUtils.h" -#include "nsComponentManagerUtils.h" -#include "nsNameSpaceManager.h" +#include "nsGkAtoms.h" +#include "nsLiteralString.h" +#include "nsMargin.h" +#include "nsQueryFrame.h" +#include "nsSize.h" +#include "nsStringFwd.h" #include "nsTableCellFrame.h" #include "nsTableWrapperFrame.h" @@ -51,6 +63,10 @@ // HTMLTableCellAccessible: LocalAccessible implementation role HTMLTableCellAccessible::NativeRole() const { + // We implement this rather than using the markup maps because we only want + // this role to be returned if this is a valid cell. An invalid cell (e.g. if + // the table has role="none") won't use this class, so it will get a generic + // role, since the markup map doesn't specify a role. if (mContent->IsMathMLElement(nsGkAtoms::mtd_)) { return roles::MATHML_CELL; } @@ -78,21 +94,22 @@ RefPtr attributes = HyperTextAccessibleWrap::NativeAttributes(); - // table-cell-index attribute - TableAccessible* table = Table(); - if (!table) { - return attributes.forget(); - } - - int32_t rowIdx = -1, colIdx = -1; - nsresult rv = GetCellIndexes(rowIdx, colIdx); - if (NS_FAILED(rv)) { - return attributes.forget(); + // We only need to expose table-cell-index to clients. If we're in the content + // process, we don't need this, so building a CachedTableAccessible is very + // wasteful. This will be exposed by RemoteAccessible in the parent process + // instead. + if (!IPCAccessibilityActive()) { + if (const TableCellAccessible* cell = AsTableCell()) { + TableAccessible* table = cell->Table(); + const uint32_t row = cell->RowIdx(); + const uint32_t col = cell->ColIdx(); + const int32_t cellIdx = table->CellIndexAt(row, col); + if (cellIdx != -1) { + attributes->SetAttribute(nsGkAtoms::tableCellIndex, cellIdx); + } + } } - attributes->SetAttribute(nsGkAtoms::tableCellIndex, - table->CellIndexAt(rowIdx, colIdx)); - // abbr attribute // Pick up object attribute from abbr DOM element (a child of the cell) or @@ -124,11 +141,6 @@ attributes->SetAttribute(nsGkAtoms::axis, std::move(axisText)); } -#ifdef DEBUG - RefPtr cppClass = NS_Atomize(u"cppclass"_ns); - attributes->SetAttributeStringCopy(cppClass, u"HTMLTableCellAccessible"_ns); -#endif - return attributes.forget(); } @@ -144,61 +156,52 @@ aAttribute == nsGkAtoms::scope) { mDoc->FireDelayedEvent(nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED, this); - if (TableAccessible* table = Table()) { + if (HTMLTableAccessible* table = Table()) { // Modifying these attributes can also modify our table's classification // as either a layout or data table. Queue an update on the table itself // to re-compute our "layout guess" - mDoc->QueueCacheUpdate(table->AsAccessible(), CacheDomain::Table); + mDoc->QueueCacheUpdate(table, CacheDomain::Table); } mDoc->QueueCacheUpdate(this, CacheDomain::Table); } else if (aAttribute == nsGkAtoms::rowspan || aAttribute == nsGkAtoms::colspan) { - if (TableAccessible* table = Table()) { + if (HTMLTableAccessible* table = Table()) { // Modifying these attributes can also modify our table's classification // as either a layout or data table. Queue an update on the table itself // to re-compute our "layout guess" - mDoc->QueueCacheUpdate(table->AsAccessible(), CacheDomain::Table); + mDoc->QueueCacheUpdate(table, CacheDomain::Table); } mDoc->QueueCacheUpdate(this, CacheDomain::Table); } } //////////////////////////////////////////////////////////////////////////////// -// HTMLTableCellAccessible: TableCellAccessible implementation +// HTMLTableCellAccessible implementation -TableAccessible* HTMLTableCellAccessible::Table() const { +HTMLTableAccessible* HTMLTableCellAccessible::Table() const { LocalAccessible* parent = const_cast(this); while ((parent = parent->LocalParent())) { - if (parent->IsTable()) { - return parent->AsTable(); + if (parent->IsHTMLTable()) { + return HTMLTableAccessible::GetFrom(parent); } } return nullptr; } -uint32_t HTMLTableCellAccessible::ColIdx() const { - nsTableCellFrame* cellFrame = GetCellFrame(); - NS_ENSURE_TRUE(cellFrame, 0); - return cellFrame->ColIndex(); -} - -uint32_t HTMLTableCellAccessible::RowIdx() const { - nsTableCellFrame* cellFrame = GetCellFrame(); - NS_ENSURE_TRUE(cellFrame, 0); - return cellFrame->RowIndex(); -} - uint32_t HTMLTableCellAccessible::ColExtent() const { int32_t rowIdx = -1, colIdx = -1; if (NS_FAILED(GetCellIndexes(rowIdx, colIdx))) { - return 0; + // This probably isn't a table according to the layout engine; e.g. it has + // display: block. + return 1; } - TableAccessible* table = Table(); - NS_ASSERTION(table, "cell not in a table!"); - if (!table) { - return 0; + HTMLTableAccessible* table = Table(); + if (NS_WARN_IF(!table)) { + // This can happen where there is a inside a
such as + // in Monorail. + return 1; } return table->ColExtentAt(rowIdx, colIdx); @@ -207,72 +210,21 @@ uint32_t HTMLTableCellAccessible::RowExtent() const { int32_t rowIdx = -1, colIdx = -1; if (NS_FAILED(GetCellIndexes(rowIdx, colIdx))) { - return 0; + // This probably isn't a table according to the layout engine; e.g. it has + // display: block. + return 1; } - TableAccessible* table = Table(); - NS_ASSERTION(table, "cell not in atable!"); - if (!table) { - return 0; + HTMLTableAccessible* table = Table(); + if (NS_WARN_IF(!table)) { + // This can happen where there is a
inside a
such as + // in Monorail. + return 1; } return table->RowExtentAt(rowIdx, colIdx); } -void HTMLTableCellAccessible::ColHeaderCells(nsTArray* aCells) { - IDRefsIterator itr(mDoc, mContent, nsGkAtoms::headers); - while (LocalAccessible* cell = itr.Next()) { - a11y::role cellRole = cell->Role(); - if (cellRole == roles::COLUMNHEADER) { - aCells->AppendElement(cell); - } else if (cellRole != roles::ROWHEADER) { - // If referred table cell is at the same column then treat it as a column - // header. - TableCellAccessible* tableCell = cell->AsTableCell(); - if (tableCell && tableCell->ColIdx() == ColIdx()) { - aCells->AppendElement(cell); - } - } - } - - if (aCells->IsEmpty()) { - TableCellAccessible::ColHeaderCells(aCells); - } -} - -void HTMLTableCellAccessible::RowHeaderCells(nsTArray* aCells) { - IDRefsIterator itr(mDoc, mContent, nsGkAtoms::headers); - while (LocalAccessible* cell = itr.Next()) { - a11y::role cellRole = cell->Role(); - if (cellRole == roles::ROWHEADER) { - aCells->AppendElement(cell); - } else if (cellRole != roles::COLUMNHEADER) { - // If referred table cell is at the same row then treat it as a column - // header. - TableCellAccessible* tableCell = cell->AsTableCell(); - if (tableCell && tableCell->RowIdx() == RowIdx()) { - aCells->AppendElement(cell); - } - } - } - - if (aCells->IsEmpty()) { - TableCellAccessible::RowHeaderCells(aCells); - } -} - -bool HTMLTableCellAccessible::Selected() { - int32_t rowIdx = -1, colIdx = -1; - if (NS_FAILED(GetCellIndexes(rowIdx, colIdx))) { - return false; - } - - TableAccessible* table = Table(); - NS_ENSURE_TRUE(table, false); - - return table->IsCellSelected(rowIdx, colIdx); -} - //////////////////////////////////////////////////////////////////////////////// // HTMLTableCellAccessible: protected implementation @@ -280,10 +232,6 @@ return do_QueryFrame(mContent->GetPrimaryFrame()); } -nsTableCellFrame* HTMLTableCellAccessible::GetCellFrame() const { - return do_QueryFrame(mContent->GetPrimaryFrame()); -} - nsresult HTMLTableCellAccessible::GetCellIndexes(int32_t& aRowIdx, int32_t& aColIdx) const { nsITableCellLayout* cellLayout = GetCellLayout(); @@ -304,22 +252,74 @@ // HTMLTableHeaderCellAccessible: LocalAccessible implementation role HTMLTableHeaderCellAccessible::NativeRole() const { - return GetHeaderCellRole(this); + dom::Element* el = Elm(); + if (!el) { + return roles::NOTHING; + } + + // Check value of @scope attribute. + static mozilla::dom::Element::AttrValuesArray scopeValues[] = { + nsGkAtoms::col, nsGkAtoms::colgroup, nsGkAtoms::row, nsGkAtoms::rowgroup, + nullptr}; + int32_t valueIdx = el->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::scope, + scopeValues, eCaseMatters); + + switch (valueIdx) { + case 0: + case 1: + return roles::COLUMNHEADER; + case 2: + case 3: + return roles::ROWHEADER; + } + + dom::Element* nextEl = el->GetNextElementSibling(); + dom::Element* prevEl = el->GetPreviousElementSibling(); + // If this is the only cell in its row, it's a column header. + if (!nextEl && !prevEl) { + return roles::COLUMNHEADER; + } + const bool nextIsHeader = nextEl && nsCoreUtils::IsHTMLTableHeader(nextEl); + const bool prevIsHeader = prevEl && nsCoreUtils::IsHTMLTableHeader(prevEl); + // If this has a header on both sides, it is a column header. + if (prevIsHeader && nextIsHeader) { + return roles::COLUMNHEADER; + } + // If this has a header on one side and only a single normal cell on the + // other, it's a column header. + if (nextIsHeader && prevEl && !prevEl->GetPreviousElementSibling()) { + return roles::COLUMNHEADER; + } + if (prevIsHeader && nextEl && !nextEl->GetNextElementSibling()) { + return roles::COLUMNHEADER; + } + // If this has a normal cell next to it, it 's a row header. + if ((nextEl && !nextIsHeader) || (prevEl && !prevIsHeader)) { + return roles::ROWHEADER; + } + // If this has a row span, it could be a row header. + if (RowExtent() > 1) { + // It isn't a row header if it has 1 or more consecutive headers next to it. + if (prevIsHeader && + (!prevEl->GetPreviousElementSibling() || + nsCoreUtils::IsHTMLTableHeader(prevEl->GetPreviousElementSibling()))) { + return roles::COLUMNHEADER; + } + if (nextIsHeader && + (!nextEl->GetNextElementSibling() || + nsCoreUtils::IsHTMLTableHeader(nextEl->GetNextElementSibling()))) { + return roles::COLUMNHEADER; + } + return roles::ROWHEADER; + } + // Otherwise, assume it's a column header. + return roles::COLUMNHEADER; } //////////////////////////////////////////////////////////////////////////////// // HTMLTableRowAccessible //////////////////////////////////////////////////////////////////////////////// -role HTMLTableRowAccessible::NativeRole() const { - if (mContent->IsMathMLElement(nsGkAtoms::mtr_)) { - return roles::MATHML_TABLE_ROW; - } else if (mContent->IsMathMLElement(nsGkAtoms::mlabeledtr_)) { - return roles::MATHML_LABELED_ROW; - } - return roles::ROW; -} - // LocalAccessible protected ENameValueFlag HTMLTableRowAccessible::NativeName(nsString& aName) const { // For table row accessibles, we only want to calculate the name from the @@ -348,13 +348,6 @@ aChild->IsHTMLCaption() ? 0 : aIndex, aChild); } -role HTMLTableAccessible::NativeRole() const { - if (mContent->IsMathMLElement(nsGkAtoms::mtable_)) { - return roles::MATHML_TABLE; - } - return roles::TABLE; -} - uint64_t HTMLTableAccessible::NativeState() const { return LocalAccessible::NativeState() | states::READONLY; } @@ -450,14 +443,6 @@ : nullptr; } -void HTMLTableAccessible::Summary(nsString& aSummary) { - dom::HTMLTableElement* table = dom::HTMLTableElement::FromNode(mContent); - - if (table) { - table->GetSummary(aSummary); - } -} - uint32_t HTMLTableAccessible::ColCount() const { nsTableWrapperFrame* tableFrame = GetTableWrapperFrame(); return tableFrame ? tableFrame->GetColCount() : 0; @@ -468,374 +453,262 @@ return tableFrame ? tableFrame->GetRowCount() : 0; } -uint32_t HTMLTableAccessible::SelectedCellCount() { +uint32_t HTMLTableAccessible::ColExtentAt(uint32_t aRowIdx, uint32_t aColIdx) { nsTableWrapperFrame* tableFrame = GetTableWrapperFrame(); if (!tableFrame) { - return 0; + return 1; } - uint32_t count = 0, rowCount = RowCount(), colCount = ColCount(); - for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) { - for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) { - nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx); - if (!cellFrame || !cellFrame->IsSelected()) { - continue; - } - - uint32_t startRow = cellFrame->RowIndex(); - uint32_t startCol = cellFrame->ColIndex(); - if (startRow == rowIdx && startCol == colIdx) { - count++; - } - } - } - - return count; + return tableFrame->GetEffectiveColSpanAt(aRowIdx, aColIdx); } -uint32_t HTMLTableAccessible::SelectedColCount() { - uint32_t count = 0, colCount = ColCount(); - - for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) { - if (IsColSelected(colIdx)) { - count++; - } +uint32_t HTMLTableAccessible::RowExtentAt(uint32_t aRowIdx, uint32_t aColIdx) { + nsTableWrapperFrame* tableFrame = GetTableWrapperFrame(); + if (!tableFrame) { + return 1; } - return count; + return tableFrame->GetEffectiveRowSpanAt(aRowIdx, aColIdx); } -uint32_t HTMLTableAccessible::SelectedRowCount() { - uint32_t count = 0, rowCount = RowCount(); +bool HTMLTableAccessible::IsProbablyLayoutTable() { + // Implement a heuristic to determine if table is most likely used for layout. - for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) { - if (IsRowSelected(rowIdx)) { - count++; - } - } + // XXX do we want to look for rowspan or colspan, especialy that span all but + // a couple cells at the beginning or end of a row/col, and especially when + // they occur at the edge of a table? + + // XXX For now debugging descriptions are always on via SHOW_LAYOUT_HEURISTIC + // This will allow release trunk builds to be used by testers to refine + // the algorithm. Integrate it into Logging. + // Change to |#define SHOW_LAYOUT_HEURISTIC DEBUG| before final release +#ifdef SHOW_LAYOUT_HEURISTIC +# define RETURN_LAYOUT_ANSWER(isLayout, heuristic) \ + { \ + mLayoutHeuristic = isLayout \ + ? nsLiteralString(u"layout table: " heuristic) \ + : nsLiteralString(u"data table: " heuristic); \ + return isLayout; \ + } +#else +# define RETURN_LAYOUT_ANSWER(isLayout, heuristic) \ + { return isLayout; } +#endif - return count; -} + MOZ_ASSERT(!IsDefunct(), "Table accessible should not be defunct"); -void HTMLTableAccessible::SelectedCells(nsTArray* aCells) { - nsTableWrapperFrame* tableFrame = GetTableWrapperFrame(); - if (!tableFrame) { - return; + // Need to see all elements while document is being edited. + if (Document()->State() & states::EDITABLE) { + RETURN_LAYOUT_ANSWER(false, "In editable document"); } - uint32_t rowCount = RowCount(), colCount = ColCount(); - for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) { - for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) { - nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx); - if (!cellFrame || !cellFrame->IsSelected()) { - continue; - } - - uint32_t startRow = cellFrame->RowIndex(); - uint32_t startCol = cellFrame->ColIndex(); - if (startRow != rowIdx || startCol != colIdx) { - continue; - } - - LocalAccessible* cell = mDoc->GetAccessible(cellFrame->GetContent()); - aCells->AppendElement(cell); - } + // Check to see if an ARIA role overrides the role from native markup, + // but for which we still expose table semantics (treegrid, for example). + if (HasARIARole()) { + RETURN_LAYOUT_ANSWER(false, "Has role attribute"); } -} -void HTMLTableAccessible::SelectedCellIndices(nsTArray* aCells) { - nsTableWrapperFrame* tableFrame = GetTableWrapperFrame(); - if (!tableFrame) { - return; + dom::Element* el = Elm(); + if (el->IsMathMLElement(nsGkAtoms::mtable_)) { + RETURN_LAYOUT_ANSWER(false, "MathML matrix"); } - uint32_t rowCount = RowCount(), colCount = ColCount(); - for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) { - for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) { - nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx); - if (!cellFrame || !cellFrame->IsSelected()) { - continue; - } + MOZ_ASSERT(el->IsHTMLElement(nsGkAtoms::table), + "Table should not be built by CSS display:table style"); - uint32_t startCol = cellFrame->ColIndex(); - uint32_t startRow = cellFrame->RowIndex(); - if (startRow == rowIdx && startCol == colIdx) { - aCells->AppendElement(CellIndexAt(rowIdx, colIdx)); - } - } + // Check if datatable attribute has "0" value. + if (el->AttrValueIs(kNameSpaceID_None, nsGkAtoms::datatable, u"0"_ns, + eCaseMatters)) { + RETURN_LAYOUT_ANSWER(true, "Has datatable = 0 attribute, it's for layout"); } -} -void HTMLTableAccessible::SelectedColIndices(nsTArray* aCols) { - uint32_t colCount = ColCount(); - for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) { - if (IsColSelected(colIdx)) { - aCols->AppendElement(colIdx); - } + // Check for legitimate data table attributes. + if (el->Element::HasNonEmptyAttr(nsGkAtoms::summary)) { + RETURN_LAYOUT_ANSWER(false, "Has summary -- legitimate table structures"); } -} -void HTMLTableAccessible::SelectedRowIndices(nsTArray* aRows) { - uint32_t rowCount = RowCount(); - for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) { - if (IsRowSelected(rowIdx)) { - aRows->AppendElement(rowIdx); - } + // Check for legitimate data table elements. + LocalAccessible* caption = LocalFirstChild(); + if (caption && caption->IsHTMLCaption() && caption->HasChildren()) { + RETURN_LAYOUT_ANSWER(false, + "Not empty caption -- legitimate table structures"); } -} -LocalAccessible* HTMLTableAccessible::CellAt(uint32_t aRowIdx, - uint32_t aColIdx) { - nsTableWrapperFrame* tableFrame = GetTableWrapperFrame(); - if (!tableFrame) { - return nullptr; - } + for (nsIContent* childElm = el->GetFirstChild(); childElm; + childElm = childElm->GetNextSibling()) { + if (!childElm->IsHTMLElement()) continue; - nsIContent* cellContent = tableFrame->GetCellAt(aRowIdx, aColIdx); - LocalAccessible* cell = mDoc->GetAccessible(cellContent); + if (childElm->IsAnyOfHTMLElements(nsGkAtoms::col, nsGkAtoms::colgroup, + nsGkAtoms::tfoot, nsGkAtoms::thead)) { + RETURN_LAYOUT_ANSWER( + false, + "Has col, colgroup, tfoot or thead -- legitimate table structures"); + } - // Sometimes, the accessible returned here is a row accessible instead of - // a cell accessible, for example when a cell has CSS display:block; set. - // In such cases, iterate through the cells in this row differently to find - // it. - if (cell && cell->IsTableRow()) { - return CellInRowAt(cell, aColIdx); - } + if (childElm->IsHTMLElement(nsGkAtoms::tbody)) { + for (nsIContent* rowElm = childElm->GetFirstChild(); rowElm; + rowElm = rowElm->GetNextSibling()) { + if (rowElm->IsHTMLElement(nsGkAtoms::tr)) { + if (LocalAccessible* row = Document()->GetAccessible(rowElm)) { + if (const nsRoleMapEntry* roleMapEntry = row->ARIARoleMap()) { + if (roleMapEntry->role != roles::ROW) { + RETURN_LAYOUT_ANSWER(true, "Repurposed tr with different role"); + } + } + } - // XXX bug 576838: bizarre tables (like table6 in tables/test_table2.html) may - // return itself as a cell what makes Orca hang. - return cell == this ? nullptr : cell; -} + for (nsIContent* cellElm = rowElm->GetFirstChild(); cellElm; + cellElm = cellElm->GetNextSibling()) { + if (cellElm->IsHTMLElement()) { + if (cellElm->NodeInfo()->Equals(nsGkAtoms::th)) { + RETURN_LAYOUT_ANSWER(false, + "Has th -- legitimate table structures"); + } -int32_t HTMLTableAccessible::CellIndexAt(uint32_t aRowIdx, uint32_t aColIdx) { - nsTableWrapperFrame* tableFrame = GetTableWrapperFrame(); - if (!tableFrame) { - return -1; - } + if (cellElm->AsElement()->HasAttr(kNameSpaceID_None, + nsGkAtoms::headers) || + cellElm->AsElement()->HasAttr(kNameSpaceID_None, + nsGkAtoms::scope) || + cellElm->AsElement()->HasAttr(kNameSpaceID_None, + nsGkAtoms::abbr)) { + RETURN_LAYOUT_ANSWER(false, + "Has headers, scope, or abbr attribute -- " + "legitimate table structures"); + } - int32_t cellIndex = tableFrame->GetIndexByRowAndColumn(aRowIdx, aColIdx); - if (cellIndex == -1) { - // Sometimes, the accessible returned here is a row accessible instead of - // a cell accessible, for example when a cell has CSS display:block; set. - // In such cases, iterate through the cells in this row differently to find - // it. - nsIContent* cellContent = tableFrame->GetCellAt(aRowIdx, aColIdx); - LocalAccessible* cell = mDoc->GetAccessible(cellContent); - if (cell && cell->IsTableRow()) { - return TableAccessible::CellIndexAt(aRowIdx, aColIdx); + if (LocalAccessible* cell = Document()->GetAccessible(cellElm)) { + if (const nsRoleMapEntry* roleMapEntry = cell->ARIARoleMap()) { + if (roleMapEntry->role != roles::CELL && + roleMapEntry->role != roles::COLUMNHEADER && + roleMapEntry->role != roles::ROWHEADER && + roleMapEntry->role != roles::GRID_CELL) { + RETURN_LAYOUT_ANSWER(true, + "Repurposed cell with different role"); + } + } + if (cell->ChildCount() == 1 && + cell->LocalFirstChild()->IsAbbreviation()) { + RETURN_LAYOUT_ANSWER( + false, "has abbr -- legitimate table structures"); + } + } + } + } + } + } } } - return cellIndex; -} - -int32_t HTMLTableAccessible::ColIndexAt(uint32_t aCellIdx) { - nsTableWrapperFrame* tableFrame = GetTableWrapperFrame(); - if (!tableFrame) { - return -1; - } - - int32_t rowIdx = -1, colIdx = -1; - tableFrame->GetRowAndColumnByIndex(aCellIdx, &rowIdx, &colIdx); - - if (colIdx == -1) { - // Sometimes, the index returned indicates that this is not a regular - // cell, for example when a cell has CSS display:block; set. - // In such cases, try the super class method to find it. - return TableAccessible::ColIndexAt(aCellIdx); + // Check for nested tables. + nsCOMPtr nestedTables = + el->GetElementsByTagName(u"table"_ns); + if (nestedTables->Length() > 0) { + RETURN_LAYOUT_ANSWER(true, "Has a nested table within it"); } - return colIdx; -} - -int32_t HTMLTableAccessible::RowIndexAt(uint32_t aCellIdx) { - nsTableWrapperFrame* tableFrame = GetTableWrapperFrame(); - if (!tableFrame) { - return -1; + // If only 1 column or only 1 row, it's for layout. + auto colCount = ColCount(); + if (colCount <= 1) { + RETURN_LAYOUT_ANSWER(true, "Has only 1 column"); } - - int32_t rowIdx = -1, colIdx = -1; - tableFrame->GetRowAndColumnByIndex(aCellIdx, &rowIdx, &colIdx); - - if (rowIdx == -1) { - // Sometimes, the index returned indicates that this is not a regular - // cell, for example when a cell has CSS display:block; set. - // In such cases, try the super class method to find it. - return TableAccessible::RowIndexAt(aCellIdx); + auto rowCount = RowCount(); + if (rowCount <= 1) { + RETURN_LAYOUT_ANSWER(true, "Has only 1 row"); } - return rowIdx; -} - -void HTMLTableAccessible::RowAndColIndicesAt(uint32_t aCellIdx, - int32_t* aRowIdx, - int32_t* aColIdx) { - nsTableWrapperFrame* tableFrame = GetTableWrapperFrame(); - if (tableFrame) { - tableFrame->GetRowAndColumnByIndex(aCellIdx, aRowIdx, aColIdx); - if (*aRowIdx == -1 || *aColIdx == -1) { - // Sometimes, the index returned indicates that this is not a regular - // cell, for example when a cell has CSS display:block; set. - // In such cases, try the super class method to find it. - TableAccessible::RowAndColIndicesAt(aCellIdx, aRowIdx, aColIdx); - } + // Check for many columns. + if (colCount >= 5) { + RETURN_LAYOUT_ANSWER(false, ">=5 columns"); } -} -uint32_t HTMLTableAccessible::ColExtentAt(uint32_t aRowIdx, uint32_t aColIdx) { - nsTableWrapperFrame* tableFrame = GetTableWrapperFrame(); + // Now we know there are 2-4 columns and 2 or more rows. Check to see if + // there are visible borders on the cells. + // XXX currently, we just check the first cell -- do we really need to do + // more? + nsTableWrapperFrame* tableFrame = do_QueryFrame(el->GetPrimaryFrame()); if (!tableFrame) { - return 0; + RETURN_LAYOUT_ANSWER(false, "table with no frame!"); } - uint32_t colExtent = tableFrame->GetEffectiveColSpanAt(aRowIdx, aColIdx); - if (colExtent == 0) { - nsIContent* cellContent = tableFrame->GetCellAt(aRowIdx, aColIdx); - LocalAccessible* cell = mDoc->GetAccessible(cellContent); - if (cell && cell->IsTableRow()) { - return TableAccessible::ColExtentAt(aRowIdx, aColIdx); - } + nsIFrame* cellFrame = tableFrame->GetCellFrameAt(0, 0); + if (!cellFrame) { + RETURN_LAYOUT_ANSWER(false, "table's first cell has no frame!"); } - return colExtent; -} - -uint32_t HTMLTableAccessible::RowExtentAt(uint32_t aRowIdx, uint32_t aColIdx) { - nsTableWrapperFrame* tableFrame = GetTableWrapperFrame(); - if (!tableFrame) { - return 0; + nsMargin border = cellFrame->StyleBorder()->GetComputedBorder(); + if (border.top && border.bottom && border.left && border.right) { + RETURN_LAYOUT_ANSWER(false, "Has nonzero border-width on table cell"); } - return tableFrame->GetEffectiveRowSpanAt(aRowIdx, aColIdx); -} + // Rules for non-bordered tables with 2-4 columns and 2+ rows from here on + // forward. -bool HTMLTableAccessible::IsColSelected(uint32_t aColIdx) { - bool isSelected = false; - - uint32_t rowCount = RowCount(); - for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) { - isSelected = IsCellSelected(rowIdx, aColIdx); - if (!isSelected) { - return false; - } - } - - return isSelected; -} + // Check for styled background color across rows (alternating background + // color is a common feature for data tables). + auto childCount = ChildCount(); + nscolor rowColor = 0; + nscolor prevRowColor; + for (auto childIdx = 0U; childIdx < childCount; childIdx++) { + LocalAccessible* child = LocalChildAt(childIdx); + if (child->IsHTMLTableRow()) { + prevRowColor = rowColor; + nsIFrame* rowFrame = child->GetFrame(); + MOZ_ASSERT(rowFrame, "Table hierarchy got screwed up"); + if (!rowFrame) { + RETURN_LAYOUT_ANSWER(false, "Unexpected table hierarchy"); + } -bool HTMLTableAccessible::IsRowSelected(uint32_t aRowIdx) { - bool isSelected = false; + rowColor = rowFrame->StyleBackground()->BackgroundColor(rowFrame); - uint32_t colCount = ColCount(); - for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) { - isSelected = IsCellSelected(aRowIdx, colIdx); - if (!isSelected) { - return false; + if (childIdx > 0 && prevRowColor != rowColor) { + RETURN_LAYOUT_ANSWER(false, + "2 styles of row background color, non-bordered"); + } } } - return isSelected; -} - -bool HTMLTableAccessible::IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx) { - nsTableWrapperFrame* tableFrame = GetTableWrapperFrame(); - if (!tableFrame) { - return false; + // Check for many rows. + const uint32_t kMaxLayoutRows = 20; + if (rowCount > kMaxLayoutRows) { // A ton of rows, this is probably for data + RETURN_LAYOUT_ANSWER(false, ">= kMaxLayoutRows (20) and non-bordered"); } - nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(aRowIdx, aColIdx); - return cellFrame ? cellFrame->IsSelected() : false; -} - -void HTMLTableAccessible::SelectRow(uint32_t aRowIdx) { - DebugOnly rv = - RemoveRowsOrColumnsFromSelection(aRowIdx, TableSelectionMode::Row, true); - NS_ASSERTION(NS_SUCCEEDED(rv), - "RemoveRowsOrColumnsFromSelection() Shouldn't fail!"); - - AddRowOrColumnToSelection(aRowIdx, TableSelectionMode::Row); -} - -void HTMLTableAccessible::SelectCol(uint32_t aColIdx) { - DebugOnly rv = RemoveRowsOrColumnsFromSelection( - aColIdx, TableSelectionMode::Column, true); - NS_ASSERTION(NS_SUCCEEDED(rv), - "RemoveRowsOrColumnsFromSelection() Shouldn't fail!"); - - AddRowOrColumnToSelection(aColIdx, TableSelectionMode::Column); -} - -void HTMLTableAccessible::UnselectRow(uint32_t aRowIdx) { - RemoveRowsOrColumnsFromSelection(aRowIdx, TableSelectionMode::Row, false); -} - -void HTMLTableAccessible::UnselectCol(uint32_t aColIdx) { - RemoveRowsOrColumnsFromSelection(aColIdx, TableSelectionMode::Column, false); -} - -//////////////////////////////////////////////////////////////////////////////// -// HTMLTableAccessible: protected implementation - -nsresult HTMLTableAccessible::AddRowOrColumnToSelection( - int32_t aIndex, TableSelectionMode aTarget) { - bool doSelectRow = (aTarget == TableSelectionMode::Row); - - nsTableWrapperFrame* tableFrame = GetTableWrapperFrame(); - if (!tableFrame) { - return NS_OK; + // Check for very wide table. + nsIFrame* documentFrame = Document()->GetFrame(); + nsSize documentSize = documentFrame->GetSize(); + if (documentSize.width > 0) { + nsSize tableSize = GetFrame()->GetSize(); + int32_t percentageOfDocWidth = (100 * tableSize.width) / documentSize.width; + if (percentageOfDocWidth > 95) { + // 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width + // Probably for layout + RETURN_LAYOUT_ANSWER( + true, "<= 4 columns, table width is 95% of document width"); + } } - uint32_t count = 0; - if (doSelectRow) { - count = ColCount(); - } else { - count = RowCount(); + // Two column rules. + if (rowCount * colCount <= 10) { + RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered"); } - PresShell* presShell = mDoc->PresShellPtr(); - RefPtr tableSelection = - const_cast(presShell->ConstFrameSelection()); - - for (uint32_t idx = 0; idx < count; idx++) { - int32_t rowIdx = doSelectRow ? aIndex : idx; - int32_t colIdx = doSelectRow ? idx : aIndex; - nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx); - if (cellFrame && !cellFrame->IsSelected()) { - nsresult rv = tableSelection->SelectCellElement(cellFrame->GetContent()); - NS_ENSURE_SUCCESS(rv, rv); + static const nsLiteralString tags[] = {u"embed"_ns, u"object"_ns, + u"iframe"_ns}; + for (const auto& tag : tags) { + nsCOMPtr descendants = el->GetElementsByTagName(tag); + if (descendants->Length() > 0) { + RETURN_LAYOUT_ANSWER(true, + "Has no borders, and has iframe, object or embed, " + "typical of advertisements"); } } - return NS_OK; + RETURN_LAYOUT_ANSWER(false, + "No layout factor strong enough, so will guess data"); } -nsresult HTMLTableAccessible::RemoveRowsOrColumnsFromSelection( - int32_t aIndex, TableSelectionMode aTarget, bool aIsOuter) { - nsTableWrapperFrame* tableFrame = GetTableWrapperFrame(); - if (!tableFrame) { - return NS_OK; - } - - PresShell* presShell = mDoc->PresShellPtr(); - RefPtr tableSelection = - const_cast(presShell->ConstFrameSelection()); - - bool doUnselectRow = (aTarget == TableSelectionMode::Row); - uint32_t count = doUnselectRow ? ColCount() : RowCount(); - - int32_t startRowIdx = doUnselectRow ? aIndex : 0; - int32_t endRowIdx = doUnselectRow ? aIndex : count - 1; - int32_t startColIdx = doUnselectRow ? 0 : aIndex; - int32_t endColIdx = doUnselectRow ? count - 1 : aIndex; - - if (aIsOuter) { - return tableSelection->RestrictCellsToSelection( - mContent, startRowIdx, startColIdx, endRowIdx, endColIdx); - } - - return tableSelection->RemoveCellsFromSelection( - mContent, startRowIdx, startColIdx, endRowIdx, endColIdx); -} +//////////////////////////////////////////////////////////////////////////////// +// HTMLTableAccessible: protected implementation void HTMLTableAccessible::Description(nsString& aDescription) const { // Helpful for debugging layout vs. data tables diff -Nru firefox-esr-115.3.1esr+build1/accessible/html/HTMLTableAccessible.h firefox-esr-115.4.0esr+build1/accessible/html/HTMLTableAccessible.h --- firefox-esr-115.3.1esr+build1/accessible/html/HTMLTableAccessible.h 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/html/HTMLTableAccessible.h 2023-10-17 05:45:44.000000000 +0000 @@ -7,8 +7,6 @@ #define mozilla_a11y_HTMLTableAccessible_h__ #include "HyperTextAccessibleWrap.h" -#include "TableAccessible.h" -#include "TableCellAccessible.h" class nsITableCellLayout; class nsTableCellFrame; @@ -16,15 +14,14 @@ namespace mozilla { -enum class TableSelectionMode : uint32_t; - namespace a11y { +class HTMLTableAccessible; + /** * HTML table cell accessible (html:td). */ -class HTMLTableCellAccessible : public HyperTextAccessibleWrap, - public TableCellAccessible { +class HTMLTableCellAccessible : public HyperTextAccessibleWrap { public: HTMLTableCellAccessible(nsIContent* aContent, DocAccessible* aDoc); @@ -33,7 +30,6 @@ HyperTextAccessibleWrap) // LocalAccessible - virtual TableCellAccessible* AsTableCell() override { return this; } virtual a11y::role NativeRole() const override; virtual uint64_t NativeState() const override; virtual uint64_t NativeInteractiveState() const override; @@ -44,16 +40,18 @@ int32_t aModType, const nsAttrValue* aOldValue, uint64_t aOldState) override; - // TableCellAccessible + // HTMLTableCellAccessible public: - virtual TableAccessible* Table() const override; - virtual uint32_t ColIdx() const override; - virtual uint32_t RowIdx() const override; - virtual uint32_t ColExtent() const override; - virtual uint32_t RowExtent() const override; - virtual void ColHeaderCells(nsTArray* aCells) override; - virtual void RowHeaderCells(nsTArray* aCells) override; - virtual bool Selected() override; + HTMLTableAccessible* Table() const; + uint32_t ColExtent() const; + uint32_t RowExtent() const; + + static HTMLTableCellAccessible* GetFrom(LocalAccessible* aAcc) { + if (aAcc->IsHTMLTableCell()) { + return static_cast(aAcc); + } + return nullptr; + } protected: virtual ~HTMLTableCellAccessible() {} @@ -64,11 +62,6 @@ nsITableCellLayout* GetCellLayout() const; /** - * Return the table cell frame. - */ - nsTableCellFrame* GetCellFrame() const; - - /** * Return row and column indices of the cell. */ nsresult GetCellIndexes(int32_t& aRowIdx, int32_t& aColIdx) const; @@ -99,9 +92,6 @@ NS_INLINE_DECL_REFCOUNTING_INHERITED(HTMLTableRowAccessible, HyperTextAccessibleWrap) - // LocalAccessible - virtual a11y::role NativeRole() const override; - protected: virtual ~HTMLTableRowAccessible() {} @@ -118,8 +108,7 @@ // data vs. layout heuristic // #define SHOW_LAYOUT_HEURISTIC -class HTMLTableAccessible : public HyperTextAccessibleWrap, - public TableAccessible { +class HTMLTableAccessible : public HyperTextAccessibleWrap { public: HTMLTableAccessible(nsIContent* aContent, DocAccessible* aDoc) : HyperTextAccessibleWrap(aContent, aDoc) { @@ -130,40 +119,23 @@ NS_INLINE_DECL_REFCOUNTING_INHERITED(HTMLTableAccessible, HyperTextAccessibleWrap) - // TableAccessible - virtual LocalAccessible* Caption() const override; - virtual void Summary(nsString& aSummary) override; - virtual uint32_t ColCount() const override; - virtual uint32_t RowCount() override; - virtual LocalAccessible* CellAt(uint32_t aRowIndex, - uint32_t aColumnIndex) override; - virtual int32_t CellIndexAt(uint32_t aRowIdx, uint32_t aColIdx) override; - virtual int32_t ColIndexAt(uint32_t aCellIdx) override; - virtual int32_t RowIndexAt(uint32_t aCellIdx) override; - virtual void RowAndColIndicesAt(uint32_t aCellIdx, int32_t* aRowIdx, - int32_t* aColIdx) override; - virtual uint32_t ColExtentAt(uint32_t aRowIdx, uint32_t aColIdx) override; - virtual uint32_t RowExtentAt(uint32_t aRowIdx, uint32_t aColIdx) override; - virtual bool IsColSelected(uint32_t aColIdx) override; - virtual bool IsRowSelected(uint32_t aRowIdx) override; - virtual bool IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx) override; - virtual uint32_t SelectedCellCount() override; - virtual uint32_t SelectedColCount() override; - virtual uint32_t SelectedRowCount() override; - virtual void SelectedCells(nsTArray* aCells) override; - virtual void SelectedCellIndices(nsTArray* aCells) override; - virtual void SelectedColIndices(nsTArray* aCols) override; - virtual void SelectedRowIndices(nsTArray* aRows) override; - virtual void SelectCol(uint32_t aColIdx) override; - virtual void SelectRow(uint32_t aRowIdx) override; - virtual void UnselectCol(uint32_t aColIdx) override; - virtual void UnselectRow(uint32_t aRowIdx) override; - virtual LocalAccessible* AsAccessible() override { return this; } + // HTMLTableAccessible + LocalAccessible* Caption() const; + uint32_t ColCount() const; + uint32_t RowCount(); + uint32_t ColExtentAt(uint32_t aRowIdx, uint32_t aColIdx); + uint32_t RowExtentAt(uint32_t aRowIdx, uint32_t aColIdx); + bool IsProbablyLayoutTable(); + + static HTMLTableAccessible* GetFrom(LocalAccessible* aAcc) { + if (aAcc->IsHTMLTable()) { + return static_cast(aAcc); + } + return nullptr; + } // LocalAccessible - virtual TableAccessible* AsTable() override { return this; } virtual void Description(nsString& aDescription) const override; - virtual a11y::role NativeRole() const override; virtual uint64_t NativeState() const override; virtual already_AddRefed NativeAttributes() override; virtual Relation RelationByType(RelationType aRelationType) const override; @@ -183,29 +155,6 @@ // HTMLTableAccessible - /** - * Add row or column to selection. - * - * @param aIndex [in] index of row or column to be selected - * @param aTarget [in] indicates what should be selected, either row or - * column (see nsFrameSelection) - */ - nsresult AddRowOrColumnToSelection(int32_t aIndex, - TableSelectionMode aTarget); - - /** - * Removes rows or columns at the given index or outside it from selection. - * - * @param aIndex [in] row or column index - * @param aTarget [in] indicates whether row or column should unselected - * @param aIsOuter [in] indicates whether all rows or column excepting - * the given one should be unselected or the given one - * should be unselected only - */ - nsresult RemoveRowsOrColumnsFromSelection(int32_t aIndex, - TableSelectionMode aTarget, - bool aIsOuter); - #ifdef SHOW_LAYOUT_HEURISTIC nsString mLayoutHeuristic; #endif diff -Nru firefox-esr-115.3.1esr+build1/accessible/interfaces/nsIAccessibleTable.idl firefox-esr-115.4.0esr+build1/accessible/interfaces/nsIAccessibleTable.idl --- firefox-esr-115.3.1esr+build1/accessible/interfaces/nsIAccessibleTable.idl 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/interfaces/nsIAccessibleTable.idl 2023-10-17 05:45:43.000000000 +0000 @@ -181,34 +181,6 @@ Array getSelectedRowIndices(); /** - * Select a row and unselects all previously selected rows. - * - * @param rowIndex [in] the row index to select - */ - void selectRow(in long rowIndex); - - /** - * Select a column and unselects all previously selected columns. - * - * @param columnIndex [in] the column index to select - */ - void selectColumn(in long columnIndex); - - /** - * Unselect the given row, leaving other selected rows selected (if any). - * - * @param rowIndex [in] the row index to select - */ - void unselectRow(in long rowIndex); - - /** - * Unselect the given column, leaving other selected columns selected (if any). - * - * @param columnIndex [in] the column index to select - */ - void unselectColumn(in long columnIndex); - - /** * Use heuristics to determine if table is most likely used for layout. */ boolean isProbablyForLayout(); diff -Nru firefox-esr-115.3.1esr+build1/accessible/ipc/RemoteAccessibleBase.cpp firefox-esr-115.4.0esr+build1/accessible/ipc/RemoteAccessibleBase.cpp --- firefox-esr-115.3.1esr+build1/accessible/ipc/RemoteAccessibleBase.cpp 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/ipc/RemoteAccessibleBase.cpp 2023-10-17 05:45:44.000000000 +0000 @@ -7,12 +7,15 @@ #include "ARIAMap.h" #include "CachedTableAccessible.h" #include "DocAccessible.h" +#include "RemoteAccessibleBase.h" #include "mozilla/a11y/DocAccessibleParent.h" #include "mozilla/a11y/DocManager.h" #include "mozilla/a11y/Platform.h" #include "mozilla/a11y/RemoteAccessibleBase.h" #include "mozilla/a11y/RemoteAccessible.h" #include "mozilla/a11y/Role.h" +#include "mozilla/a11y/TableAccessible.h" +#include "mozilla/a11y/TableCellAccessible.h" #include "mozilla/BinarySearch.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/BrowserParent.h" @@ -412,6 +415,60 @@ } template +RemoteAccessible* RemoteAccessibleBase::DoFuzzyHittesting() { + uint32_t childCount = ChildCount(); + if (!childCount) { + return nullptr; + } + // Check if this match has a clipped child. + // This usually indicates invisible text, and we're + // interested in returning the inner text content + // even if it doesn't contain the point we're hittesting. + RemoteAccessible* clippedContainer = nullptr; + for (uint32_t i = 0; i < childCount; i++) { + RemoteAccessible* child = RemoteChildAt(i); + if (child->Role() == roles::TEXT_CONTAINER) { + if (child->IsClipped()) { + clippedContainer = child; + break; + } + } + } + // If we found a clipped container, descend it in search of + // meaningful text leaves. Ignore non-text-leaf/text-container + // siblings. + RemoteAccessible* container = clippedContainer; + while (container) { + RemoteAccessible* textLeaf = nullptr; + bool continueSearch = false; + childCount = container->ChildCount(); + for (uint32_t i = 0; i < childCount; i++) { + RemoteAccessible* child = container->RemoteChildAt(i); + if (child->Role() == roles::TEXT_CONTAINER) { + container = child; + continueSearch = true; + break; + } + if (child->IsTextLeaf()) { + textLeaf = child; + // Don't break here -- it's possible a text container + // exists as another sibling, and we should descend as + // deep as possible. + } + } + if (textLeaf) { + return textLeaf; + } + if (!continueSearch) { + // We didn't find anything useful in this set of siblings. + // Don't keep searching + break; + } + } + return nullptr; +} + +template Accessible* RemoteAccessibleBase::ChildAtPoint( int32_t aX, int32_t aY, LocalAccessible::EWhichChildAtPoint aWhichChild) { // Elements that are partially on-screen should have their bounds masked by @@ -484,6 +541,13 @@ // this call shouldn't pass the boundary defined by // the acc this call originated on. If we hit `this`, // return our most recent match. + if (!lastMatch && + BoundsWithOffset(Nothing(), hitTesting).Contains(aX, aY)) { + // If we haven't found a match, but `this` contains the point we're + // looking for, set it as our temp last match so we can + // (potentially) do fuzzy hittesting on it below. + lastMatch = acc; + } break; } @@ -495,6 +559,10 @@ break; } } + if (lastMatch) { + RemoteAccessible* fuzzyMatch = lastMatch->DoFuzzyHittesting(); + lastMatch = fuzzyMatch ? fuzzyMatch : lastMatch; + } } } @@ -653,6 +721,27 @@ } template +bool RemoteAccessibleBase::IsOverflowHidden() const { + MOZ_ASSERT(mCachedFields); + if (auto maybeOverflow = + mCachedFields->GetAttribute>(nsGkAtoms::overflow)) { + return *maybeOverflow == nsGkAtoms::hidden; + } + + return false; +} + +template +bool RemoteAccessibleBase::IsClipped() const { + MOZ_ASSERT(mCachedFields); + if (mCachedFields->GetAttribute(nsGkAtoms::clip_rule)) { + return true; + } + + return false; +} + +template LayoutDeviceIntRect RemoteAccessibleBase::BoundsWithOffset( Maybe aOffset, bool aBoundsAreForHittesting) const { Maybe maybeBounds = RetrieveCachedBounds(); @@ -728,11 +817,12 @@ // that the bounds we've calculated so far are constrained to the // bounds of the scroll area. Without this, we'll "hit" the off-screen // portions of accs that are are partially (but not fully) within the - // scroll area. - if (aBoundsAreForHittesting && hasScrollArea) { - nsRect selfRelativeScrollBounds(0, 0, remoteBounds.width, - remoteBounds.height); - bounds = bounds.SafeIntersect(selfRelativeScrollBounds); + // scroll area. This is also a problem for accs with overflow:hidden; + if (aBoundsAreForHittesting && + (hasScrollArea || remoteAcc->IsOverflowHidden())) { + nsRect selfRelativeVisibleBounds(0, 0, remoteBounds.width, + remoteBounds.height); + bounds = bounds.SafeIntersect(selfRelativeVisibleBounds); } } if (remoteAcc->IsDoc()) { @@ -1386,8 +1476,8 @@ attributes->SetAttribute(nsGkAtoms::display, display); } - if (TableCellAccessibleBase* cell = AsTableCellBase()) { - TableAccessibleBase* table = cell->Table(); + if (TableCellAccessible* cell = AsTableCell()) { + TableAccessible* table = cell->Table(); uint32_t row = cell->RowIdx(); uint32_t col = cell->ColIdx(); int32_t cellIdx = table->CellIndexAt(row, col); @@ -1899,7 +1989,7 @@ } template -TableAccessibleBase* RemoteAccessibleBase::AsTableBase() { +TableAccessible* RemoteAccessibleBase::AsTable() { if (IsTable()) { return CachedTableAccessible::GetFrom(this); } @@ -1907,7 +1997,7 @@ } template -TableCellAccessibleBase* RemoteAccessibleBase::AsTableCellBase() { +TableCellAccessible* RemoteAccessibleBase::AsTableCell() { if (IsTableCell()) { return CachedTableCellAccessible::GetFrom(this); } diff -Nru firefox-esr-115.3.1esr+build1/accessible/ipc/RemoteAccessibleBase.h firefox-esr-115.4.0esr+build1/accessible/ipc/RemoteAccessibleBase.h --- firefox-esr-115.3.1esr+build1/accessible/ipc/RemoteAccessibleBase.h 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/ipc/RemoteAccessibleBase.h 2023-10-17 05:45:43.000000000 +0000 @@ -393,8 +393,8 @@ : nullptr; } - virtual TableAccessibleBase* AsTableBase() override; - virtual TableCellAccessibleBase* AsTableCellBase() override; + virtual TableAccessible* AsTable() override; + virtual TableCellAccessible* AsTableCell() override; virtual void DOMNodeID(nsString& aID) const override; @@ -452,6 +452,24 @@ LayoutDeviceIntRect BoundsWithOffset( Maybe aOffset, bool aBoundsAreForHittesting = false) const; bool IsFixedPos() const; + bool IsOverflowHidden() const; + + /** + * Returns true if an accessible's frame has no scrollable overflow, and + * false otherwise. + * Does not return true for partially clipped accessibles. + */ + bool IsClipped() const; + + /** + * Checks if our hittesting match has any clipped children and, if so + * descends it and subsequent TEXT_CONTAINERs in search of a text leaf. + * We do this because some sites use clipping to hide text that is only + * visible to a11y, while displaying a visual version of the same text on + * the web page. We want a hittest of the visible text to resolve to the + * hidden, a11y-only text node. + */ + RemoteAccessible* DoFuzzyHittesting(); // This function is used exclusively for hit testing. bool ContainsPoint(int32_t aX, int32_t aY); diff -Nru firefox-esr-115.3.1esr+build1/accessible/mac/mozAccessible.mm firefox-esr-115.4.0esr+build1/accessible/mac/mozAccessible.mm --- firefox-esr-115.3.1esr+build1/accessible/mac/mozAccessible.mm 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/mac/mozAccessible.mm 2023-10-17 05:45:44.000000000 +0000 @@ -22,7 +22,6 @@ #include "Relation.h" #include "Role.h" #include "RootAccessible.h" -#include "TableAccessible.h" #include "mozilla/a11y/PDocAccessible.h" #include "mozilla/dom/BrowserParent.h" #include "OuterDocAccessible.h" diff -Nru firefox-esr-115.3.1esr+build1/accessible/mac/mozTableAccessible.mm firefox-esr-115.4.0esr+build1/accessible/mac/mozTableAccessible.mm --- firefox-esr-115.3.1esr+build1/accessible/mac/mozTableAccessible.mm 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/mac/mozTableAccessible.mm 2023-10-17 05:45:44.000000000 +0000 @@ -11,8 +11,8 @@ #include "AccIterator.h" #include "LocalAccessible.h" -#include "mozilla/a11y/TableAccessibleBase.h" -#include "mozilla/a11y/TableCellAccessibleBase.h" +#include "mozilla/a11y/TableAccessible.h" +#include "mozilla/a11y/TableCellAccessible.h" #include "nsAccessibilityService.h" #include "XULTreeAccessible.h" #include "Pivot.h" @@ -48,7 +48,7 @@ mChildren = [[NSMutableArray alloc] init]; - TableAccessibleBase* table = [mParent geckoAccessible]->AsTableBase(); + TableAccessible* table = [mParent geckoAccessible]->AsTable(); MOZ_ASSERT(table, "Got null table when fetching column children!"); uint32_t numRows = table->RowCount(); @@ -137,7 +137,7 @@ } // For LocalAccessible and cached RemoteAccessible, we could use - // AsTableBase()->IsProbablyLayoutTable(). However, if the cache is enabled, + // AsTable()->IsProbablyLayoutTable(). However, if the cache is enabled, // that would build the table cache, which is pointless for layout tables on // Mac because layout tables are AXGroups and do not expose table properties // like AXRows, AXColumns, etc. @@ -174,13 +174,13 @@ - (NSNumber*)moxRowCount { MOZ_ASSERT(mGeckoAccessible); - return @(mGeckoAccessible->AsTableBase()->RowCount()); + return @(mGeckoAccessible->AsTable()->RowCount()); } - (NSNumber*)moxColumnCount { MOZ_ASSERT(mGeckoAccessible); - return @(mGeckoAccessible->AsTableBase()->ColCount()); + return @(mGeckoAccessible->AsTable()->ColCount()); } - (NSArray*)moxRows { @@ -221,7 +221,7 @@ mColContainers = [[NSMutableArray alloc] init]; uint32_t numCols = 0; - numCols = mGeckoAccessible->AsTableBase()->ColCount(); + numCols = mGeckoAccessible->AsTable()->ColCount(); for (uint32_t i = 0; i < numCols; i++) { mozColumnContainer* container = [[mozColumnContainer alloc] initWithIndex:i andParent:self]; @@ -244,9 +244,9 @@ MOZ_ASSERT(mGeckoAccessible); uint32_t numCols = 0; - TableAccessibleBase* table = nullptr; + TableAccessible* table = nullptr; - table = mGeckoAccessible->AsTableBase(); + table = mGeckoAccessible->AsTable(); numCols = table->ColCount(); NSMutableArray* colHeaders = [[[NSMutableArray alloc] initWithCapacity:numCols] autorelease]; @@ -272,7 +272,7 @@ MOZ_ASSERT(mGeckoAccessible); - Accessible* cell = mGeckoAccessible->AsTableBase()->CellAt(row, col); + Accessible* cell = mGeckoAccessible->AsTable()->CellAt(row, col); if (!cell) { return nil; } @@ -338,7 +338,7 @@ - (NSValue*)moxRowIndexRange { MOZ_ASSERT(mGeckoAccessible); - TableCellAccessibleBase* cell = mGeckoAccessible->AsTableCellBase(); + TableCellAccessible* cell = mGeckoAccessible->AsTableCell(); return [NSValue valueWithRange:NSMakeRange(cell->RowIdx(), cell->RowExtent())]; } @@ -346,7 +346,7 @@ - (NSValue*)moxColumnIndexRange { MOZ_ASSERT(mGeckoAccessible); - TableCellAccessibleBase* cell = mGeckoAccessible->AsTableCellBase(); + TableCellAccessible* cell = mGeckoAccessible->AsTableCell(); return [NSValue valueWithRange:NSMakeRange(cell->ColIdx(), cell->ColExtent())]; } @@ -354,7 +354,7 @@ - (NSArray*)moxRowHeaderUIElements { MOZ_ASSERT(mGeckoAccessible); - TableCellAccessibleBase* cell = mGeckoAccessible->AsTableCellBase(); + TableCellAccessible* cell = mGeckoAccessible->AsTableCell(); AutoTArray headerCells; if (cell) { cell->RowHeaderCells(&headerCells); @@ -365,7 +365,7 @@ - (NSArray*)moxColumnHeaderUIElements { MOZ_ASSERT(mGeckoAccessible); - TableCellAccessibleBase* cell = mGeckoAccessible->AsTableCellBase(); + TableCellAccessible* cell = mGeckoAccessible->AsTableCell(); AutoTArray headerCells; if (cell) { cell->ColHeaderCells(&headerCells); diff -Nru firefox-esr-115.3.1esr+build1/accessible/tests/browser/e10s/browser_caching_table.js firefox-esr-115.4.0esr+build1/accessible/tests/browser/e10s/browser_caching_table.js --- firefox-esr-115.3.1esr+build1/accessible/tests/browser/e10s/browser_caching_table.js 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/tests/browser/e10s/browser_caching_table.js 2023-10-17 05:45:43.000000000 +0000 @@ -427,15 +427,11 @@
`, async function (browser, docAcc) { - // XXX We don't create a TableAccessible in this case (bug 1494196). For - // now, just ensure we don't crash (bug 1793073). - const table = findAccessibleChildByID(docAcc, "table"); - let queryOk = false; - try { - table.QueryInterface(nsIAccessibleTable); - queryOk = true; - } catch (e) {} - todo(queryOk, "Got nsIAccessibleTable"); + const table = findAccessibleChildByID(docAcc, "table", [ + nsIAccessibleTable, + ]); + is(table.rowCount, 1, "table rowCount correct"); + is(table.columnCount, 1, "table columnCount correct"); }, { chrome: true, @@ -496,3 +492,15 @@ }, { topLevel: true } ); + +/** + * Verify that we don't crash for authoring error like
. + */ +addAccessibleTask( + `
`, + async function (browser, docAcc) { + const table = findAccessibleChildByID(docAcc, "table"); + ok(table, "Retrieved table Accessible"); + }, + { chrome: true, topLevel: true } +); diff -Nru firefox-esr-115.3.1esr+build1/accessible/tests/browser/events/browser_test_panel.js firefox-esr-115.4.0esr+build1/accessible/tests/browser/events/browser_test_panel.js --- firefox-esr-115.3.1esr+build1/accessible/tests/browser/events/browser_test_panel.js 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/tests/browser/events/browser_test_panel.js 2023-10-17 05:45:44.000000000 +0000 @@ -31,9 +31,13 @@ ok(isAccessible(PopupNotifications.panel), "Popup panel is accessible"); testAccessibleTree(PopupNotifications.panel, { ALERT: [ - { LABEL: [{ TEXT_LEAF: [] }] }, - { PUSHBUTTON: [] }, - { PUSHBUTTON: [] }, + { + TEXT_CONTAINER: [ + { LABEL: [{ TEXT_LEAF: [] }] }, + { PUSHBUTTON: [] }, + { PUSHBUTTON: [] }, + ], + }, ], }); // Verify the popup panel is associated with the chrome window. diff -Nru firefox-esr-115.3.1esr+build1/accessible/tests/browser/hittest/browser_test_general.js firefox-esr-115.4.0esr+build1/accessible/tests/browser/hittest/browser_test_general.js --- firefox-esr-115.3.1esr+build1/accessible/tests/browser/hittest/browser_test_general.js 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/tests/browser/hittest/browser_test_general.js 2023-10-17 05:45:43.000000000 +0000 @@ -232,3 +232,108 @@ iframeAttrs: { style: "width: 600px; height: 600px; padding: 10px;" }, } ); + +/** + * Verify that hit testing returns the proper accessible when one acc content + * is partially hidden due to overflow:hidden; + */ +addAccessibleTask( + ` + +
+
abcde
fghij
+
`, + async function (browser, docAcc) { + const container = findAccessibleChildByID(docAcc, "container"); + const aNode = findAccessibleChildByID(docAcc, "aNode"); + const fNode = findAccessibleChildByID(docAcc, "fNode"); + const dpr = await getContentDPR(browser); + const [, , containerWidth] = Layout.getBounds(container, dpr); + const [, , aNodeWidth] = Layout.getBounds(aNode, dpr); + + await testChildAtPoint( + dpr, + containerWidth - 1, + 1, + container, + aNode, + aNode.firstChild + ); + await testChildAtPoint( + dpr, + containerWidth - aNodeWidth - 1, + 1, + container, + fNode, + fNode.firstChild + ); + }, + { chrome: true, iframe: true, remoteIframe: true } +); + +/** + * Verify that hit testing is appropriately fuzzy when working with generics. + * If we match on a generic which contains additional generics and a single text + * leaf, we should return the text leaf as the deepest match instead of the + * generic itself. + */ +addAccessibleTask( + ` + + I am some invisible text + `, + async function (browser, docAcc) { + const link = findAccessibleChildByID(docAcc, "link"); + const generic = findAccessibleChildByID(docAcc, "generic"); + const invisible = findAccessibleChildByID(docAcc, "invisible"); + const dpr = await getContentDPR(browser); + + await testChildAtPoint( + dpr, + 1, + 1, + link, + generic, // Direct Child + invisible.firstChild // Deepest Child + ); + + await testOffsetAtPoint( + findAccessibleChildByID(docAcc, "invisible", [Ci.nsIAccessibleText]), + 1, + 1, + COORDTYPE_PARENT_RELATIVE, + 0 + ); + }, + { chrome: false, iframe: true, remoteIframe: true } +); + +/** + * Verify that hit testing is appropriately fuzzy when working with generics with siblings. + * We should return the deepest text leaf as the deepest match instead of the generic itself. + */ +addAccessibleTask( + ` +
hello world
Mozilla

I am some other text
`, + async function (browser, docAcc) { + const generic = findAccessibleChildByID(docAcc, "generic"); + const invisible = findAccessibleChildByID(docAcc, "invisible"); + const dpr = await getContentDPR(browser); + + await testChildAtPoint( + dpr, + 1, + 1, + generic, + invisible, // Direct Child + invisible.firstChild // Deepest Child + ); + }, + { chrome: false, iframe: true, remoteIframe: true } +); diff -Nru firefox-esr-115.3.1esr+build1/accessible/tests/browser/hittest/head.js firefox-esr-115.4.0esr+build1/accessible/tests/browser/hittest/head.js --- firefox-esr-115.3.1esr+build1/accessible/tests/browser/hittest/head.js 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/tests/browser/hittest/head.js 2023-10-17 05:45:43.000000000 +0000 @@ -47,6 +47,7 @@ await untilCacheIs( () => { actual = getChildAtPoint(container, x, y, false); + info(`Got direct child match of ${CommonUtils.prettyName(actual)}`); return actual; }, child, @@ -60,6 +61,7 @@ await untilCacheIs( () => { actual = getChildAtPoint(container, x, y, true); + info(`Got deepest child match of ${CommonUtils.prettyName(actual)}`); return actual; }, grandChild, diff -Nru firefox-esr-115.3.1esr+build1/accessible/tests/browser/mac/browser_table.js firefox-esr-115.4.0esr+build1/accessible/tests/browser/mac/browser_table.js --- firefox-esr-115.3.1esr+build1/accessible/tests/browser/mac/browser_table.js 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/tests/browser/mac/browser_table.js 2023-10-17 05:45:43.000000000 +0000 @@ -363,12 +363,12 @@ // after abbr is set we should have a data table again await testIsLayout( table, - "cellOne", + "cellThree", EVENT_OBJECT_ATTRIBUTE_CHANGED, async () => { await SpecialPowers.spawn(browser, [], () => { content.document - .getElementById("cellOne") + .getElementById("cellThree") .setAttribute("abbr", "hello world"); }); }, @@ -379,11 +379,11 @@ // after abbr is removed we should have a layout table again await testIsLayout( table, - "cellOne", + "cellThree", EVENT_OBJECT_ATTRIBUTE_CHANGED, async () => { await SpecialPowers.spawn(browser, [], () => { - content.document.getElementById("cellOne").removeAttribute("abbr"); + content.document.getElementById("cellThree").removeAttribute("abbr"); }); }, true @@ -393,12 +393,12 @@ // after scope is set we should have a data table again await testIsLayout( table, - "cellOne", + "cellThree", EVENT_OBJECT_ATTRIBUTE_CHANGED, async () => { await SpecialPowers.spawn(browser, [], () => { content.document - .getElementById("cellOne") + .getElementById("cellThree") .setAttribute("scope", "col"); }); }, @@ -409,11 +409,11 @@ // remove scope should give layout await testIsLayout( table, - "cellOne", + "cellThree", EVENT_OBJECT_ATTRIBUTE_CHANGED, async () => { await SpecialPowers.spawn(browser, [], () => { - content.document.getElementById("cellOne").removeAttribute("scope"); + content.document.getElementById("cellThree").removeAttribute("scope"); }); }, true diff -Nru firefox-esr-115.3.1esr+build1/accessible/tests/browser/tree/browser_general.js firefox-esr-115.4.0esr+build1/accessible/tests/browser/tree/browser_general.js --- firefox-esr-115.3.1esr+build1/accessible/tests/browser/tree/browser_general.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/tests/browser/tree/browser_general.js 2023-10-17 05:45:43.000000000 +0000 @@ -0,0 +1,128 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +/** + * Verify adding `overflow:hidden;` styling to a div causes it to + * get an accessible. + */ +addAccessibleTask(`

hello world

`, async function (browser, docAcc) { + const originalTree = { DOCUMENT: [{ PARAGRAPH: [{ TEXT_LEAF: [] }] }] }; + + testAccessibleTree(docAcc, originalTree); + info("Adding div element"); + await contentSpawnMutation( + browser, + { unexpected: [[EVENT_REORDER, docAcc]] }, + function () { + const d = content.document.createElement("div"); + content.document.body.appendChild(d); + } + ); + + testAccessibleTree(docAcc, originalTree); + info("Adding overflow:hidden styling to div"); + await contentSpawnMutation( + browser, + { expected: [[EVENT_REORDER, docAcc]] }, + function () { + content.document.body.lastElementChild.setAttribute( + "style", + "overflow:hidden;" + ); + } + ); + + testAccessibleTree(docAcc, { + DOCUMENT: [{ PARAGRAPH: [{ TEXT_LEAF: [] }] }, { TEXT_CONTAINER: [] }], + }); +}); + +/** + * Verify adding `overflow:scroll;` styling to a div causes + * it to get an accessible. + */ +addAccessibleTask(`

hello world

`, async function (browser, docAcc) { + const originalTree = { DOCUMENT: [{ PARAGRAPH: [{ TEXT_LEAF: [] }] }] }; + + testAccessibleTree(docAcc, originalTree); + info("Adding div element"); + await contentSpawnMutation( + browser, + { unexpected: [[EVENT_REORDER, docAcc]] }, + function () { + const d = content.document.createElement("div"); + content.document.body.appendChild(d); + } + ); + + testAccessibleTree(docAcc, originalTree); + info("Adding overflow:scroll styling to div"); + await contentSpawnMutation( + browser, + { expected: [[EVENT_REORDER, docAcc]] }, + function () { + content.document.body.lastElementChild.setAttribute( + "style", + "overflow:scroll;" + ); + } + ); + + testAccessibleTree(docAcc, { + DOCUMENT: [{ PARAGRAPH: [{ TEXT_LEAF: [] }] }, { TEXT_CONTAINER: [] }], + }); +}); + +/** + * Verify adding `overflow:auto;` styling to a div causes + * it to get an accessible, but `overflow:visible` does not. + */ +addAccessibleTask(`

hello world

`, async function (browser, docAcc) { + const originalTree = { DOCUMENT: [{ PARAGRAPH: [{ TEXT_LEAF: [] }] }] }; + + testAccessibleTree(docAcc, originalTree); + info("Adding div element"); + await contentSpawnMutation( + browser, + { unexpected: [[EVENT_REORDER, docAcc]] }, + function () { + const d = content.document.createElement("div"); + content.document.body.appendChild(d); + } + ); + + testAccessibleTree(docAcc, originalTree); + info("Adding overflow:visible styling to div"); + await contentSpawnMutation( + browser, + { unexpected: [[EVENT_REORDER, docAcc]] }, + function () { + content.document.body.lastElementChild.setAttribute( + "style", + "overflow:visible;" + ); + } + ); + + testAccessibleTree(docAcc, originalTree); + info("Adding overflow:auto styling to div"); + await contentSpawnMutation( + browser, + { expected: [[EVENT_REORDER, docAcc]] }, + function () { + content.document.body.lastElementChild.setAttribute( + "style", + "overflow:auto;" + ); + } + ); + + testAccessibleTree(docAcc, { + DOCUMENT: [{ PARAGRAPH: [{ TEXT_LEAF: [] }] }, { TEXT_CONTAINER: [] }], + }); +}); diff -Nru firefox-esr-115.3.1esr+build1/accessible/tests/browser/tree/browser.ini firefox-esr-115.4.0esr+build1/accessible/tests/browser/tree/browser.ini --- firefox-esr-115.3.1esr+build1/accessible/tests/browser/tree/browser.ini 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/tests/browser/tree/browser.ini 2023-10-17 05:45:43.000000000 +0000 @@ -12,6 +12,7 @@ skip-if = true || (verify && !debug && (os == 'linux')) #Bug 1445513 [browser_browser_element.js] [browser_css_content_visibility.js] +[browser_general.js] [browser_lazy_tabs.js] [browser_searchbar.js] [browser_shadowdom.js] diff -Nru firefox-esr-115.3.1esr+build1/accessible/tests/mochitest/elm/test_shadowroot_subframe.html firefox-esr-115.4.0esr+build1/accessible/tests/mochitest/elm/test_shadowroot_subframe.html --- firefox-esr-115.3.1esr+build1/accessible/tests/mochitest/elm/test_shadowroot_subframe.html 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/tests/mochitest/elm/test_shadowroot_subframe.html 2023-10-17 05:45:43.000000000 +0000 @@ -61,8 +61,8 @@ var table = document.getElementById("table"); shadow = table.attachShadow({mode: "open"}); - shadow.innerHTML = "
" + - "
hi
" + + shadow.innerHTML = "
" + + "
hi
" + "
"; diff -Nru firefox-esr-115.3.1esr+build1/accessible/tests/mochitest/events/test_attrchange.html firefox-esr-115.4.0esr+build1/accessible/tests/mochitest/events/test_attrchange.html --- firefox-esr-115.3.1esr+build1/accessible/tests/mochitest/events/test_attrchange.html 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/tests/mochitest/events/test_attrchange.html 2023-10-17 05:45:43.000000000 +0000 @@ -94,7 +94,7 @@
- + diff -Nru firefox-esr-115.3.1esr+build1/accessible/tests/mochitest/table/test_sels_ariagrid.html firefox-esr-115.4.0esr+build1/accessible/tests/mochitest/table/test_sels_ariagrid.html --- firefox-esr-115.3.1esr+build1/accessible/tests/mochitest/table/test_sels_ariagrid.html 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/tests/mochitest/table/test_sels_ariagrid.html 2023-10-17 05:45:43.000000000 +0000 @@ -33,10 +33,6 @@ ]; testTableSelection("table", cellsArray); - testUnselectTableColumn("table", 3, cellsArray); - testUnselectTableRow("table", 3, cellsArray); - testSelectTableColumn("table", 0, cellsArray); - testSelectTableRow("table", 0, cellsArray); // //////////////////////////////////////////////////////////////////////// // a bit strange ARIA grid @@ -47,10 +43,6 @@ ]; testTableSelection("grid2", cellsArray); - testSelectTableColumn("grid2", 0, cellsArray); - testSelectTableRow("grid2", 0, cellsArray); - testUnselectTableColumn("grid2", 0, cellsArray); - testUnselectTableRow("grid2", 0, cellsArray); // //////////////////////////////////////////////////////////////////////// // ARIA grid (column and row headers) @@ -62,10 +54,6 @@ ]; testTableSelection("grid3", cellsArray); - testSelectTableColumn("grid3", 0, cellsArray); - testSelectTableRow("grid3", 0, cellsArray); - testUnselectTableColumn("grid3", 0, cellsArray); - testUnselectTableRow("grid3", 0, cellsArray); SimpleTest.finish(); } diff -Nru firefox-esr-115.3.1esr+build1/accessible/tests/mochitest/table/test_sels_table.html firefox-esr-115.4.0esr+build1/accessible/tests/mochitest/table/test_sels_table.html --- firefox-esr-115.3.1esr+build1/accessible/tests/mochitest/table/test_sels_table.html 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/tests/mochitest/table/test_sels_table.html 2023-10-17 05:45:43.000000000 +0000 @@ -33,24 +33,6 @@ testTableSelection("table", cellsArray); - var rowCount = 4; - for (let rowIdx = 0; rowIdx < rowCount; rowIdx++) - testSelectTableRow("table", rowIdx, cellsArray); - - for (let rowIdx = 0; rowIdx < rowCount; rowIdx++) { - testSelectTableRow("table", rowIdx, cellsArray); - testUnselectTableRow("table", rowIdx, cellsArray); - } - - var columsCount = 8; - for (let colIdx = 0; colIdx < columsCount; colIdx++) - testSelectTableColumn("table", colIdx, cellsArray); - - for (let colIdx = 0; colIdx < columsCount; colIdx++) { - testSelectTableColumn("table", colIdx, cellsArray); - testUnselectTableColumn("table", colIdx, cellsArray); - } - var accTable = getAccessible("table", [nsIAccessibleTable]); ok(!accTable.isProbablyForLayout(), "table is not for layout"); @@ -88,11 +70,6 @@ Mozilla Bug 501635 - Mozilla Bug 417929 - - Mozilla Bug 501659 diff -Nru firefox-esr-115.3.1esr+build1/accessible/tests/mochitest/table/test_sels_tree.xhtml firefox-esr-115.4.0esr+build1/accessible/tests/mochitest/table/test_sels_tree.xhtml --- firefox-esr-115.3.1esr+build1/accessible/tests/mochitest/table/test_sels_tree.xhtml 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/tests/mochitest/table/test_sels_tree.xhtml 2023-10-17 05:45:43.000000000 +0000 @@ -39,8 +39,6 @@ ]; testTableSelection("tree", cellsArray); - testSelectTableRow("tree", 0, cellsArray); - testUnselectTableRow("tree", 0, cellsArray); SimpleTest.finish(); } diff -Nru firefox-esr-115.3.1esr+build1/accessible/tests/mochitest/table.js firefox-esr-115.4.0esr+build1/accessible/tests/mochitest/table.js --- firefox-esr-115.3.1esr+build1/accessible/tests/mochitest/table.js 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/tests/mochitest/table.js 2023-10-17 05:45:43.000000000 +0000 @@ -745,189 +745,6 @@ } /** - * Test unselectColumn method of accessible table. - */ -function testUnselectTableColumn(aIdentifier, aColIdx, aCellsArray) { - var acc = getAccessible(aIdentifier, [nsIAccessibleTable]); - if (!acc) { - return; - } - - var rowCount = aCellsArray.length; - for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) { - // Unselect origin cell. - var [origRowIdx, origColIdx] = getOrigRowAndColumn( - aCellsArray, - rowIdx, - aColIdx - ); - aCellsArray[origRowIdx][origColIdx] = false; - } - - acc.unselectColumn(aColIdx); - testTableSelection( - aIdentifier, - aCellsArray, - "Unselect " + aColIdx + " column: " - ); -} - -/** - * Test selectColumn method of accessible table. - */ -function testSelectTableColumn(aIdentifier, aColIdx, aCellsArray) { - var acc = getAccessible(aIdentifier, [nsIAccessibleTable]); - if (!acc) { - return; - } - - var rowCount = aCellsArray.length; - var colsCount = aCellsArray[0].length; - - for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) { - for (var colIdx = 0; colIdx < colsCount; colIdx++) { - var cellState = aCellsArray[rowIdx][colIdx]; - - if (colIdx == aColIdx) { - // select target column - if (!(cellState & kSpanned)) { - // Select the cell if it is origin. - aCellsArray[rowIdx][colIdx] = true; - } else { - // If the cell is spanned then search origin cell and select it. - var [origRowIdx, origColIdx] = getOrigRowAndColumn( - aCellsArray, - rowIdx, - colIdx - ); - aCellsArray[origRowIdx][origColIdx] = true; - } - } else if (!(cellState & kSpanned)) { - // unselect other columns - if (colIdx > aColIdx) { - // Unselect the cell if traversed column index is greater than column - // index of target cell. - aCellsArray[rowIdx][colIdx] = false; - } else if (!(aCellsArray[rowIdx][aColIdx] & kColSpanned)) { - // Unselect the cell if the target cell is not row spanned. - aCellsArray[rowIdx][colIdx] = false; - } else { - // Unselect the cell if it is not spanned to the target cell. - for ( - var spannedColIdx = colIdx + 1; - spannedColIdx < aColIdx; - spannedColIdx++ - ) { - var spannedCellState = aCellsArray[rowIdx][spannedColIdx]; - if (!(spannedCellState & kRowSpanned)) { - aCellsArray[rowIdx][colIdx] = false; - break; - } - } - } - } - } - } - - acc.selectColumn(aColIdx); - testTableSelection( - aIdentifier, - aCellsArray, - "Select " + aColIdx + " column: " - ); -} - -/** - * Test unselectRow method of accessible table. - */ -function testUnselectTableRow(aIdentifier, aRowIdx, aCellsArray) { - var acc = getAccessible(aIdentifier, [nsIAccessibleTable]); - if (!acc) { - return; - } - - var colsCount = aCellsArray[0].length; - for (var colIdx = 0; colIdx < colsCount; colIdx++) { - // Unselect origin cell. - var [origRowIdx, origColIdx] = getOrigRowAndColumn( - aCellsArray, - aRowIdx, - colIdx - ); - aCellsArray[origRowIdx][origColIdx] = false; - } - - acc.unselectRow(aRowIdx); - testTableSelection( - aIdentifier, - aCellsArray, - "Unselect " + aRowIdx + " row: " - ); -} - -/** - * Test selectRow method of accessible table. - */ -function testSelectTableRow(aIdentifier, aRowIdx, aCellsArray) { - var acc = getAccessible(aIdentifier, [nsIAccessibleTable]); - if (!acc) { - return; - } - - var rowCount = aCellsArray.length; - var colsCount = aCellsArray[0].length; - - for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) { - for (var colIdx = 0; colIdx < colsCount; colIdx++) { - var cellState = aCellsArray[rowIdx][colIdx]; - - if (rowIdx == aRowIdx) { - // select the given row - if (!(cellState & kSpanned)) { - // Select the cell if it is origin. - aCellsArray[rowIdx][colIdx] = true; - } else { - // If the cell is spanned then search origin cell and select it. - var [origRowIdx, origColIdx] = getOrigRowAndColumn( - aCellsArray, - rowIdx, - colIdx - ); - - aCellsArray[origRowIdx][origColIdx] = true; - } - } else if (!(cellState & kSpanned)) { - // unselect other rows - if (rowIdx > aRowIdx) { - // Unselect the cell if traversed row index is greater than row - // index of target cell. - aCellsArray[rowIdx][colIdx] = false; - } else if (!(aCellsArray[aRowIdx][colIdx] & kRowSpanned)) { - // Unselect the cell if the target cell is not row spanned. - aCellsArray[rowIdx][colIdx] = false; - } else { - // Unselect the cell if it is not spanned to the target cell. - for ( - var spannedRowIdx = rowIdx + 1; - spannedRowIdx < aRowIdx; - spannedRowIdx++ - ) { - var spannedCellState = aCellsArray[spannedRowIdx][colIdx]; - if (!(spannedCellState & kRowSpanned)) { - aCellsArray[rowIdx][colIdx] = false; - break; - } - } - } - } - } - } - - acc.selectRow(aRowIdx); - testTableSelection(aIdentifier, aCellsArray, "Select " + aRowIdx + " row: "); -} - -/** * Test columnHeaderCells and rowHeaderCells of accessible table. */ function testHeaderCells(aHeaderInfoMap) { diff -Nru firefox-esr-115.3.1esr+build1/accessible/tests/mochitest/tree/test_table_2.html firefox-esr-115.4.0esr+build1/accessible/tests/mochitest/tree/test_table_2.html --- firefox-esr-115.3.1esr+build1/accessible/tests/mochitest/tree/test_table_2.html 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/tests/mochitest/tree/test_table_2.html 2023-10-17 05:45:44.000000000 +0000 @@ -150,14 +150,16 @@ name: "Top 10 Grossing Animated Films of All Time", }, ] }, - { ROW: [ - { role: COLHEADER, name: "Film Title" }, - { role: COLHEADER, name: "Released" }, - { role: COLHEADER, name: "Studio" }, - { role: COLHEADER, name: "Worldwide Gross" }, - { role: COLHEADER, name: "Domestic Gross" }, - { role: COLHEADER, name: "Foreign Gross" }, - { role: COLHEADER, name: "Budget" }, + { TEXT_CONTAINER: [ + { ROW: [ + { role: COLHEADER, name: "Film Title" }, + { role: COLHEADER, name: "Released" }, + { role: COLHEADER, name: "Studio" }, + { role: COLHEADER, name: "Worldwide Gross" }, + { role: COLHEADER, name: "Domestic Gross" }, + { role: COLHEADER, name: "Foreign Gross" }, + { role: COLHEADER, name: "Budget" }, + ] }, ] }, { ROW: [ { role: CELL }, diff -Nru firefox-esr-115.3.1esr+build1/accessible/tests/mochitest/tree/test_table_3.html firefox-esr-115.4.0esr+build1/accessible/tests/mochitest/tree/test_table_3.html --- firefox-esr-115.3.1esr+build1/accessible/tests/mochitest/tree/test_table_3.html 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/tests/mochitest/tree/test_table_3.html 2023-10-17 05:45:44.000000000 +0000 @@ -116,14 +116,16 @@ name: "Top 10 Grossing Animated Films of All Time", }, ] }, - { ROW: [ - { COLUMNHEADER: [ { role: TEXT_LEAF, name: "Film Title" } ] }, - { COLUMNHEADER: [ { role: TEXT_LEAF, name: "Released" } ] }, - { COLUMNHEADER: [ { role: TEXT_LEAF, name: "Studio" } ] }, - { COLUMNHEADER: [ { role: TEXT_LEAF, name: "Worldwide Gross" } ] }, - { COLUMNHEADER: [ { role: TEXT_LEAF, name: "Domestic Gross" } ] }, - { COLUMNHEADER: [ { role: TEXT_LEAF, name: "Foreign Gross" } ] }, - { COLUMNHEADER: [ { role: TEXT_LEAF, name: "Budget" } ] }, + { TEXT_CONTAINER: [ + { ROW: [ + { COLUMNHEADER: [ { role: TEXT_LEAF, name: "Film Title" } ] }, + { COLUMNHEADER: [ { role: TEXT_LEAF, name: "Released" } ] }, + { COLUMNHEADER: [ { role: TEXT_LEAF, name: "Studio" } ] }, + { COLUMNHEADER: [ { role: TEXT_LEAF, name: "Worldwide Gross" } ] }, + { COLUMNHEADER: [ { role: TEXT_LEAF, name: "Domestic Gross" } ] }, + { COLUMNHEADER: [ { role: TEXT_LEAF, name: "Foreign Gross" } ] }, + { COLUMNHEADER: [ { role: TEXT_LEAF, name: "Budget" } ] }, + ] }, ] }, { ROW: [ { role: CELL }, diff -Nru firefox-esr-115.3.1esr+build1/accessible/tests/mochitest/tree/test_table.html firefox-esr-115.4.0esr+build1/accessible/tests/mochitest/tree/test_table.html --- firefox-esr-115.3.1esr+build1/accessible/tests/mochitest/tree/test_table.html 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/tests/mochitest/tree/test_table.html 2023-10-17 05:45:43.000000000 +0000 @@ -422,7 +422,7 @@
cell1 cell2
- + @@ -482,8 +482,8 @@
bla
- - +
+ diff -Nru firefox-esr-115.3.1esr+build1/accessible/tests/mochitest/treeupdate/test_cssoverflow.html firefox-esr-115.4.0esr+build1/accessible/tests/mochitest/treeupdate/test_cssoverflow.html --- firefox-esr-115.3.1esr+build1/accessible/tests/mochitest/treeupdate/test_cssoverflow.html 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/tests/mochitest/treeupdate/test_cssoverflow.html 2023-10-17 05:45:43.000000000 +0000 @@ -61,7 +61,7 @@ } /** - * Change scrollbar styles from hidden to auto to make the scroll area focusable. + * Change scrollbar styles from visible to auto to make the scroll area focusable. * That causes us to create an accessible for it. * Make sure the tree stays intact. * The scroll area has no ID on purpose to make it inaccessible initially. @@ -120,7 +120,7 @@ gQueue = new eventQueue(); gQueue.push(new changeScrollRange("container", "scrollarea")); - gQueue.push(new makeFocusableByScrollbarStyles("container3")); + gQueue.push(new makeFocusableByScrollbarStyles("container2")); gQueue.invoke(); // Will call SimpleTest.finish(); } @@ -144,7 +144,6 @@
-
-

foo

+

foo

diff -Nru firefox-esr-115.3.1esr+build1/accessible/windows/ia2/ia2AccessibleTableCell.cpp firefox-esr-115.4.0esr+build1/accessible/windows/ia2/ia2AccessibleTableCell.cpp --- firefox-esr-115.3.1esr+build1/accessible/windows/ia2/ia2AccessibleTableCell.cpp 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/windows/ia2/ia2AccessibleTableCell.cpp 2023-10-17 05:45:43.000000000 +0000 @@ -12,16 +12,16 @@ #include "IUnknownImpl.h" #include "mozilla/a11y/Accessible.h" -#include "mozilla/a11y/TableAccessibleBase.h" -#include "mozilla/a11y/TableCellAccessibleBase.h" +#include "mozilla/a11y/TableAccessible.h" +#include "mozilla/a11y/TableCellAccessible.h" #include "nsCOMPtr.h" #include "nsString.h" using namespace mozilla::a11y; -TableCellAccessibleBase* ia2AccessibleTableCell::CellAcc() { +TableCellAccessible* ia2AccessibleTableCell::CellAcc() { Accessible* acc = Acc(); - return acc ? acc->AsTableCellBase() : nullptr; + return acc ? acc->AsTableCell() : nullptr; } // IUnknown @@ -37,10 +37,10 @@ if (!aTable) return E_INVALIDARG; *aTable = nullptr; - TableCellAccessibleBase* tableCell = CellAcc(); + TableCellAccessible* tableCell = CellAcc(); if (!tableCell) return CO_E_OBJNOTCONNECTED; - TableAccessibleBase* table = tableCell->Table(); + TableAccessible* table = tableCell->Table(); if (!table) return E_FAIL; Accessible* tableAcc = table->AsAccessible(); @@ -54,7 +54,7 @@ if (!aSpan) return E_INVALIDARG; *aSpan = 0; - TableCellAccessibleBase* tableCell = CellAcc(); + TableCellAccessible* tableCell = CellAcc(); if (!tableCell) return CO_E_OBJNOTCONNECTED; *aSpan = tableCell->ColExtent(); @@ -69,7 +69,7 @@ *aCellAccessibles = nullptr; *aNColumnHeaderCells = 0; - TableCellAccessibleBase* tableCell = CellAcc(); + TableCellAccessible* tableCell = CellAcc(); if (!tableCell) return CO_E_OBJNOTCONNECTED; AutoTArray cells; @@ -94,7 +94,7 @@ if (!aColIdx) return E_INVALIDARG; *aColIdx = -1; - TableCellAccessibleBase* tableCell = CellAcc(); + TableCellAccessible* tableCell = CellAcc(); if (!tableCell) return CO_E_OBJNOTCONNECTED; *aColIdx = tableCell->ColIdx(); @@ -106,7 +106,7 @@ if (!aSpan) return E_INVALIDARG; *aSpan = 0; - TableCellAccessibleBase* tableCell = CellAcc(); + TableCellAccessible* tableCell = CellAcc(); if (!tableCell) return CO_E_OBJNOTCONNECTED; *aSpan = tableCell->RowExtent(); @@ -120,7 +120,7 @@ *aCellAccessibles = nullptr; *aNRowHeaderCells = 0; - TableCellAccessibleBase* tableCell = CellAcc(); + TableCellAccessible* tableCell = CellAcc(); if (!tableCell) return CO_E_OBJNOTCONNECTED; AutoTArray cells; @@ -144,7 +144,7 @@ if (!aRowIdx) return E_INVALIDARG; *aRowIdx = -1; - TableCellAccessibleBase* tableCell = CellAcc(); + TableCellAccessible* tableCell = CellAcc(); if (!tableCell) return CO_E_OBJNOTCONNECTED; *aRowIdx = tableCell->RowIdx(); @@ -161,7 +161,7 @@ *aRowIdx = *aColIdx = *aRowExtents = *aColExtents = 0; *aIsSelected = false; - TableCellAccessibleBase* tableCell = CellAcc(); + TableCellAccessible* tableCell = CellAcc(); if (!tableCell) return CO_E_OBJNOTCONNECTED; *aRowIdx = tableCell->RowIdx(); @@ -178,7 +178,7 @@ if (!aIsSelected) return E_INVALIDARG; *aIsSelected = false; - TableCellAccessibleBase* tableCell = CellAcc(); + TableCellAccessible* tableCell = CellAcc(); if (!tableCell) return CO_E_OBJNOTCONNECTED; *aIsSelected = tableCell->Selected(); diff -Nru firefox-esr-115.3.1esr+build1/accessible/windows/ia2/ia2AccessibleTableCell.h firefox-esr-115.4.0esr+build1/accessible/windows/ia2/ia2AccessibleTableCell.h --- firefox-esr-115.3.1esr+build1/accessible/windows/ia2/ia2AccessibleTableCell.h 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/windows/ia2/ia2AccessibleTableCell.h 2023-10-17 05:45:43.000000000 +0000 @@ -14,7 +14,7 @@ namespace mozilla { namespace a11y { -class TableCellAccessibleBase; +class TableCellAccessible; class ia2AccessibleTableCell : public IAccessibleTableCell, public ia2AccessibleHypertext { @@ -62,7 +62,7 @@ using ia2AccessibleHypertext::ia2AccessibleHypertext; private: - TableCellAccessibleBase* CellAcc(); + TableCellAccessible* CellAcc(); }; } // namespace a11y diff -Nru firefox-esr-115.3.1esr+build1/accessible/windows/ia2/ia2AccessibleTable.cpp firefox-esr-115.4.0esr+build1/accessible/windows/ia2/ia2AccessibleTable.cpp --- firefox-esr-115.3.1esr+build1/accessible/windows/ia2/ia2AccessibleTable.cpp 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/windows/ia2/ia2AccessibleTable.cpp 2023-10-17 05:45:43.000000000 +0000 @@ -13,16 +13,16 @@ #include "IUnknownImpl.h" #include "mozilla/a11y/Accessible.h" -#include "mozilla/a11y/TableAccessibleBase.h" +#include "mozilla/a11y/TableAccessible.h" #include "nsCOMPtr.h" #include "nsString.h" #include "Statistics.h" using namespace mozilla::a11y; -TableAccessibleBase* ia2AccessibleTable::TableAcc() { +TableAccessible* ia2AccessibleTable::TableAcc() { Accessible* acc = Acc(); - return acc ? acc->AsTableBase() : nullptr; + return acc ? acc->AsTable() : nullptr; } // IUnknown @@ -63,7 +63,7 @@ if (!aAccessible) return E_INVALIDARG; *aAccessible = nullptr; - TableAccessibleBase* table = TableAcc(); + TableAccessible* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; Accessible* caption = table->Caption(); @@ -80,7 +80,7 @@ if (!aChildIdx) return E_INVALIDARG; *aChildIdx = 0; - TableAccessibleBase* table = TableAcc(); + TableAccessible* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aRowIdx < 0 || aColIdx < 0 || @@ -97,7 +97,7 @@ if (!aDescription) return E_INVALIDARG; *aDescription = nullptr; - TableAccessibleBase* table = TableAcc(); + TableAccessible* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aColIdx < 0 || static_cast(aColIdx) >= table->ColCount()) @@ -117,7 +117,7 @@ if (!aSpan) return E_INVALIDARG; *aSpan = 0; - TableAccessibleBase* table = TableAcc(); + TableAccessible* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aRowIdx < 0 || aColIdx < 0 || @@ -144,7 +144,7 @@ if (!aColIdx) return E_INVALIDARG; *aColIdx = 0; - TableAccessibleBase* table = TableAcc(); + TableAccessible* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aCellIdx < 0) { @@ -165,7 +165,7 @@ if (!aColCount) return E_INVALIDARG; *aColCount = 0; - TableAccessibleBase* table = TableAcc(); + TableAccessible* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; *aColCount = table->ColCount(); @@ -177,7 +177,7 @@ if (!aRowCount) return E_INVALIDARG; *aRowCount = 0; - TableAccessibleBase* table = TableAcc(); + TableAccessible* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; *aRowCount = table->RowCount(); @@ -194,7 +194,7 @@ if (!aColCount) return E_INVALIDARG; *aColCount = 0; - TableAccessibleBase* table = TableAcc(); + TableAccessible* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; *aColCount = table->SelectedColCount(); @@ -206,7 +206,7 @@ if (!aRowCount) return E_INVALIDARG; *aRowCount = 0; - TableAccessibleBase* table = TableAcc(); + TableAccessible* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; *aRowCount = table->SelectedRowCount(); @@ -219,7 +219,7 @@ if (!aDescription) return E_INVALIDARG; *aDescription = nullptr; - TableAccessibleBase* table = TableAcc(); + TableAccessible* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aRowIdx < 0 || static_cast(aRowIdx) >= table->RowCount()) @@ -238,7 +238,7 @@ if (!aSpan) return E_INVALIDARG; *aSpan = 0; - TableAccessibleBase* table = TableAcc(); + TableAccessible* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aRowIdx < 0 || aColIdx < 0 || @@ -265,7 +265,7 @@ if (!aRowIdx) return E_INVALIDARG; *aRowIdx = 0; - TableAccessibleBase* table = TableAcc(); + TableAccessible* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aCellIdx < 0) { @@ -288,7 +288,7 @@ *aChildren = nullptr; *aNChildren = 0; - TableAccessibleBase* table = TableAcc(); + TableAccessible* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; AutoTArray cellIndices; @@ -334,7 +334,7 @@ if (!aIsSelected) return E_INVALIDARG; *aIsSelected = false; - TableAccessibleBase* table = TableAcc(); + TableAccessible* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aColIdx < 0 || static_cast(aColIdx) >= table->ColCount()) @@ -349,7 +349,7 @@ if (!aIsSelected) return E_INVALIDARG; *aIsSelected = false; - TableAccessibleBase* table = TableAcc(); + TableAccessible* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aRowIdx < 0 || static_cast(aRowIdx) >= table->RowCount()) @@ -365,7 +365,7 @@ if (!aIsSelected) return E_INVALIDARG; *aIsSelected = false; - TableAccessibleBase* table = TableAcc(); + TableAccessible* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aRowIdx < 0 || aColIdx < 0 || @@ -378,52 +378,16 @@ } STDMETHODIMP -ia2AccessibleTable::selectRow(long aRowIdx) { - TableAccessibleBase* table = TableAcc(); - if (!table) return CO_E_OBJNOTCONNECTED; - - if (aRowIdx < 0 || static_cast(aRowIdx) >= table->RowCount()) - return E_INVALIDARG; - - table->SelectRow(aRowIdx); - return S_OK; -} +ia2AccessibleTable::selectRow(long aRowIdx) { return E_NOTIMPL; } STDMETHODIMP -ia2AccessibleTable::selectColumn(long aColIdx) { - TableAccessibleBase* table = TableAcc(); - if (!table) return CO_E_OBJNOTCONNECTED; - - if (aColIdx < 0 || static_cast(aColIdx) >= table->ColCount()) - return E_INVALIDARG; - - table->SelectCol(aColIdx); - return S_OK; -} +ia2AccessibleTable::selectColumn(long aColIdx) { return E_NOTIMPL; } STDMETHODIMP -ia2AccessibleTable::unselectRow(long aRowIdx) { - TableAccessibleBase* table = TableAcc(); - if (!table) return CO_E_OBJNOTCONNECTED; - - if (aRowIdx < 0 || static_cast(aRowIdx) >= table->RowCount()) - return E_INVALIDARG; - - table->UnselectRow(aRowIdx); - return S_OK; -} +ia2AccessibleTable::unselectRow(long aRowIdx) { return E_NOTIMPL; } STDMETHODIMP -ia2AccessibleTable::unselectColumn(long aColIdx) { - TableAccessibleBase* table = TableAcc(); - if (!table) return CO_E_OBJNOTCONNECTED; - - if (aColIdx < 0 || static_cast(aColIdx) >= table->ColCount()) - return E_INVALIDARG; - - table->UnselectCol(aColIdx); - return S_OK; -} +ia2AccessibleTable::unselectColumn(long aColIdx) { return E_NOTIMPL; } STDMETHODIMP ia2AccessibleTable::get_rowColumnExtentsAtIndex(long aCellIdx, long* aRowIdx, @@ -439,7 +403,7 @@ *aRowExtents = 0; *aColExtents = 0; *aIsSelected = false; - TableAccessibleBase* table = TableAcc(); + TableAccessible* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aCellIdx < 0) { @@ -475,7 +439,7 @@ *aCell = nullptr; - TableAccessibleBase* table = TableAcc(); + TableAccessible* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; Accessible* cell = table->CellAt(aRowIdx, aColIdx); @@ -491,7 +455,7 @@ if (!aCellCount) return E_INVALIDARG; *aCellCount = 0; - TableAccessibleBase* table = TableAcc(); + TableAccessible* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; *aCellCount = table->SelectedCellCount(); @@ -505,7 +469,7 @@ *aCells = nullptr; *aNSelectedCells = 0; - TableAccessibleBase* table = TableAcc(); + TableAccessible* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; AutoTArray cells; @@ -531,7 +495,7 @@ *aColumns = nullptr; *aNColumns = 0; - TableAccessibleBase* table = TableAcc(); + TableAccessible* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; AutoTArray colIndices; @@ -553,7 +517,7 @@ *aRows = nullptr; *aNRows = 0; - TableAccessibleBase* table = TableAcc(); + TableAccessible* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; AutoTArray rowIndices; diff -Nru firefox-esr-115.3.1esr+build1/accessible/windows/ia2/ia2AccessibleTable.h firefox-esr-115.4.0esr+build1/accessible/windows/ia2/ia2AccessibleTable.h --- firefox-esr-115.3.1esr+build1/accessible/windows/ia2/ia2AccessibleTable.h 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/windows/ia2/ia2AccessibleTable.h 2023-10-17 05:45:43.000000000 +0000 @@ -16,7 +16,7 @@ namespace mozilla { namespace a11y { -class TableAccessibleBase; +class TableAccessible; class ia2AccessibleTable : public IAccessibleTable, public IAccessibleTable2, @@ -169,7 +169,7 @@ using ia2AccessibleHypertext::ia2AccessibleHypertext; private: - TableAccessibleBase* TableAcc(); + TableAccessible* TableAcc(); }; } // namespace a11y diff -Nru firefox-esr-115.3.1esr+build1/accessible/xpcom/xpcAccessibleTableCell.cpp firefox-esr-115.4.0esr+build1/accessible/xpcom/xpcAccessibleTableCell.cpp --- firefox-esr-115.3.1esr+build1/accessible/xpcom/xpcAccessibleTableCell.cpp 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/xpcom/xpcAccessibleTableCell.cpp 2023-10-17 05:45:43.000000000 +0000 @@ -7,8 +7,8 @@ #include "xpcAccessibleTableCell.h" #include "mozilla/a11y/Accessible.h" -#include "mozilla/a11y/TableAccessibleBase.h" -#include "mozilla/a11y/TableCellAccessibleBase.h" +#include "mozilla/a11y/TableAccessible.h" +#include "mozilla/a11y/TableCellAccessible.h" #include "nsIAccessibleTable.h" #include "nsComponentManagerUtils.h" @@ -34,7 +34,7 @@ if (!Intl()) return NS_ERROR_FAILURE; - TableAccessibleBase* table = Intl()->Table(); + TableAccessible* table = Intl()->Table(); if (!table) return NS_ERROR_FAILURE; nsCOMPtr xpcTable = do_QueryInterface( diff -Nru firefox-esr-115.3.1esr+build1/accessible/xpcom/xpcAccessibleTableCell.h firefox-esr-115.4.0esr+build1/accessible/xpcom/xpcAccessibleTableCell.h --- firefox-esr-115.3.1esr+build1/accessible/xpcom/xpcAccessibleTableCell.h 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/xpcom/xpcAccessibleTableCell.h 2023-10-17 05:45:43.000000000 +0000 @@ -13,7 +13,7 @@ namespace mozilla { namespace a11y { -class TableCellAccessibleBase; +class TableCellAccessible; /** * XPCOM wrapper around TableAccessibleCell class. @@ -40,7 +40,7 @@ virtual ~xpcAccessibleTableCell() {} private: - TableCellAccessibleBase* Intl() { return mIntl->AsTableCellBase(); } + TableCellAccessible* Intl() { return mIntl->AsTableCell(); } xpcAccessibleTableCell(const xpcAccessibleTableCell&) = delete; xpcAccessibleTableCell& operator=(const xpcAccessibleTableCell&) = delete; diff -Nru firefox-esr-115.3.1esr+build1/accessible/xpcom/xpcAccessibleTable.cpp firefox-esr-115.4.0esr+build1/accessible/xpcom/xpcAccessibleTable.cpp --- firefox-esr-115.3.1esr+build1/accessible/xpcom/xpcAccessibleTable.cpp 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/xpcom/xpcAccessibleTable.cpp 2023-10-17 05:45:43.000000000 +0000 @@ -7,7 +7,7 @@ #include "xpcAccessibleTable.h" #include "mozilla/a11y/Accessible.h" -#include "mozilla/a11y/TableAccessibleBase.h" +#include "mozilla/a11y/TableAccessible.h" #include "nsIMutableArray.h" #include "nsComponentManagerUtils.h" @@ -361,51 +361,3 @@ *aResult = Intl()->IsProbablyLayoutTable(); return NS_OK; } - -NS_IMETHODIMP -xpcAccessibleTable::SelectColumn(int32_t aColIdx) { - if (!Intl()) return NS_ERROR_FAILURE; - - if (aColIdx < 0 || static_cast(aColIdx) >= Intl()->ColCount()) { - return NS_ERROR_INVALID_ARG; - } - - Intl()->SelectCol(aColIdx); - return NS_OK; -} - -NS_IMETHODIMP -xpcAccessibleTable::SelectRow(int32_t aRowIdx) { - if (!Intl()) return NS_ERROR_FAILURE; - - if (aRowIdx < 0 || static_cast(aRowIdx) >= Intl()->RowCount()) { - return NS_ERROR_INVALID_ARG; - } - - Intl()->SelectRow(aRowIdx); - return NS_OK; -} - -NS_IMETHODIMP -xpcAccessibleTable::UnselectColumn(int32_t aColIdx) { - if (!Intl()) return NS_ERROR_FAILURE; - - if (aColIdx < 0 || static_cast(aColIdx) >= Intl()->ColCount()) { - return NS_ERROR_INVALID_ARG; - } - - Intl()->UnselectCol(aColIdx); - return NS_OK; -} - -NS_IMETHODIMP -xpcAccessibleTable::UnselectRow(int32_t aRowIdx) { - if (!Intl()) return NS_ERROR_FAILURE; - - if (aRowIdx < 0 || static_cast(aRowIdx) >= Intl()->RowCount()) { - return NS_ERROR_INVALID_ARG; - } - - Intl()->UnselectRow(aRowIdx); - return NS_OK; -} diff -Nru firefox-esr-115.3.1esr+build1/accessible/xpcom/xpcAccessibleTable.h firefox-esr-115.4.0esr+build1/accessible/xpcom/xpcAccessibleTable.h --- firefox-esr-115.3.1esr+build1/accessible/xpcom/xpcAccessibleTable.h 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/xpcom/xpcAccessibleTable.h 2023-10-17 05:45:43.000000000 +0000 @@ -12,7 +12,7 @@ namespace mozilla { namespace a11y { -class TableAccessibleBase; +class TableAccessible; /** * XPCOM wrapper around TableAccessible class. @@ -56,17 +56,13 @@ NS_IMETHOD GetSelectedCellIndices(nsTArray& aCellsArray) final; NS_IMETHOD GetSelectedColumnIndices(nsTArray& aColsArray) final; NS_IMETHOD GetSelectedRowIndices(nsTArray& aRowsArray) final; - NS_IMETHOD SelectColumn(int32_t aColIdx) final; - NS_IMETHOD SelectRow(int32_t aRowIdx) final; - NS_IMETHOD UnselectColumn(int32_t aColIdx) final; - NS_IMETHOD UnselectRow(int32_t aRowIdx) final; NS_IMETHOD IsProbablyForLayout(bool* aIsForLayout) final; protected: virtual ~xpcAccessibleTable() {} private: - TableAccessibleBase* Intl() { return mIntl->AsTableBase(); } + TableAccessible* Intl() { return mIntl->AsTable(); } xpcAccessibleTable(const xpcAccessibleTable&) = delete; xpcAccessibleTable& operator=(const xpcAccessibleTable&) = delete; diff -Nru firefox-esr-115.3.1esr+build1/accessible/xul/XULListboxAccessible.cpp firefox-esr-115.4.0esr+build1/accessible/xul/XULListboxAccessible.cpp --- firefox-esr-115.3.1esr+build1/accessible/xul/XULListboxAccessible.cpp 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/xul/XULListboxAccessible.cpp 2023-10-17 05:45:43.000000000 +0000 @@ -309,40 +309,6 @@ } } -void XULListboxAccessible::SelectRow(uint32_t aRowIdx) { - nsCOMPtr control = - Elm()->AsXULMultiSelectControl(); - NS_ASSERTION(control, - "Doesn't implement nsIDOMXULMultiSelectControlElement."); - - RefPtr item; - control->GetItemAtIndex(aRowIdx, getter_AddRefs(item)); - if (!item) { - return; - } - - nsCOMPtr itemElm = - item->AsXULSelectControlItem(); - control->SelectItem(itemElm); -} - -void XULListboxAccessible::UnselectRow(uint32_t aRowIdx) { - nsCOMPtr control = - Elm()->AsXULMultiSelectControl(); - NS_ASSERTION(control, - "Doesn't implement nsIDOMXULMultiSelectControlElement."); - - RefPtr item; - control->GetItemAtIndex(aRowIdx, getter_AddRefs(item)); - if (!item) { - return; - } - - nsCOMPtr itemElm = - item->AsXULSelectControlItem(); - control->RemoveItemFromSelection(itemElm); -} - //////////////////////////////////////////////////////////////////////////////// // XULListboxAccessible: Widgets diff -Nru firefox-esr-115.3.1esr+build1/accessible/xul/XULListboxAccessible.h firefox-esr-115.4.0esr+build1/accessible/xul/XULListboxAccessible.h --- firefox-esr-115.3.1esr+build1/accessible/xul/XULListboxAccessible.h 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/xul/XULListboxAccessible.h 2023-10-17 05:45:43.000000000 +0000 @@ -7,8 +7,7 @@ #define mozilla_a11y_XULListboxAccessible_h__ #include "BaseAccessibles.h" -#include "TableAccessible.h" -#include "TableCellAccessible.h" +#include "mozilla/a11y/TableAccessible.h" #include "XULMenuAccessible.h" #include "XULSelectControlAccessible.h" @@ -70,8 +69,6 @@ virtual void SelectedCellIndices(nsTArray* aCells) override; virtual void SelectedColIndices(nsTArray* aCols) override; virtual void SelectedRowIndices(nsTArray* aRows) override; - virtual void SelectRow(uint32_t aRowIdx) override; - virtual void UnselectRow(uint32_t aRowIdx) override; virtual LocalAccessible* AsAccessible() override { return this; } // LocalAccessible diff -Nru firefox-esr-115.3.1esr+build1/accessible/xul/XULTreeGridAccessible.cpp firefox-esr-115.4.0esr+build1/accessible/xul/XULTreeGridAccessible.cpp --- firefox-esr-115.3.1esr+build1/accessible/xul/XULTreeGridAccessible.cpp 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/xul/XULTreeGridAccessible.cpp 2023-10-17 05:45:43.000000000 +0000 @@ -5,6 +5,7 @@ #include "XULTreeGridAccessible.h" +#include #include "AccAttributes.h" #include "LocalAccessible-inl.h" #include "nsAccCache.h" @@ -21,6 +22,7 @@ #include "nsITreeSelection.h" #include "nsComponentManagerUtils.h" #include "mozilla/PresShell.h" +#include "mozilla/a11y/TableAccessible.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/TreeColumnBinding.h" #include "mozilla/dom/XULTreeElementBinding.h" @@ -149,23 +151,36 @@ return IsRowSelected(aRowIdx); } -void XULTreeGridAccessible::SelectRow(uint32_t aRowIdx) { - if (!mTreeView) return; - - nsCOMPtr selection; - mTreeView->GetSelection(getter_AddRefs(selection)); - NS_ASSERTION(selection, "GetSelection() Shouldn't fail!"); +int32_t XULTreeGridAccessible::ColIndexAt(uint32_t aCellIdx) { + uint32_t colCount = ColCount(); + if (colCount < 1 || aCellIdx >= colCount * RowCount()) { + return -1; // Error: column count is 0 or index out of bounds. + } - selection->Select(aRowIdx); + return static_cast(aCellIdx % colCount); } -void XULTreeGridAccessible::UnselectRow(uint32_t aRowIdx) { - if (!mTreeView) return; +int32_t XULTreeGridAccessible::RowIndexAt(uint32_t aCellIdx) { + uint32_t colCount = ColCount(); + if (colCount < 1 || aCellIdx >= colCount * RowCount()) { + return -1; // Error: column count is 0 or index out of bounds. + } - nsCOMPtr selection; - mTreeView->GetSelection(getter_AddRefs(selection)); + return static_cast(aCellIdx / colCount); +} + +void XULTreeGridAccessible::RowAndColIndicesAt(uint32_t aCellIdx, + int32_t* aRowIdx, + int32_t* aColIdx) { + uint32_t colCount = ColCount(); + if (colCount < 1 || aCellIdx >= colCount * RowCount()) { + *aRowIdx = -1; + *aColIdx = -1; + return; // Error: column count is 0 or index out of bounds. + } - if (selection) selection->ClearRange(aRowIdx, aRowIdx); + *aRowIdx = static_cast(aCellIdx / colCount); + *aColIdx = static_cast(aCellIdx % colCount); } //////////////////////////////////////////////////////////////////////////////// diff -Nru firefox-esr-115.3.1esr+build1/accessible/xul/XULTreeGridAccessible.h firefox-esr-115.4.0esr+build1/accessible/xul/XULTreeGridAccessible.h --- firefox-esr-115.3.1esr+build1/accessible/xul/XULTreeGridAccessible.h 2023-09-29 10:59:36.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/accessible/xul/XULTreeGridAccessible.h 2023-10-17 05:45:43.000000000 +0000 @@ -7,9 +7,9 @@ #ifndef mozilla_a11y_XULTreeGridAccessible_h__ #define mozilla_a11y_XULTreeGridAccessible_h__ +#include "mozilla/a11y/TableAccessible.h" +#include "mozilla/a11y/TableCellAccessible.h" #include "XULTreeAccessible.h" -#include "TableAccessible.h" -#include "TableCellAccessible.h" namespace mozilla { namespace a11y { @@ -44,10 +44,17 @@ virtual void SelectedCellIndices(nsTArray* aCells) override; virtual void SelectedColIndices(nsTArray* aCols) override; virtual void SelectedRowIndices(nsTArray* aRows) override; - virtual void SelectRow(uint32_t aRowIdx) override; - virtual void UnselectRow(uint32_t aRowIdx) override; virtual LocalAccessible* AsAccessible() override { return this; } + virtual int32_t CellIndexAt(uint32_t aRowIdx, uint32_t aColIdx) override { + return static_cast(ColCount() * aRowIdx + aColIdx); + } + + virtual int32_t ColIndexAt(uint32_t aCellIdx) override; + virtual int32_t RowIndexAt(uint32_t aCellIdx) override; + virtual void RowAndColIndicesAt(uint32_t aCellIdx, int32_t* aRowIdx, + int32_t* aColIdx) override; + // LocalAccessible virtual TableAccessible* AsTable() override { return this; } virtual a11y::role NativeRole() const override; diff -Nru firefox-esr-115.3.1esr+build1/browser/app/winlauncher/freestanding/DllBlocklist.cpp firefox-esr-115.4.0esr+build1/browser/app/winlauncher/freestanding/DllBlocklist.cpp --- firefox-esr-115.3.1esr+build1/browser/app/winlauncher/freestanding/DllBlocklist.cpp 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/browser/app/winlauncher/freestanding/DllBlocklist.cpp 2023-10-17 05:45:44.000000000 +0000 @@ -465,6 +465,7 @@ if (::RtlCompareUnicodeString(&k32Name, &leafOnStack, TRUE) == 0) { blockAction = BlockAction::Allow; } else { + auto noSharedSectionReset{SharedSection::AutoNoReset()}; k32Exports = gSharedSection.GetKernel32Exports(); // Small optimization: Since loading a dependent module does not involve // LdrLoadDll, we know isInjectedDependent is false if we hold a top frame. diff -Nru firefox-esr-115.3.1esr+build1/browser/app/winlauncher/freestanding/SharedSection.cpp firefox-esr-115.4.0esr+build1/browser/app/winlauncher/freestanding/SharedSection.cpp --- firefox-esr-115.3.1esr+build1/browser/app/winlauncher/freestanding/SharedSection.cpp 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/browser/app/winlauncher/freestanding/SharedSection.cpp 2023-10-17 05:45:43.000000000 +0000 @@ -113,8 +113,10 @@ HANDLE SharedSection::sSectionHandle = nullptr; SharedSection::Layout* SharedSection::sWriteCopyView = nullptr; RTL_RUN_ONCE SharedSection::sEnsureOnce = RTL_RUN_ONCE_INIT; +nt::SRWLock SharedSection::sLock; void SharedSection::Reset(HANDLE aNewSectionObject) { + nt::AutoExclusiveLock{sLock}; if (sWriteCopyView) { nt::AutoMappedView view(sWriteCopyView); sWriteCopyView = nullptr; diff -Nru firefox-esr-115.3.1esr+build1/browser/app/winlauncher/freestanding/SharedSection.h firefox-esr-115.4.0esr+build1/browser/app/winlauncher/freestanding/SharedSection.h --- firefox-esr-115.3.1esr+build1/browser/app/winlauncher/freestanding/SharedSection.h 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/browser/app/winlauncher/freestanding/SharedSection.h 2023-10-17 05:45:44.000000000 +0000 @@ -152,6 +152,15 @@ static Layout* sWriteCopyView; static RTL_RUN_ONCE sEnsureOnce; + // The sLock lock guarantees that while it is held, sSectionHandle will not + // change nor get closed, sEnsureOnce will not get reinitialized, and + // sWriteCopyView will not change nor get unmapped once initialized. We take + // sLock on paths that could run concurrently with ConvertToReadOnly(). This + // method is only called on the main process, and very early, so the only + // real risk here should be threads started by third-party products reaching + // our patched_NtMapViewOfSection (see bug 1850969). + static nt::SRWLock sLock; + static ULONG NTAPI EnsureWriteCopyViewOnce(PRTL_RUN_ONCE, PVOID, PVOID*); static Layout* EnsureWriteCopyView(bool requireKernel32Exports = false); @@ -164,6 +173,10 @@ // Replace |sSectionHandle| with a given handle. static void Reset(HANDLE aNewSectionObject = sSectionHandle); + static inline nt::AutoSharedLock AutoNoReset() { + return nt::AutoSharedLock{sLock}; + } + // Replace |sSectionHandle| with a new readonly handle. static void ConvertToReadOnly(); diff -Nru firefox-esr-115.3.1esr+build1/browser/base/content/tabbrowser.js firefox-esr-115.4.0esr+build1/browser/base/content/tabbrowser.js --- firefox-esr-115.3.1esr+build1/browser/base/content/tabbrowser.js 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/browser/base/content/tabbrowser.js 2023-10-17 05:45:44.000000000 +0000 @@ -759,7 +759,10 @@ }, _notifyPinnedStatus(aTab) { - aTab.linkedBrowser.browsingContext.isAppTab = aTab.pinned; + // browsingContext is expected to not be defined on discarded tabs. + if (aTab.linkedBrowser.browsingContext) { + aTab.linkedBrowser.browsingContext.isAppTab = aTab.pinned; + } let event = document.createEvent("Events"); event.initEvent(aTab.pinned ? "TabPinned" : "TabUnpinned", true, false); diff -Nru firefox-esr-115.3.1esr+build1/browser/base/content/test/tabs/browser_pinnedTabs.js firefox-esr-115.4.0esr+build1/browser/base/content/test/tabs/browser_pinnedTabs.js --- firefox-esr-115.3.1esr+build1/browser/base/content/test/tabs/browser_pinnedTabs.js 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/browser/base/content/test/tabs/browser_pinnedTabs.js 2023-10-17 05:45:44.000000000 +0000 @@ -47,6 +47,10 @@ indexTest(2, 2); indexTest(3, 3); + // Discard one of the test tabs to verify that pinning/unpinning + // discarded tabs does not regress (regression test for Bug 1852391). + gBrowser.discardBrowser(tabs[1], true); + var eh = new PinUnpinHandler(tabs[3], "TabPinned"); gBrowser.pinTab(tabs[3]); is(eh.eventCount, 2, "TabPinned event should be fired"); diff -Nru firefox-esr-115.3.1esr+build1/browser/components/extensions/parent/ext-windows.js firefox-esr-115.4.0esr+build1/browser/components/extensions/parent/ext-windows.js --- firefox-esr-115.3.1esr+build1/browser/components/extensions/parent/ext-windows.js 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/browser/components/extensions/parent/ext-windows.js 2023-10-17 05:45:43.000000000 +0000 @@ -150,26 +150,6 @@ const { windowManager } = extension; - function getTriggeringPrincipalForUrl(url) { - if (context.checkLoadURL(url, { dontReportErrors: true })) { - return context.principal; - } - let window = context.currentWindow || windowTracker.topWindow; - // The extension principal cannot directly load about:-URLs except for about:blank, and - // possibly some other loads such as moz-extension. Ensure any page set as a home page - // will load by using a content principal. - return Services.scriptSecurityManager.createContentPrincipal( - Services.io.newURI(url), - { - privateBrowsingId: PrivateBrowsingUtils.isBrowserPrivate( - window.gBrowser - ) - ? 1 - : 0, - } - ); - } - return { windows: { onCreated: new EventManager({ @@ -265,11 +245,23 @@ Ci.nsIMutableArray ); + // Whether there is only one URL to load, and it is a moz-extension:-URL. + let isOnlyMozExtensionUrl = false; + // Creating a new window allows one single triggering principal for all tabs that // are created in the window. Due to that, if we need a browser principal to load // some urls, we fallback to using a content principal like we do in the tabs api. // Throws if url is an array and any url can't be loaded by the extension principal. - let { allowScriptsToClose, principal } = createData; + let principal = context.principal; + function setContentTriggeringPrincipal(url) { + principal = Services.scriptSecurityManager.createContentPrincipal( + Services.io.newURI(url), + { + // Note: privateBrowsingAllowed was already checked before. + privateBrowsingId: createData.incognito ? 1 : 0, + } + ); + } if (createData.tabId !== null) { if (createData.url !== null) { @@ -330,12 +322,22 @@ array.appendElement(mkstr(url)); } args.appendElement(array); + // TODO bug 1780583: support multiple triggeringPrincipals to + // avoid having to use the system principal here. + principal = Services.scriptSecurityManager.getSystemPrincipal(); } else { let url = context.uri.resolve(createData.url); args.appendElement(mkstr(url)); - principal = getTriggeringPrincipalForUrl(url); - if (allowScriptsToClose === null) { - allowScriptsToClose = url.startsWith("moz-extension://"); + isOnlyMozExtensionUrl = url.startsWith("moz-extension://"); + if (!context.checkLoadURL(url, { dontReportErrors: true })) { + if (isOnlyMozExtensionUrl) { + // For backwards-compatibility (also in tabs APIs), we allow + // extensions to open other moz-extension:-URLs even if that + // other resource is not listed in web_accessible_resources. + setContentTriggeringPrincipal(url); + } else { + throw new ExtensionError(`Illegal URL: ${url}`); + } } } } else { @@ -345,7 +347,15 @@ ? "about:privatebrowsing" : HomePage.get().split("|", 1)[0]; args.appendElement(mkstr(url)); - principal = getTriggeringPrincipalForUrl(url); + isOnlyMozExtensionUrl = url.startsWith("moz-extension://"); + + if (!context.checkLoadURL(url, { dontReportErrors: true })) { + // The extension principal cannot directly load about:-URLs, + // except for about:blank, or other moz-extension:-URLs that are + // not in web_accessible_resources. Ensure any page set as a home + // page will load by using a content principal. + setContentTriggeringPrincipal(url); + } } args.appendElement(null); // extraOptions @@ -432,6 +442,10 @@ window.addEventListener( "DOMContentLoaded", function () { + let { allowScriptsToClose } = createData; + if (allowScriptsToClose === null && isOnlyMozExtensionUrl) { + allowScriptsToClose = true; + } if (allowScriptsToClose) { window.gBrowserAllowScriptsToCloseInitialTabs = true; } diff -Nru firefox-esr-115.3.1esr+build1/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated.js firefox-esr-115.4.0esr+build1/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated.js --- firefox-esr-115.3.1esr+build1/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated.js 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated.js 2023-10-17 05:45:43.000000000 +0000 @@ -294,4 +294,46 @@ await extension.unload(); }); +// Regression test for Bug 1852391. +add_task(async function test_pin_discarded_tab() { + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["tabs"], + }, + async background() { + const url = "http://mochi.test:8888"; + const newTab = await browser.tabs.create({ + url, + active: false, + discarded: true, + }); + browser.tabs.onUpdated.addListener( + async (tabId, changeInfo) => { + browser.test.assertEq( + tabId, + newTab.id, + "Expect onUpdated to be fired for the expected tab" + ); + browser.test.assertEq( + changeInfo.pinned, + true, + "Expect pinned to be set to true" + ); + await browser.tabs.remove(newTab.id); + browser.test.notifyPass("onPinned"); + }, + { properties: ["pinned"] } + ); + await browser.tabs.update(newTab.id, { pinned: true }).catch(err => { + browser.test.fail(`Got unexpected rejection from tabs.update: ${err}`); + browser.test.notifyFail("onPinned"); + }); + }, + }); + + await extension.startup(); + await extension.awaitFinish("onPinned"); + await extension.unload(); +}); + add_task(forceGC); diff -Nru firefox-esr-115.3.1esr+build1/browser/components/extensions/test/browser/browser_ext_windows_create_url.js firefox-esr-115.4.0esr+build1/browser/components/extensions/test/browser/browser_ext_windows_create_url.js --- firefox-esr-115.3.1esr+build1/browser/components/extensions/test/browser/browser_ext_windows_create_url.js 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/browser/components/extensions/test/browser/browser_ext_windows_create_url.js 2023-10-17 05:45:44.000000000 +0000 @@ -60,6 +60,7 @@ async function create(options) { browser.test.log(`creating window for ${options.url}`); + // Note: may reject let window = await browser.windows.create(options); let win = windows.get(window.id); win.id = window.id; @@ -69,17 +70,6 @@ return win.promise; } - function createFail(options) { - return browser.windows - .create(options) - .then(() => { - browser.test.fail(`window opened with ${options.url}`); - }) - .catch(() => { - browser.test.succeed(`window could not open with ${options.url}`); - }); - } - let TEST_SETS = [ { name: "Single protocol URL in this extension", @@ -97,16 +87,35 @@ expect: [EXTENSION_URL], }, { + // This is primarily for backwards-compatibility, to allow extensions + // to open other home pages. This test case opens the home page + // explicitly; the implicit case (windows.create({}) without URL) is at + // browser_ext_chrome_settings_overrides_home.js. name: "Single, absolute, other extension URL", url: OTHER_PAGE, expect: [OTHER_PAGE], }, { + // This is oddly inconsistent with the non-array case, but here we are + // intentionally stricter because of lesser backwards-compatibility + // concerns. + name: "Array, absolute, other extension URL", + url: [OTHER_PAGE], + expectError: `Illegal URL: ${OTHER_PAGE}`, + }, + { name: "Single protocol URL in other extension", url: OTHER_PROTO, expect: [`${OTHER_PAGE}?val=ext%2Bfoo%3Abar`], }, { + name: "Single, about:blank", + // Added "?" after "about:blank" because the test's tab load detection + // ignores about:blank. + url: "about:blank?", + expect: ["about:blank?"], + }, + { name: "multiple urls", url: [EXT_PROTO, "test.html", EXTENSION_URL, OTHER_PROTO], expect: [ @@ -116,31 +125,54 @@ `${OTHER_PAGE}?val=ext%2Bfoo%3Abar`, ], }, + { + name: "Reject array of own allowed URLs and other moz-extension:-URL", + url: [EXTENSION_URL, EXT_PROTO, "about:blank?#", OTHER_PAGE], + expectError: `Illegal URL: ${OTHER_PAGE}`, + }, + { + name: "Single, about:robots", + url: "about:robots", + expectError: "Illegal URL: about:robots", + }, + { + name: "Array containing about:robots", + url: ["about:robots"], + expectError: "Illegal URL: about:robots", + }, ]; + async function checkCreateResult({ status, value, reason }, testCase) { + const window = status === "fulfilled" ? value : null; + try { + if (testCase.expectError) { + let error = reason?.message; + browser.test.assertEq(testCase.expectError, error, testCase.name); + } else { + let tabUrls = []; + for (let [tabIndex, tab] of window.tabs) { + tabUrls[tabIndex] = tab.url; + } + browser.test.assertDeepEq(testCase.expect, tabUrls, testCase.name); + } + } catch (e) { + browser.test.fail(`Unexpected failure in ${testCase.name} :: ${e}`); + } finally { + // Close opened windows, whether they were expected or not. + if (window) { + await browser.windows.remove(window.id); + } + } + } try { - let windows = await Promise.all( + // First collect all results, in parallel. + const results = await Promise.allSettled( TEST_SETS.map(t => create({ url: t.url })) ); - - TEST_SETS.forEach((test, i) => { - test.expect.forEach((expectUrl, x) => { - browser.test.assertEq( - expectUrl, - windows[i].tabs.get(x)?.url, - TEST_SETS[i].name - ); - }); - }); - - Promise.all(windows.map(({ id }) => browser.windows.remove(id))).then( - () => { - browser.test.notifyPass("window-create-url"); - } + // Then check the results sequentially + await Promise.all( + TEST_SETS.map((t, i) => checkCreateResult(results[i], t)) ); - - // Expecting to fail when opening windows with multiple urls that includes - // other extension urls. - await Promise.all([createFail({ url: [EXTENSION_URL, OTHER_PAGE] })]); + browser.test.notifyPass("window-create-url"); } catch (e) { browser.test.fail(`${e} :: ${e.stack}`); browser.test.notifyFail("window-create-url"); diff -Nru firefox-esr-115.3.1esr+build1/browser/components/preferences/dialogs/sitePermissions.css firefox-esr-115.4.0esr+build1/browser/components/preferences/dialogs/sitePermissions.css --- firefox-esr-115.3.1esr+build1/browser/components/preferences/dialogs/sitePermissions.css 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/browser/components/preferences/dialogs/sitePermissions.css 2023-10-17 05:45:44.000000000 +0000 @@ -12,6 +12,7 @@ #permissionsBox { flex: 1 auto; height: 18em; + min-height: 70px; /* 2 * 35px, which is the min row height specified below */ } #siteCol, diff -Nru firefox-esr-115.3.1esr+build1/browser/components/urlbar/tests/browser/browser_UrlbarInput_formatValue.js firefox-esr-115.4.0esr+build1/browser/components/urlbar/tests/browser/browser_UrlbarInput_formatValue.js --- firefox-esr-115.3.1esr+build1/browser/components/urlbar/tests/browser/browser_UrlbarInput_formatValue.js 2023-09-29 10:59:38.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/browser/components/urlbar/tests/browser/browser_UrlbarInput_formatValue.js 2023-10-17 05:45:44.000000000 +0000 @@ -95,6 +95,9 @@ testVal("mozilla.org< >"); testVal("mozilla.org< >"); + // RTL characters in domain change order of domain and suffix. Domain should + // be highlighted correctly. + testVal("اختبار.اختبار"); testVal("mozilla.org"); testVal("mozilla.org"); diff -Nru firefox-esr-115.3.1esr+build1/browser/components/urlbar/UrlbarInput.sys.mjs firefox-esr-115.4.0esr+build1/browser/components/urlbar/UrlbarInput.sys.mjs --- firefox-esr-115.3.1esr+build1/browser/components/urlbar/UrlbarInput.sys.mjs 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/browser/components/urlbar/UrlbarInput.sys.mjs 2023-10-17 05:45:44.000000000 +0000 @@ -2579,9 +2579,14 @@ * The trimmed string */ _trimValue(val) { - return lazy.UrlbarPrefs.get("trimURLs") + let trimmedValue = lazy.UrlbarPrefs.get("trimURLs") ? lazy.BrowserUIUtils.trimURL(val) : val; + // Only trim value if the directionality doesn't change to RTL. + return this.window.windowUtils.getDirectionFromText(trimmedValue) == + this.window.windowUtils.DIRECTION_RTL + ? val + : trimmedValue; } /** diff -Nru firefox-esr-115.3.1esr+build1/browser/config/version_display.txt firefox-esr-115.4.0esr+build1/browser/config/version_display.txt --- firefox-esr-115.3.1esr+build1/browser/config/version_display.txt 2023-09-29 11:00:31.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/browser/config/version_display.txt 2023-10-17 05:45:44.000000000 +0000 @@ -1 +1 @@ -115.3.1esr +115.4.0esr diff -Nru firefox-esr-115.3.1esr+build1/browser/config/version.txt firefox-esr-115.4.0esr+build1/browser/config/version.txt --- firefox-esr-115.3.1esr+build1/browser/config/version.txt 2023-09-29 11:00:31.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/browser/config/version.txt 2023-10-17 05:45:44.000000000 +0000 @@ -1 +1 @@ -115.3.1 +115.4.0 diff -Nru firefox-esr-115.3.1esr+build1/BUILDID firefox-esr-115.4.0esr+build1/BUILDID --- firefox-esr-115.3.1esr+build1/BUILDID 2023-09-29 11:08:16.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/BUILDID 2023-10-17 05:52:54.000000000 +0000 @@ -1 +1 @@ -20230927234915 \ No newline at end of file +20231016232700 \ No newline at end of file diff -Nru firefox-esr-115.3.1esr+build1/caps/nsScriptSecurityManager.cpp firefox-esr-115.4.0esr+build1/caps/nsScriptSecurityManager.cpp --- firefox-esr-115.3.1esr+build1/caps/nsScriptSecurityManager.cpp 2023-09-29 10:59:38.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/caps/nsScriptSecurityManager.cpp 2023-10-17 05:45:44.000000000 +0000 @@ -78,7 +78,7 @@ using namespace mozilla; using namespace mozilla::dom; -nsIIOService* nsScriptSecurityManager::sIOService = nullptr; +StaticRefPtr nsScriptSecurityManager::sIOService; std::atomic nsScriptSecurityManager::sStrictFileOriginPolicy = true; namespace { @@ -1548,9 +1548,12 @@ } nsresult nsScriptSecurityManager::Init() { - nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService); - NS_ENSURE_SUCCESS(rv, rv); - + nsresult rv; + RefPtr io = mozilla::components::IO::Service(&rv); + if (NS_FAILED(rv)) { + return rv; + } + sIOService = std::move(io); InitPrefs(); // Create our system principal singleton @@ -1596,7 +1599,7 @@ } void nsScriptSecurityManager::Shutdown() { - NS_IF_RELEASE(sIOService); + sIOService = nullptr; BundleHelper::Shutdown(); SystemPrincipal::Shutdown(); } diff -Nru firefox-esr-115.3.1esr+build1/caps/nsScriptSecurityManager.h firefox-esr-115.4.0esr+build1/caps/nsScriptSecurityManager.h --- firefox-esr-115.3.1esr+build1/caps/nsScriptSecurityManager.h 2023-09-29 10:59:37.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/caps/nsScriptSecurityManager.h 2023-10-17 05:45:44.000000000 +0000 @@ -135,7 +135,7 @@ static std::atomic sStrictFileOriginPolicy; - static nsIIOService* sIOService; + static mozilla::StaticRefPtr sIOService; static nsIStringBundle* sStrBundle; }; diff -Nru firefox-esr-115.3.1esr+build1/CLOBBER firefox-esr-115.4.0esr+build1/CLOBBER --- firefox-esr-115.3.1esr+build1/CLOBBER 2023-09-29 11:00:31.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/CLOBBER 2023-10-17 05:45:43.000000000 +0000 @@ -22,4 +22,4 @@ # changes to stick? As of bug 928195, this shouldn't be necessary! Please # don't change CLOBBER for WebIDL changes any more. -Merge day clobber 2023-08-28 \ No newline at end of file +Merge day clobber 2023-09-25 \ No newline at end of file diff -Nru firefox-esr-115.3.1esr+build1/config/milestone.txt firefox-esr-115.4.0esr+build1/config/milestone.txt --- firefox-esr-115.3.1esr+build1/config/milestone.txt 2023-09-29 11:00:31.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/config/milestone.txt 2023-10-17 05:45:44.000000000 +0000 @@ -10,4 +10,4 @@ # hardcoded milestones in the tree from these two files. #-------------------------------------------------------- -115.3.1 +115.4.0 diff -Nru firefox-esr-115.3.1esr+build1/.cron.yml firefox-esr-115.4.0esr+build1/.cron.yml --- firefox-esr-115.3.1esr+build1/.cron.yml 2023-09-29 11:00:31.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/.cron.yml 2023-10-17 05:45:43.000000000 +0000 @@ -205,7 +205,6 @@ - mozilla-central - mozilla-beta - mozilla-release - - mozilla-esr102 - mozilla-esr115 when: by-project: @@ -219,10 +218,9 @@ mozilla-release: - {hour: 7, minute: 0} - {hour: 19, minute: 0} - mozilla-esr102: + mozilla-esr115: - {hour: 7, minute: 0} - {hour: 19, minute: 0} - mozilla-esr115: [] - name: periodic-update job: diff -Nru firefox-esr-115.3.1esr+build1/debian/changelog firefox-esr-115.4.0esr+build1/debian/changelog --- firefox-esr-115.3.1esr+build1/debian/changelog 2023-09-29 10:58:25.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/debian/changelog 2023-10-17 05:51:00.000000000 +0000 @@ -1,3 +1,9 @@ +firefox-esr (115.4.0esr+build1-0ubuntu0.20.04.1~mt1) focal; urgency=medium + + * New upstream stable release (FIREFOX_115_4_0esr_BUILD1) + + -- Rico Tzschichholz Tue, 17 Oct 2023 07:51:00 +0200 + firefox-esr (115.3.1esr+build1-0ubuntu0.20.04.1~mt1) focal; urgency=medium * New upstream stable release (FIREFOX_115_3_1esr_BUILD1) diff -Nru firefox-esr-115.3.1esr+build1/docshell/base/nsDocShell.cpp firefox-esr-115.4.0esr+build1/docshell/base/nsDocShell.cpp --- firefox-esr-115.3.1esr+build1/docshell/base/nsDocShell.cpp 2023-09-29 10:59:39.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/docshell/base/nsDocShell.cpp 2023-10-17 05:45:44.000000000 +0000 @@ -1304,6 +1304,7 @@ : nullptr; if (mBrowsingContext->IsTop()) { doc->NotifyPossibleTitleChange(false); + doc->SetLoadingOrRestoredFromBFCacheTimeStampToNow(); if (inner) { // Now that we have found the inner window of the page restored // from the history, we have to make sure that diff -Nru firefox-esr-115.3.1esr+build1/docshell/shistory/nsSHistory.cpp firefox-esr-115.4.0esr+build1/docshell/shistory/nsSHistory.cpp --- firefox-esr-115.3.1esr+build1/docshell/shistory/nsSHistory.cpp 2023-09-29 10:59:39.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/docshell/shistory/nsSHistory.cpp 2023-10-17 05:45:44.000000000 +0000 @@ -1370,7 +1370,7 @@ ->GetCurrentWindowGlobal()) { wgp->PermitUnload([canonicalBC, loadState, she, frameLoader, currentFrameLoader, canSave](bool aAllow) { - if (aAllow) { + if (aAllow && !canonicalBC->IsReplaced()) { FinishRestore(canonicalBC, loadState, she, frameLoader, canSave && canonicalBC->AllowedInBFCache( Nothing(), nullptr)); diff -Nru firefox-esr-115.3.1esr+build1/dom/base/Document.cpp firefox-esr-115.4.0esr+build1/dom/base/Document.cpp --- firefox-esr-115.3.1esr+build1/dom/base/Document.cpp 2023-09-29 10:59:39.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/base/Document.cpp 2023-10-17 05:45:44.000000000 +0000 @@ -12181,7 +12181,7 @@ } if (aUpdateTimingInformation && READYSTATE_LOADING == aReadyState) { - mLoadingTimeStamp = TimeStamp::Now(); + SetLoadingOrRestoredFromBFCacheTimeStampToNow(); } NotifyLoading(mAncestorIsLoading, mReadyState, aReadyState); mReadyState = aReadyState; @@ -12479,8 +12479,8 @@ return; } - nsCOMPtr speculator( - do_QueryInterface(nsContentUtils::GetIOService())); + nsCOMPtr speculator = + mozilla::components::IO::Service(); if (!speculator) { return; } @@ -13531,8 +13531,9 @@ void Document::SetNavigationTiming(nsDOMNavigationTiming* aTiming) { mTiming = aTiming; - if (!mLoadingTimeStamp.IsNull() && mTiming) { - mTiming->SetDOMLoadingTimeStamp(GetDocumentURI(), mLoadingTimeStamp); + if (!mLoadingOrRestoredFromBFCacheTimeStamp.IsNull() && mTiming) { + mTiming->SetDOMLoadingTimeStamp(GetDocumentURI(), + mLoadingOrRestoredFromBFCacheTimeStamp); } // If there's already the DocumentTimeline instance, tell it since the diff -Nru firefox-esr-115.3.1esr+build1/dom/base/Document.h firefox-esr-115.4.0esr+build1/dom/base/Document.h --- firefox-esr-115.3.1esr+build1/dom/base/Document.h 2023-09-29 10:59:39.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/base/Document.h 2023-10-17 05:45:44.000000000 +0000 @@ -1048,6 +1048,13 @@ void SetLoadedAsData(bool aLoadedAsData, bool aConsiderForMemoryReporting); + TimeStamp GetLoadingOrRestoredFromBFCacheTimeStamp() const { + return mLoadingOrRestoredFromBFCacheTimeStamp; + } + void SetLoadingOrRestoredFromBFCacheTimeStampToNow() { + mLoadingOrRestoredFromBFCacheTimeStamp = TimeStamp::Now(); + } + /** * Normally we assert if a runnable labeled with one DocGroup touches data * from another DocGroup. Calling IgnoreDocGroupMismatches() on a document @@ -5203,8 +5210,9 @@ RefPtr mTiming; - // Recorded time of change to 'loading' state. - TimeStamp mLoadingTimeStamp; + // Recorded time of change to 'loading' state + // or time of the page gets restored from BFCache. + TimeStamp mLoadingOrRestoredFromBFCacheTimeStamp; // Decided to use nsTObserverArray because it allows us to // remove candidates while iterating them and this is what diff -Nru firefox-esr-115.3.1esr+build1/dom/base/Element.cpp firefox-esr-115.4.0esr+build1/dom/base/Element.cpp --- firefox-esr-115.3.1esr+build1/dom/base/Element.cpp 2023-09-29 10:59:39.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/base/Element.cpp 2023-10-17 05:45:44.000000000 +0000 @@ -26,6 +26,7 @@ #include "mozilla/AnimationTarget.h" #include "mozilla/AsyncEventDispatcher.h" #include "mozilla/CORSMode.h" +#include "mozilla/Components.h" #include "mozilla/ComputedStyle.h" #include "mozilla/ContentEvents.h" #include "mozilla/DebugOnly.h" @@ -3259,10 +3260,11 @@ // connection to be sure we have one ready when we open the channel. if (nsIDocShell* shell = OwnerDoc()->GetDocShell()) { if (nsCOMPtr absURI = GetHrefURI()) { - nsCOMPtr sc = - do_QueryInterface(nsContentUtils::GetIOService()); - nsCOMPtr ir = do_QueryInterface(shell); - sc->SpeculativeConnect(absURI, NodePrincipal(), ir, false); + if (nsCOMPtr sc = + mozilla::components::IO::Service()) { + nsCOMPtr ir = do_QueryInterface(shell); + sc->SpeculativeConnect(absURI, NodePrincipal(), ir, false); + } } } } diff -Nru firefox-esr-115.3.1esr+build1/dom/base/nsContentUtils.cpp firefox-esr-115.4.0esr+build1/dom/base/nsContentUtils.cpp --- firefox-esr-115.3.1esr+build1/dom/base/nsContentUtils.cpp 2023-09-29 10:59:39.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/base/nsContentUtils.cpp 2023-10-17 05:45:44.000000000 +0000 @@ -420,7 +420,6 @@ nsIScriptSecurityManager* nsContentUtils::sSecurityManager; nsIPrincipal* nsContentUtils::sSystemPrincipal; nsIPrincipal* nsContentUtils::sNullSubjectPrincipal; -nsIIOService* nsContentUtils::sIOService; nsIConsoleService* nsContentUtils::sConsoleService; nsTHashMap, EventNameMapping>* nsContentUtils::sAtomEventTable = nullptr; @@ -802,13 +801,6 @@ nullPrincipal.forget(&sNullSubjectPrincipal); - nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService); - if (NS_FAILED(rv)) { - // This makes life easier, but we can live without it. - - sIOService = nullptr; - } - if (!InitializeEventTable()) return NS_ERROR_FAILURE; if (!sEventListenerManagersHash) { @@ -1894,7 +1886,6 @@ NS_IF_RELEASE(sSecurityManager); NS_IF_RELEASE(sSystemPrincipal); NS_IF_RELEASE(sNullSubjectPrincipal); - NS_IF_RELEASE(sIOService); sBidiKeyboard = nullptr; @@ -2101,8 +2092,15 @@ return true; } + nsresult rv = NS_OK; + nsCOMPtr io = mozilla::components::IO::Service(&rv); + MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); + if (NS_FAILED(rv)) { + return false; + } + uint32_t flags; - if (NS_SUCCEEDED(sIOService->GetProtocolFlags(scheme.get(), &flags))) { + if (NS_SUCCEEDED(io->GetProtocolFlags(scheme.get(), &flags))) { return flags & nsIProtocolHandler::URI_NORELATIVE; } @@ -6365,7 +6363,7 @@ /* static */ bool nsContentUtils::URIIsLocalFile(nsIURI* aURI) { bool isFile; - nsCOMPtr util = do_QueryInterface(sIOService); + nsCOMPtr util = mozilla::components::IO::Service(); // Important: we do NOT test the entire URI chain here! return util && diff -Nru firefox-esr-115.3.1esr+build1/dom/base/nsContentUtils.h firefox-esr-115.4.0esr+build1/dom/base/nsContentUtils.h --- firefox-esr-115.3.1esr+build1/dom/base/nsContentUtils.h 2023-09-29 10:59:39.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/base/nsContentUtils.h 2023-10-17 05:45:44.000000000 +0000 @@ -840,8 +840,6 @@ // element. static bool InProlog(nsINode* aNode); - static nsIIOService* GetIOService() { return sIOService; } - static nsIBidiKeyboard* GetBidiKeyboard(); /** @@ -3461,8 +3459,6 @@ static nsIPrincipal* sSystemPrincipal; static nsIPrincipal* sNullSubjectPrincipal; - static nsIIOService* sIOService; - static nsIConsoleService* sConsoleService; static nsTHashMap, EventNameMapping>* sAtomEventTable; diff -Nru firefox-esr-115.3.1esr+build1/dom/base/nsObjectLoadingContent.cpp firefox-esr-115.4.0esr+build1/dom/base/nsObjectLoadingContent.cpp --- firefox-esr-115.3.1esr+build1/dom/base/nsObjectLoadingContent.cpp 2023-09-29 10:59:40.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/base/nsObjectLoadingContent.cpp 2023-10-17 05:45:45.000000000 +0000 @@ -176,8 +176,10 @@ return false; } - nsIIOService* ios = nsContentUtils::GetIOService(); - if (!ios) return false; + nsCOMPtr ios = mozilla::components::IO::Service(); + if (!ios) { + return false; + } nsCOMPtr handler; ios->GetProtocolHandler(scheme.get(), getter_AddRefs(handler)); diff -Nru firefox-esr-115.3.1esr+build1/dom/base/test/browser.ini firefox-esr-115.4.0esr+build1/dom/base/test/browser.ini --- firefox-esr-115.3.1esr+build1/dom/base/test/browser.ini 2023-09-29 10:59:40.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/base/test/browser.ini 2023-10-17 05:45:45.000000000 +0000 @@ -105,3 +105,8 @@ file_browser_refresh_iframe.sjs [browser_page_load_event_telemetry.js] [browser_xml_toggle.js] +[browser_user_input_handling_delay.js] +[browser_user_input_handling_delay_bfcache.js] +[browser_user_input_handling_delay_aboutblank.js] +[browser_user_input_handling_delay_invisible_iframe.js] +[browser_user_input_handling_delay_reload_ticks.js] diff -Nru firefox-esr-115.3.1esr+build1/dom/base/test/browser_user_input_handling_delay_aboutblank.js firefox-esr-115.4.0esr+build1/dom/base/test/browser_user_input_handling_delay_aboutblank.js --- firefox-esr-115.3.1esr+build1/dom/base/test/browser_user_input_handling_delay_aboutblank.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/base/test/browser_user_input_handling_delay_aboutblank.js 2023-10-17 05:45:44.000000000 +0000 @@ -0,0 +1,58 @@ +/* -*- Mode: JavaScript; 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/. */ + +async function test_user_input_handling_delay_aboutblank_helper(prefs) { + await SpecialPowers.pushPrefEnv({ + set: prefs, + }); + + let newTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, "about:blank"); + + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + // Open about:blank + content.window.open(); + }); + + const tab = await newTabOpened; + + let mouseDownPromise = BrowserTestUtils.waitForContentEvent( + tab.linkedBrowser, + "mousedown" + ).then(function () { + Assert.ok(true, "about:blank can handle user input events anytime"); + }); + + // Now gBrowser.selectedBrowser is the newly opened about:blank + await BrowserTestUtils.synthesizeMouseAtPoint( + 10, + 10, + { type: "mousedown" }, + tab.linkedBrowser + ); + + await mouseDownPromise; + BrowserTestUtils.removeTab(tab); +} + +add_task(async function test_MinRAF_aboutblank() { + const prefs = [ + ["dom.input_events.security.minNumTicks", 100], + ["dom.input_events.security.minTimeElapsedInMS", 0], + ["dom.input_events.security.isUserInputHandlingDelayTest", true], + ]; + + await test_user_input_handling_delay_aboutblank_helper(prefs); +}); + +add_task(async function test_MinElapsedTime_aboutblank() { + const prefs = [ + ["dom.input_events.security.minNumTicks", 0], + ["dom.input_events.security.minTimeElapsedInMS", 5000], + ["dom.input_events.security.isUserInputHandlingDelayTest", true], + ]; + + await test_user_input_handling_delay_aboutblank_helper(prefs); +}); diff -Nru firefox-esr-115.3.1esr+build1/dom/base/test/browser_user_input_handling_delay_bfcache.js firefox-esr-115.4.0esr+build1/dom/base/test/browser_user_input_handling_delay_bfcache.js --- firefox-esr-115.3.1esr+build1/dom/base/test/browser_user_input_handling_delay_bfcache.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/base/test/browser_user_input_handling_delay_bfcache.js 2023-10-17 05:45:44.000000000 +0000 @@ -0,0 +1,107 @@ +/* -*- Mode: JavaScript; 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/. */ + +async function test_user_input_handling_delay_BFCache_helper(prefs) { + await SpecialPowers.pushPrefEnv({ + set: prefs, + }); + + const tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + `data:text/html,`, + true + ); + + let switchAwayPromise = BrowserTestUtils.browserLoaded( + browser, + false, + "about:blank" + ); + // Navigate away to make the page enters BFCache + await SpecialPowers.spawn(tab.linkedBrowser, [], () => { + content.location = "about:blank"; + }); + await switchAwayPromise; + + // Navigate back to restore the page from BFCache + let pageShownPromise = BrowserTestUtils.waitForContentEvent( + tab.linkedBrowser, + "pageshow", + true + ); + + await SpecialPowers.spawn(tab.linkedBrowser, [], () => { + content.history.back(); + }); + + await pageShownPromise; + + let canHandleInput = false; + let mouseDownPromise = BrowserTestUtils.waitForContentEvent( + tab.linkedBrowser, + "mousedown" + ).then(function () { + Assert.ok( + canHandleInput, + "This promise should be resolved after the 5 seconds mark has passed" + ); + }); + // Ensure the events are discarded initially + for (let i = 0; i < 10; ++i) { + await BrowserTestUtils.synthesizeMouseAtPoint( + 10, + 10, + { type: "mousedown" }, + tab.linkedBrowser + ); + } + + // Wait for roughly 5 seconds to give chances for the + // above mousedown event to be handled. + await SpecialPowers.spawn(tab.linkedBrowser, [], async () => { + for (let i = 0; i < 330; ++i) { + await new Promise(r => { + content.requestAnimationFrame(r); + }); + } + }); + + // If any user input events were handled in the above 5 seconds + // the mouseDownPromise would be resolved with canHandleInput = false, + // so that the test would fail. + canHandleInput = true; + + // Ensure the events can be handled eventually + await BrowserTestUtils.synthesizeMouseAtPoint( + 10, + 10, + { type: "mousedown" }, + tab.linkedBrowser + ); + + await mouseDownPromise; + BrowserTestUtils.removeTab(tab); +} + +add_task(async function test_MinRAF_BFCache() { + const prefs = [ + ["dom.input_events.security.minNumTicks", 100], + ["dom.input_events.security.minTimeElapsedInMS", 0], + ["dom.input_events.security.isUserInputHandlingDelayTest", true], + ]; + + await test_user_input_handling_delay_BFCache_helper(prefs); +}); + +add_task(async function test_MinElapsedTime_BFCache() { + const prefs = [ + ["dom.input_events.security.minNumTicks", 0], + ["dom.input_events.security.minTimeElapsedInMS", 5000], + ["dom.input_events.security.isUserInputHandlingDelayTest", true], + ]; + + await test_user_input_handling_delay_BFCache_helper(prefs); +}); diff -Nru firefox-esr-115.3.1esr+build1/dom/base/test/browser_user_input_handling_delay_invisible_iframe.js firefox-esr-115.4.0esr+build1/dom/base/test/browser_user_input_handling_delay_invisible_iframe.js --- firefox-esr-115.3.1esr+build1/dom/base/test/browser_user_input_handling_delay_invisible_iframe.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/base/test/browser_user_input_handling_delay_invisible_iframe.js 2023-10-17 05:45:44.000000000 +0000 @@ -0,0 +1,78 @@ +/* -*- Mode: JavaScript; 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/. */ + +async function test_user_input_handling_delay_helper(prefs) { + await SpecialPowers.pushPrefEnv({ + set: prefs, + }); + + const tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + ` + data:text/html, + + + + ` + ); + + await BrowserTestUtils.reloadTab(tab); + + let iframeFocused = SpecialPowers.spawn( + tab.linkedBrowser, + [], + async function () { + let iframe = content.document.querySelector("iframe"); + await ContentTaskUtils.waitForCondition(function () { + return content.document.activeElement == iframe; + }); + } + ); + + // Now the focus moves to the cross origin iframe + await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { + content.document.querySelector("iframe").focus(); + }); + + await iframeFocused; + + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + await new Promise(r => setTimeout(r, 1000)); + + const inputGetFocused = SpecialPowers.spawn( + tab.linkedBrowser, + [], + async function () { + await ContentTaskUtils.waitForEvent( + content.document.querySelector("input"), + "focus" + ); + } + ).then(function () { + Assert.ok( + true, + "Invisible OOP iframe shouldn't prevent user input event handling" + ); + }); + + let iframeBC = tab.linkedBrowser.browsingContext.children[0]; + // Next tab key should move the focus from the iframe to link + await BrowserTestUtils.synthesizeKey("KEY_Tab", {}, iframeBC); + + await inputGetFocused; + + BrowserTestUtils.removeTab(tab); +} + +add_task(async function test_InvisibleIframe() { + const prefs = [ + ["dom.input_events.security.minNumTicks", 3], + ["dom.input_events.security.minTimeElapsedInMS", 0], + ["dom.input_events.security.isUserInputHandlingDelayTest", true], + ]; + + await test_user_input_handling_delay_helper(prefs); +}); diff -Nru firefox-esr-115.3.1esr+build1/dom/base/test/browser_user_input_handling_delay.js firefox-esr-115.4.0esr+build1/dom/base/test/browser_user_input_handling_delay.js --- firefox-esr-115.3.1esr+build1/dom/base/test/browser_user_input_handling_delay.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/base/test/browser_user_input_handling_delay.js 2023-10-17 05:45:44.000000000 +0000 @@ -0,0 +1,82 @@ +/* -*- Mode: JavaScript; 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/. */ + +async function test_user_input_handling_delay_helper(prefs) { + await SpecialPowers.pushPrefEnv({ + set: prefs, + }); + + const tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + `data:text/html,` + ); + + let canHandleInput = false; + let mouseDownPromise = BrowserTestUtils.waitForContentEvent( + tab.linkedBrowser, + "mousedown" + ).then(function () { + Assert.ok( + canHandleInput, + "This promise should be resolved after the 5 seconds mark has passed" + ); + }); + + for (let i = 0; i < 10; ++i) { + await BrowserTestUtils.synthesizeMouseAtPoint( + 10, + 10, + { type: "mousedown" }, + tab.linkedBrowser + ); + } + // Wait for roughly 5 seconds to give chances for the + // above mousedown event to be handled. + await SpecialPowers.spawn(tab.linkedBrowser, [], async () => { + for (let i = 0; i < 330; ++i) { + await new Promise(r => { + content.requestAnimationFrame(r); + }); + } + }); + + // If any user input events were handled in the above 5 seconds + // the mouseDownPromise would be resolved with canHandleInput = false, + // so that the test would fail. + canHandleInput = true; + + // Ensure the events can be handled eventually + await BrowserTestUtils.synthesizeMouseAtPoint( + 10, + 10, + { type: "mousedown" }, + tab.linkedBrowser + ); + + await mouseDownPromise; + + BrowserTestUtils.removeTab(tab); +} + +add_task(async function test_MinRAF() { + const prefs = [ + ["dom.input_events.security.minNumTicks", 100], + ["dom.input_events.security.minTimeElapsedInMS", 0], + ["dom.input_events.security.isUserInputHandlingDelayTest", true], + ]; + + await test_user_input_handling_delay_helper(prefs); +}); + +add_task(async function test_MinElapsedTime() { + const prefs = [ + ["dom.input_events.security.minNumTicks", 0], + ["dom.input_events.security.minTimeElapsedInMS", 5000], + ["dom.input_events.security.isUserInputHandlingDelayTest", true], + ]; + + await test_user_input_handling_delay_helper(prefs); +}); diff -Nru firefox-esr-115.3.1esr+build1/dom/base/test/browser_user_input_handling_delay_reload_ticks.js firefox-esr-115.4.0esr+build1/dom/base/test/browser_user_input_handling_delay_reload_ticks.js --- firefox-esr-115.3.1esr+build1/dom/base/test/browser_user_input_handling_delay_reload_ticks.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/base/test/browser_user_input_handling_delay_reload_ticks.js 2023-10-17 05:45:44.000000000 +0000 @@ -0,0 +1,54 @@ +/* -*- Mode: JavaScript; 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/. */ + +async function test_user_input_handling_delay_helper(prefs) { + await SpecialPowers.pushPrefEnv({ + set: prefs, + }); + + const tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + `data:text/html,` + ); + + await BrowserTestUtils.reloadTab(tab); + + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + await new Promise(r => setTimeout(r, 5000)); + + const userInputHappend = SpecialPowers.spawn( + tab.linkedBrowser, + [], + async function () { + await ContentTaskUtils.waitForEvent(content, "keydown"); + } + ).then(function () { + Assert.ok( + true, + "User input event should be able to work after 5 seconds of an reload" + ); + }); + + // In the buggy build, the following tab key doesn't work + await BrowserTestUtils.synthesizeKey("KEY_Tab", {}, tab.linkedBrowser); + await BrowserTestUtils.synthesizeKey("KEY_Tab", {}, tab.linkedBrowser); + await BrowserTestUtils.synthesizeKey("KEY_Tab", {}, tab.linkedBrowser); + await BrowserTestUtils.synthesizeKey("KEY_Tab", {}, tab.linkedBrowser); + + await userInputHappend; + + BrowserTestUtils.removeTab(tab); +} + +add_task(async function test_MinTick() { + const prefs = [ + ["dom.input_events.security.minNumTicks", 10], + ["dom.input_events.security.minTimeElapsedInMS", 0], + ["dom.input_events.security.isUserInputHandlingDelayTest", true], + ]; + + await test_user_input_handling_delay_helper(prefs); +}); diff -Nru firefox-esr-115.3.1esr+build1/dom/canvas/DrawTargetWebgl.cpp firefox-esr-115.4.0esr+build1/dom/canvas/DrawTargetWebgl.cpp --- firefox-esr-115.3.1esr+build1/dom/canvas/DrawTargetWebgl.cpp 2023-09-29 10:59:39.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/canvas/DrawTargetWebgl.cpp 2023-10-17 05:45:44.000000000 +0000 @@ -1192,8 +1192,10 @@ u"attribute vec3 a_vertex;\n" "uniform vec2 u_transform[3];\n" "uniform vec2 u_viewport;\n" + "uniform vec4 u_clipbounds;\n" "uniform float u_aa;\n" - "varying vec4 v_cliptc;\n" + "varying vec2 v_cliptc;\n" + "varying vec4 v_clipdist;\n" "varying vec4 v_dist;\n" "varying float v_alpha;\n" "void main() {\n" @@ -1207,7 +1209,9 @@ " u_transform[1] * extrude.y +\n" " u_transform[2];\n" " gl_Position = vec4(vertex * 2.0 / u_viewport - 1.0, 0.0, 1.0);\n" - " v_cliptc = vec4(vertex / u_viewport, vertex);\n" + " v_cliptc = vertex / u_viewport;\n" + " v_clipdist = vec4(vertex - u_clipbounds.xy,\n" + " u_clipbounds.zw - vertex);\n" " v_dist = vec4(extrude, 1.0 - extrude) * scale.xyxy + 1.5 - u_aa;\n" " v_alpha = a_vertex.z;\n" "}\n"_ns; @@ -1215,15 +1219,13 @@ u"precision mediump float;\n" "uniform vec4 u_color;\n" "uniform sampler2D u_clipmask;\n" - "uniform vec4 u_clipbounds;\n" - "varying vec4 v_cliptc;\n" + "varying highp vec2 v_cliptc;\n" + "varying vec4 v_clipdist;\n" "varying vec4 v_dist;\n" "varying float v_alpha;\n" "void main() {\n" - " float clip = texture2D(u_clipmask, v_cliptc.xy).r;\n" - " vec4 clipdist = vec4(v_cliptc.zw - u_clipbounds.xy,\n" - " u_clipbounds.zw - v_cliptc.zw);\n" - " vec4 dist = min(v_dist, clipdist);\n" + " float clip = texture2D(u_clipmask, v_cliptc).r;\n" + " vec4 dist = min(v_dist, v_clipdist);\n" " dist.xy = min(dist.xy, dist.zw);\n" " float aa = v_alpha * clamp(min(dist.x, dist.y), 0.0, 1.0);\n" " gl_FragColor = clip * aa * u_color;\n" @@ -1274,11 +1276,13 @@ auto vsSource = u"attribute vec3 a_vertex;\n" "uniform vec2 u_viewport;\n" + "uniform vec4 u_clipbounds;\n" "uniform float u_aa;\n" "uniform vec2 u_transform[3];\n" "uniform vec2 u_texmatrix[3];\n" - "varying vec4 v_cliptc;\n" + "varying vec2 v_cliptc;\n" "varying vec2 v_texcoord;\n" + "varying vec4 v_clipdist;\n" "varying vec4 v_dist;\n" "varying float v_alpha;\n" "void main() {\n" @@ -1292,7 +1296,9 @@ " u_transform[1] * extrude.y +\n" " u_transform[2];\n" " gl_Position = vec4(vertex * 2.0 / u_viewport - 1.0, 0.0, 1.0);\n" - " v_cliptc = vec4(vertex / u_viewport, vertex);\n" + " v_cliptc = vertex / u_viewport;\n" + " v_clipdist = vec4(vertex - u_clipbounds.xy,\n" + " u_clipbounds.zw - vertex);\n" " v_texcoord = u_texmatrix[0] * extrude.x +\n" " u_texmatrix[1] * extrude.y +\n" " u_texmatrix[2];\n" @@ -1306,18 +1312,17 @@ "uniform float u_swizzle;\n" "uniform sampler2D u_sampler;\n" "uniform sampler2D u_clipmask;\n" - "uniform vec4 u_clipbounds;\n" - "varying vec4 v_cliptc;\n" - "varying vec2 v_texcoord;\n" + "varying highp vec2 v_cliptc;\n" + "varying highp vec2 v_texcoord;\n" + "varying vec4 v_clipdist;\n" "varying vec4 v_dist;\n" "varying float v_alpha;\n" "void main() {\n" - " vec2 tc = clamp(v_texcoord, u_texbounds.xy, u_texbounds.zw);\n" + " highp vec2 tc = clamp(v_texcoord, u_texbounds.xy,\n" + " u_texbounds.zw);\n" " vec4 image = texture2D(u_sampler, tc);\n" - " float clip = texture2D(u_clipmask, v_cliptc.xy).r;\n" - " vec4 clipdist = vec4(v_cliptc.zw - u_clipbounds.xy,\n" - " u_clipbounds.zw - v_cliptc.zw);\n" - " vec4 dist = min(v_dist, clipdist);\n" + " float clip = texture2D(u_clipmask, v_cliptc).r;\n" + " vec4 dist = min(v_dist, v_clipdist);\n" " dist.xy = min(dist.xy, dist.zw);\n" " float aa = v_alpha * clamp(min(dist.x, dist.y), 0.0, 1.0);\n" " gl_FragColor = clip * aa * u_color *\n" diff -Nru firefox-esr-115.3.1esr+build1/dom/canvas/WebGLContextDraw.cpp firefox-esr-115.4.0esr+build1/dom/canvas/WebGLContextDraw.cpp --- firefox-esr-115.3.1esr+build1/dom/canvas/WebGLContextDraw.cpp 2023-09-29 10:59:39.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/canvas/WebGLContextDraw.cpp 2023-10-17 05:45:45.000000000 +0000 @@ -674,7 +674,7 @@ //////////////////////////////////////// void WebGLContext::DrawArraysInstanced(const GLenum mode, const GLint first, - const GLsizei vertCount, + const GLsizei iVertCount, const GLsizei instanceCount) { const FuncScope funcScope(*this, "drawArraysInstanced"); // AUTO_PROFILER_LABEL("WebGLContext::DrawArraysInstanced", GRAPHICS); @@ -684,10 +684,11 @@ // - if (!ValidateNonNegative("first", first) || - !ValidateNonNegative("vertCount", vertCount) || + !ValidateNonNegative("vertCount", iVertCount) || !ValidateNonNegative("instanceCount", instanceCount)) { return; } + const auto vertCount = AssertedCast(iVertCount); if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) { MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart)); @@ -720,6 +721,14 @@ return; } + if (vertCount > mMaxVertIdsPerDraw) { + ErrorOutOfMemory( + "Context's max vertCount is %u, but %u requested. " + "[webgl.max-vert-ids-per-draw]", + mMaxVertIdsPerDraw, vertCount); + return; + } + // - bool error = false; @@ -936,9 +945,11 @@ } } -void WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei indexCount, - GLenum type, WebGLintptr byteOffset, - GLsizei instanceCount) { +void WebGLContext::DrawElementsInstanced(const GLenum mode, + const GLsizei iIndexCount, + const GLenum type, + const WebGLintptr byteOffset, + const GLsizei instanceCount) { const FuncScope funcScope(*this, "drawElementsInstanced"); // AUTO_PROFILER_LABEL("WebGLContext::DrawElementsInstanced", GRAPHICS); if (IsContextLost()) return; @@ -946,8 +957,9 @@ const gl::GLContext::TlsScope inTls(gl); const auto indexBuffer = - DrawElements_check(indexCount, type, byteOffset, instanceCount); + DrawElements_check(iIndexCount, type, byteOffset, instanceCount); if (!indexBuffer) return; + const auto indexCount = AssertedCast(iIndexCount); // - @@ -1009,6 +1021,14 @@ } } + if (indexCount > mMaxVertIdsPerDraw) { + ErrorOutOfMemory( + "Context's max indexCount is %u, but %u requested. " + "[webgl.max-vert-ids-per-draw]", + mMaxVertIdsPerDraw, indexCount); + return; + } + // - bool error = false; diff -Nru firefox-esr-115.3.1esr+build1/dom/canvas/WebGLContext.h firefox-esr-115.4.0esr+build1/dom/canvas/WebGLContext.h --- firefox-esr-115.3.1esr+build1/dom/canvas/WebGLContext.h 2023-09-29 10:59:40.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/canvas/WebGLContext.h 2023-10-17 05:45:44.000000000 +0000 @@ -25,6 +25,7 @@ #include "mozilla/gfx/2D.h" #include "mozilla/Mutex.h" #include "mozilla/StaticMutex.h" +#include "mozilla/StaticPrefs_webgl.h" #include "mozilla/UniquePtr.h" #include "mozilla/WeakPtr.h" #include "nsICanvasRenderingContextInternal.h" @@ -300,6 +301,8 @@ WebGLContextOptions mOptions; const uint32_t mPrincipalKey; Maybe mLimits; + const uint32_t mMaxVertIdsPerDraw = + StaticPrefs::webgl_max_vert_ids_per_draw(); bool mIsContextLost = false; Atomic mPendingContextLoss; diff -Nru firefox-esr-115.3.1esr+build1/dom/indexedDB/IndexedDatabaseManager.cpp firefox-esr-115.4.0esr+build1/dom/indexedDB/IndexedDatabaseManager.cpp --- firefox-esr-115.3.1esr+build1/dom/indexedDB/IndexedDatabaseManager.cpp 2023-09-29 10:59:41.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/indexedDB/IndexedDatabaseManager.cpp 2023-10-17 05:45:45.000000000 +0000 @@ -142,10 +142,11 @@ #undef IDB_PREF_LOGGING_BRANCH_ROOT #undef IDB_PREF_BRANCH_ROOT -StaticRefPtr gDBManager; +StaticMutex gDBManagerMutex; +StaticRefPtr gDBManager MOZ_GUARDED_BY(gDBManagerMutex); +bool gInitialized MOZ_GUARDED_BY(gDBManagerMutex) = false; +bool gClosed MOZ_GUARDED_BY(gDBManagerMutex) = false; -Atomic gInitialized(false); -Atomic gClosed(false); Atomic gDataThresholdBytes(0); Atomic gMaxSerializedMsgSize(0); Atomic gMaxPreloadExtraRecords(0); @@ -227,7 +228,9 @@ IndexedDatabaseManager* IndexedDatabaseManager::GetOrCreate() { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - if (IsClosed()) { + StaticMutexAutoLock lock(gDBManagerMutex); + + if (gClosed) { NS_ERROR("Calling GetOrCreate() after shutdown!"); return nullptr; } @@ -235,17 +238,23 @@ if (!gDBManager) { sIsMainProcess = XRE_IsParentProcess(); + if (gInitialized) { + NS_ERROR("Initialized more than once?!"); + } + RefPtr instance(new IndexedDatabaseManager()); - QM_TRY(MOZ_TO_RESULT(instance->Init()), nullptr); + { + StaticMutexAutoUnlock unlock(gDBManagerMutex); - if (gInitialized.exchange(true)) { - NS_ERROR("Initialized more than once?!"); + QM_TRY(MOZ_TO_RESULT(instance->Init()), nullptr); } gDBManager = instance; ClearOnShutdown(&gDBManager); + + gInitialized = true; } return gDBManager; @@ -253,6 +262,8 @@ // static IndexedDatabaseManager* IndexedDatabaseManager::Get() { + StaticMutexAutoLock lock(gDBManagerMutex); + // Does not return an owning reference. return gDBManager; } @@ -309,10 +320,16 @@ } void IndexedDatabaseManager::Destroy() { - // Setting the closed flag prevents the service from being recreated. - // Don't set it though if there's no real instance created. - if (gInitialized && gClosed.exchange(true)) { - NS_ERROR("Shutdown more than once?!"); + { + StaticMutexAutoLock lock(gDBManagerMutex); + + // Setting the closed flag prevents the service from being recreated. + // Don't set it though if there's no real instance created. + if (gInitialized && gClosed) { + NS_ERROR("Shutdown more than once?!"); + } + + gClosed = true; } Preferences::UnregisterCallback(LoggingModePrefChangedCallback, @@ -390,12 +407,16 @@ } // static -bool IndexedDatabaseManager::IsClosed() { return gClosed; } +bool IndexedDatabaseManager::IsClosed() { + StaticMutexAutoLock lock(gDBManagerMutex); + + return gClosed; +} #ifdef DEBUG // static bool IndexedDatabaseManager::IsMainProcess() { - NS_ASSERTION(gDBManager, + NS_ASSERTION(Get(), "IsMainProcess() called before indexedDB has been initialized!"); NS_ASSERTION((XRE_IsParentProcess()) == sIsMainProcess, "XRE_GetProcessType changed its tune!"); @@ -404,7 +425,7 @@ // static IndexedDatabaseManager::LoggingMode IndexedDatabaseManager::GetLoggingMode() { - MOZ_ASSERT(gDBManager, + MOZ_ASSERT(Get(), "GetLoggingMode called before IndexedDatabaseManager has been " "initialized!"); @@ -413,7 +434,7 @@ // static mozilla::LogModule* IndexedDatabaseManager::GetLoggingModule() { - MOZ_ASSERT(gDBManager, + MOZ_ASSERT(Get(), "GetLoggingModule called before IndexedDatabaseManager has been " "initialized!"); @@ -424,7 +445,7 @@ // static bool IndexedDatabaseManager::FullSynchronous() { - MOZ_ASSERT(gDBManager, + MOZ_ASSERT(Get(), "FullSynchronous() called before indexedDB has been initialized!"); return sFullSynchronousMode; @@ -432,7 +453,7 @@ // static uint32_t IndexedDatabaseManager::DataThreshold() { - MOZ_ASSERT(gDBManager, + MOZ_ASSERT(Get(), "DataThreshold() called before indexedDB has been initialized!"); return gDataThresholdBytes; @@ -441,7 +462,7 @@ // static uint32_t IndexedDatabaseManager::MaxSerializedMsgSize() { MOZ_ASSERT( - gDBManager, + Get(), "MaxSerializedMsgSize() called before indexedDB has been initialized!"); MOZ_ASSERT(gMaxSerializedMsgSize > 0); @@ -450,7 +471,7 @@ // static int32_t IndexedDatabaseManager::MaxPreloadExtraRecords() { - MOZ_ASSERT(gDBManager, + MOZ_ASSERT(Get(), "MaxPreloadExtraRecords() called before indexedDB has been " "initialized!"); diff -Nru firefox-esr-115.3.1esr+build1/dom/ipc/ContentParent.cpp firefox-esr-115.4.0esr+build1/dom/ipc/ContentParent.cpp --- firefox-esr-115.3.1esr+build1/dom/ipc/ContentParent.cpp 2023-09-29 10:59:41.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/ipc/ContentParent.cpp 2023-10-17 05:45:45.000000000 +0000 @@ -1699,7 +1699,9 @@ RefPtr gmps( GeckoMediaPluginServiceParent::GetSingleton()); - gmps->UpdateContentProcessGMPCapabilities(this); + if (gmps) { + gmps->UpdateContentProcessGMPCapabilities(this); + } // Flush any pref updates that happened during launch and weren't // included in the blobs set up in BeginSubprocessLaunch. diff -Nru firefox-esr-115.3.1esr+build1/dom/media/gmp/GMPContentParent.cpp firefox-esr-115.4.0esr+build1/dom/media/gmp/GMPContentParent.cpp --- firefox-esr-115.3.1esr+build1/dom/media/gmp/GMPContentParent.cpp 2023-09-29 10:59:41.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/media/gmp/GMPContentParent.cpp 2023-10-17 05:45:45.000000000 +0000 @@ -118,7 +118,9 @@ toClose = this; RefPtr gmp( GeckoMediaPluginServiceChild::GetSingleton()); - gmp->RemoveGMPContentParent(toClose); + if (gmp) { + gmp->RemoveGMPContentParent(toClose); + } } NS_DispatchToCurrentThread(NewRunnableMethod( "gmp::GMPContentParent::Close", toClose, &GMPContentParent::Close)); diff -Nru firefox-esr-115.3.1esr+build1/dom/media/gmp/GMPCrashHelperHolder.cpp firefox-esr-115.4.0esr+build1/dom/media/gmp/GMPCrashHelperHolder.cpp --- firefox-esr-115.3.1esr+build1/dom/media/gmp/GMPCrashHelperHolder.cpp 2023-09-29 10:59:41.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/media/gmp/GMPCrashHelperHolder.cpp 2023-10-17 05:45:45.000000000 +0000 @@ -22,7 +22,9 @@ if (!aAbnormalShutdown) { RefPtr service( gmp::GeckoMediaPluginService::GetGeckoMediaPluginService()); - service->DisconnectCrashHelper(GetCrashHelper()); + if (service) { + service->DisconnectCrashHelper(GetCrashHelper()); + } } } diff -Nru firefox-esr-115.3.1esr+build1/dom/media/gmp/GMPServiceChild.cpp firefox-esr-115.4.0esr+build1/dom/media/gmp/GMPServiceChild.cpp --- firefox-esr-115.3.1esr+build1/dom/media/gmp/GMPServiceChild.cpp 2023-09-29 10:59:41.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/media/gmp/GMPServiceChild.cpp 2023-10-17 05:45:45.000000000 +0000 @@ -427,15 +427,20 @@ MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!mShuttingDownOnGMPThread, "No call paths should add blockers once we're shutting down!"); -#ifdef DEBUG MOZ_ASSERT(!mShutdownBlockerAdded, "Should only add blocker once!"); - mShutdownBlockerAdded = true; -#endif GMP_LOG_DEBUG("%s::%s ", __CLASS__, __FUNCTION__); - nsresult rv = GetShutdownBarrier()->AddBlocker( - this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__, - kShutdownBlockerName); + nsCOMPtr barrier = GetShutdownBarrier(); + if (NS_WARN_IF(!barrier)) { + return NS_ERROR_NOT_AVAILABLE; + } + + nsresult rv = + barrier->AddBlocker(this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), + __LINE__, kShutdownBlockerName); +#ifdef DEBUG + mShutdownBlockerAdded = NS_SUCCEEDED(rv); +#endif return rv; } @@ -449,7 +454,12 @@ "GeckoMediaPluginServiceChild::" "RemoveShutdownBlocker", [this, self = RefPtr(this)]() { - nsresult rv = GetShutdownBarrier()->RemoveBlocker(this); + nsCOMPtr barrier = GetShutdownBarrier(); + if (NS_WARN_IF(!barrier)) { + return; + } + + nsresult rv = barrier->RemoveBlocker(this); MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv)); })); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -520,6 +530,7 @@ NS_IMETHOD Run() override { RefPtr gmp = GeckoMediaPluginServiceChild::GetSingleton(); + MOZ_RELEASE_ASSERT(gmp); MOZ_ASSERT(!gmp->mServiceChild); if (mEndpoint.Bind(mGMPServiceChild.get())) { gmp->SetServiceChild(std::move(mGMPServiceChild)); @@ -538,6 +549,10 @@ bool GMPServiceChild::Create(Endpoint&& aGMPService) { RefPtr gmp = GeckoMediaPluginServiceChild::GetSingleton(); + if (NS_WARN_IF(!gmp)) { + return false; + } + MOZ_ASSERT(!gmp->mServiceChild); RefPtr serviceChild(new GMPServiceChild()); diff -Nru firefox-esr-115.3.1esr+build1/dom/media/gmp/GMPService.cpp firefox-esr-115.4.0esr+build1/dom/media/gmp/GMPService.cpp --- firefox-esr-115.3.1esr+build1/dom/media/gmp/GMPService.cpp 2023-09-29 10:59:41.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/media/gmp/GMPService.cpp 2023-10-17 05:45:45.000000000 +0000 @@ -110,7 +110,9 @@ if (XRE_IsParentProcess()) { RefPtr service = new GeckoMediaPluginServiceParent(); - service->Init(); + if (NS_WARN_IF(NS_FAILED(service->Init()))) { + return nullptr; + } sSingletonService = service; #if defined(XP_MACOSX) && defined(MOZ_SANDBOX) // GMPProcessParent should only be instantiated in the parent @@ -120,7 +122,9 @@ } else { RefPtr service = new GeckoMediaPluginServiceChild(); - service->Init(); + if (NS_WARN_IF(NS_FAILED(service->Init()))) { + return nullptr; + } sSingletonService = service; } ClearOnShutdown(&sSingletonService); @@ -365,12 +369,18 @@ /* static */ nsCOMPtr GeckoMediaPluginService::GetShutdownBarrier() { nsCOMPtr svc = services::GetAsyncShutdownService(); - MOZ_RELEASE_ASSERT(svc); + if (NS_WARN_IF(!svc)) { + MOZ_ASSERT_UNREACHABLE("No async shutdown service!"); + return nullptr; + } nsCOMPtr barrier; nsresult rv = svc->GetXpcomWillShutdown(getter_AddRefs(barrier)); + if (NS_WARN_IF(NS_FAILED(rv))) { + MOZ_ASSERT_UNREACHABLE("Could not create shutdown barrier!"); + return nullptr; + } - MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv)); MOZ_RELEASE_ASSERT(barrier); return barrier; } diff -Nru firefox-esr-115.3.1esr+build1/dom/media/gmp/GMPServiceParent.cpp firefox-esr-115.4.0esr+build1/dom/media/gmp/GMPServiceParent.cpp --- firefox-esr-115.3.1esr+build1/dom/media/gmp/GMPServiceParent.cpp 2023-09-29 10:59:41.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/media/gmp/GMPServiceParent.cpp 2023-10-17 05:45:45.000000000 +0000 @@ -1722,7 +1722,10 @@ MOZ_ASSERT(!mServiceParents.Contains(aServiceParent)); mServiceParents.AppendElement(aServiceParent); if (mServiceParents.Length() == 1) { - nsresult rv = GetShutdownBarrier()->AddBlocker( + nsCOMPtr barrier = GetShutdownBarrier(); + MOZ_RELEASE_ASSERT(barrier); + + nsresult rv = barrier->AddBlocker( this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__, u"GeckoMediaPluginServiceParent shutdown"_ns); MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv)); @@ -1737,7 +1740,9 @@ MOZ_ASSERT(mServiceParents.Contains(aServiceParent)); mServiceParents.RemoveElement(aServiceParent); if (mServiceParents.IsEmpty()) { - nsresult rv = GetShutdownBarrier()->RemoveBlocker(this); + nsCOMPtr barrier = GetShutdownBarrier(); + MOZ_RELEASE_ASSERT(barrier); + nsresult rv = barrier->RemoveBlocker(this); MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv)); } } @@ -1904,7 +1909,7 @@ RefPtr gmp = GeckoMediaPluginServiceParent::GetSingleton(); - if (gmp->mShuttingDown) { + if (!gmp || gmp->mShuttingDown) { // Shutdown is initiated. There is no point creating a new actor. return false; } diff -Nru firefox-esr-115.3.1esr+build1/dom/media/mediasource/MediaSourceDemuxer.cpp firefox-esr-115.4.0esr+build1/dom/media/mediasource/MediaSourceDemuxer.cpp --- firefox-esr-115.3.1esr+build1/dom/media/mediasource/MediaSourceDemuxer.cpp 2023-09-29 10:59:41.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/media/mediasource/MediaSourceDemuxer.cpp 2023-10-17 05:45:45.000000000 +0000 @@ -171,16 +171,18 @@ void MediaSourceDemuxer::DetachSourceBuffer( RefPtr& aSourceBuffer) { - nsCOMPtr task = NewRunnableMethod&&>( - "MediaSourceDemuxer::DoDetachSourceBuffer", this, - &MediaSourceDemuxer::DoDetachSourceBuffer, aSourceBuffer); + nsCOMPtr task = + NS_NewRunnableFunction("MediaSourceDemuxer::DoDetachSourceBuffer", + [self = RefPtr{this}, aSourceBuffer]() { + self->DoDetachSourceBuffer(aSourceBuffer); + }); nsresult rv = GetTaskQueue()->Dispatch(task.forget()); MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); Unused << rv; } void MediaSourceDemuxer::DoDetachSourceBuffer( - RefPtr&& aSourceBuffer) { + const RefPtr& aSourceBuffer) { MOZ_ASSERT(OnTaskQueue()); mSourceBuffers.RemoveElementsBy( [&aSourceBuffer](const RefPtr aLinkedSourceBuffer) { diff -Nru firefox-esr-115.3.1esr+build1/dom/media/mediasource/MediaSourceDemuxer.h firefox-esr-115.4.0esr+build1/dom/media/mediasource/MediaSourceDemuxer.h --- firefox-esr-115.3.1esr+build1/dom/media/mediasource/MediaSourceDemuxer.h 2023-09-29 10:59:41.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/media/mediasource/MediaSourceDemuxer.h 2023-10-17 05:45:45.000000000 +0000 @@ -82,7 +82,7 @@ RefPtr GetManager(TrackInfo::TrackType aType); TrackInfo* GetTrackInfo(TrackInfo::TrackType); void DoAttachSourceBuffer(RefPtr&& aSourceBuffer); - void DoDetachSourceBuffer(RefPtr&& aSourceBuffer); + void DoDetachSourceBuffer(const RefPtr& aSourceBuffer); bool OnTaskQueue() { return !GetTaskQueue() || GetTaskQueue()->IsCurrentThreadIn(); } diff -Nru firefox-esr-115.3.1esr+build1/dom/media/webrtc/libwebrtcglue/WebrtcGmpVideoCodec.cpp firefox-esr-115.4.0esr+build1/dom/media/webrtc/libwebrtcglue/WebrtcGmpVideoCodec.cpp --- firefox-esr-115.3.1esr+build1/dom/media/webrtc/libwebrtcglue/WebrtcGmpVideoCodec.cpp 2023-09-29 10:59:42.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/media/webrtc/libwebrtcglue/WebrtcGmpVideoCodec.cpp 2023-10-17 05:45:45.000000000 +0000 @@ -507,10 +507,16 @@ void WebrtcGmpVideoEncoder::Terminated() { GMP_LOG_DEBUG("GMP Encoder Terminated: %p", (void*)this); - mGMP->Close(); + GMPVideoEncoderProxy* gmp(mGMP); mGMP = nullptr; mHost = nullptr; mInitting = false; + + if (gmp) { + // Do this last, since this could cause us to be destroyed + gmp->Close(); + } + // Could now notify that it's dead } @@ -947,10 +953,16 @@ void WebrtcGmpVideoDecoder::Terminated() { GMP_LOG_DEBUG("GMP Decoder Terminated: %p", (void*)this); - mGMP->Close(); + GMPVideoDecoderProxy* gmp(mGMP); mGMP = nullptr; mHost = nullptr; mInitting = false; + + if (gmp) { + // Do this last, since this could cause us to be destroyed + gmp->Close(); + } + // Could now notify that it's dead } diff -Nru firefox-esr-115.3.1esr+build1/dom/midi/crashtests/1851829.html firefox-esr-115.4.0esr+build1/dom/midi/crashtests/1851829.html --- firefox-esr-115.3.1esr+build1/dom/midi/crashtests/1851829.html 1970-01-01 00:00:00.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/midi/crashtests/1851829.html 2023-10-17 05:45:45.000000000 +0000 @@ -0,0 +1,31 @@ + + + + + + Bug 1851829 + + + diff -Nru firefox-esr-115.3.1esr+build1/dom/midi/crashtests/crashtests.list firefox-esr-115.4.0esr+build1/dom/midi/crashtests/crashtests.list --- firefox-esr-115.3.1esr+build1/dom/midi/crashtests/crashtests.list 1970-01-01 00:00:00.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/midi/crashtests/crashtests.list 2023-10-17 05:45:45.000000000 +0000 @@ -0,0 +1 @@ +skip-if(Android) test-pref(app.normandy.enabled,false) test-pref(app.update.auto,false) test-pref(app.update.staging.enabled,false) test-pref(app.update.url.android,'') test-pref(apz.wr.activate_all_scroll_frames,true) test-pref(browser.EULA.override,true) test-pref(browser.cache.disk.enable,false) test-pref(browser.cache.disk_cache_ssl,false) test-pref(browser.cache.memory.enable,false) test-pref(browser.cache.offline.enable,false) test-pref(browser.chrome.site_icons,false) test-pref(browser.chrome.toolbar_tips,false) test-pref(browser.dom.window.dump.enabled,true) test-pref(browser.newtabpage.enabled,false) test-pref(browser.pagethumbnails.capturing_disabled,true) test-pref(browser.reader.detectedFirstArticle,true) test-pref(browser.safebrowsing.blockedURIs.enabled,false) test-pref(browser.safebrowsing.downloads.enabled,false) test-pref(browser.safebrowsing.downloads.remote.enabled,false) test-pref(browser.safebrowsing.enabled,false) test-pref(browser.safebrowsing.malware.enabled,false) test-pref(browser.safebrowsing.phishing.enabled,false) test-pref(browser.search.geoip.url,'') test-pref(browser.search.region,'US') test-pref(browser.search.suggest.enabled,false) test-pref(browser.search.suggest.prompted,true) test-pref(browser.search.update,false) test-pref(browser.sessionstore.resume_from_crash,false) test-pref(browser.shell.checkDefaultBrowser,false) test-pref(browser.ssl_override_behavior,1) test-pref(browser.startup.homepage,'about:blank') test-pref(browser.startup.homepage_override.mstone,'ignore') test-pref(browser.startup.page,0) test-pref(browser.tabs.warnOnClose,false) test-pref(browser.tabs.warnOnCloseOtherTabs,false) test-pref(browser.warnOnQuit,false) test-pref(canvas.hitregions.enabled,true) test-pref(captivedetect.canonicalURL,'') test-pref(clipboard.autocopy,true) test-pref(csp.skip_about_page_has_csp_assert,true) test-pref(datareporting.healthreport.uploadEnabled,false) test-pref(datareporting.policy.dataSubmissionEnabled,false) test-pref(datareporting.policy.dataSubmissionPolicyAcceptedVersion,2) test-pref(datareporting.policy.dataSubmissionPolicyBypassNotification,true) test-pref(datareporting.policy.firstRunURL,'') test-pref(device.sensors.ambientLight.enabled,true) test-pref(device.sensors.proximity.enabled,true) test-pref(devtools.selfxss.count,999) test-pref(dom.allow_scripts_to_close_windows,true) test-pref(dom.always_stop_slow_scripts,true) test-pref(dom.caches.testing.enabled,true) test-pref(dom.css_pseudo_element.enabled,true) test-pref(dom.dialog_element.enabled,true) test-pref(dom.disable_open_during_load,false) test-pref(dom.disable_window_flip,false) test-pref(dom.element.popover.enabled,true) test-pref(dom.fetchObserver.enabled,true) test-pref(dom.forms.datetime.others,true) test-pref(dom.gamepad.extensions.lightindicator,false) test-pref(dom.gamepad.extensions.multitouch,false) test-pref(dom.gamepad.test.enabled,false) test-pref(dom.image-lazy-loading.enabled,false) test-pref(dom.indexedDB.experimental,true) test-pref(dom.input.dirpicker,true) test-pref(dom.input_events.beforeinput.enabled,true) test-pref(dom.max_chrome_script_run_time,0) test-pref(dom.max_script_run_time,0) test-pref(dom.payments.request.enabled,true) test-pref(dom.presentation.controller.enabled,true) test-pref(dom.presentation.enabled,true) test-pref(dom.presentation.receiver.enabled,true) test-pref(dom.push.testing.ignorePermission,true) test-pref(dom.security.featurePolicy.webidl.enabled,true) test-pref(dom.security.sanitizer.enabled,true) test-pref(dom.security.setHTML.enabled,true) test-pref(dom.send_after_paint_to_content,true) test-pref(dom.serviceWorkers.testing.enabled,true) test-pref(dom.successive_dialog_time_limit,0) test-pref(dom.textMetrics.baselines.enabled,true) test-pref(dom.textMetrics.emHeight.enabled,true) test-pref(dom.textMetrics.fontBoundingBox.enabled,true) test-pref(dom.visualviewport.enabled,true) test-pref(dom.vr.external.notdetected.timeout,0) test-pref(dom.vr.external.quit.timeout,0) test-pref(dom.vr.poseprediction.enabled,false) test-pref(dom.vr.puppet.enabled,false) test-pref(dom.vr.require-gesture,false) test-pref(dom.vr.webxr.enabled,false) test-pref(dom.webgpu.enabled,true) test-pref(dom.weblocks.enabled,true) test-pref(dom.webmidi.enabled,true) test-pref(dom.window_print.fuzzing.block_while_printing,true) test-pref(extensions.autoDisableScopes,0) test-pref(extensions.blocklist.enabled,false) test-pref(extensions.enabledScopes,5) test-pref(extensions.getAddons.cache.enabled,false) test-pref(extensions.installDistroAddons,false) test-pref(extensions.showMismatchUI,false) test-pref(extensions.update.enabled,false) test-pref(extensions.update.notifyUser,false) test-pref(full-screen-api.allow-trusted-requests-only,false) test-pref(full-screen-api.warning.timeout,500) test-pref(fuzzing.enabled,true) test-pref(general.useragent.updates.enabled,false) test-pref(general.warnOnAboutConfig,false) test-pref(geo.enabled,false) test-pref(gfx.color_management.mode,1) test-pref(gfx.downloadable_fonts.disable_cache,true) test-pref(gfx.downloadable_fonts.otl_validation,false) test-pref(gfx.downloadable_fonts.sanitize_omt,false) test-pref(gfx.downloadable_fonts.validate_variation_tables,false) test-pref(gfx.offscreencanvas.enabled,true) test-pref(gfx.webgpu.force-enabled,true) test-pref(gfx.webrender.all,true) test-pref(gfx.webrender.debug.restrict-blob-size,true) test-pref(image.animated.decode-on-demand.batch-size,1) test-pref(image.animated.decode-on-demand.threshold-kb,0) test-pref(image.avif.sequence.enabled,true) test-pref(layout.accessiblecaret.enabled,true) test-pref(layout.css.backdrop-filter.enabled,true) test-pref(layout.css.constructable-stylesheets.enabled,true) test-pref(layout.css.container-queries.enabled,true) test-pref(layout.css.content-visibility.enabled,true) test-pref(layout.css.initial-letter.enabled,true) test-pref(layout.css.moz-control-character-visibility.enabled,true) test-pref(layout.css.moz-document.content.enabled,true) test-pref(layout.css.overflow-clip-box.enabled,true) test-pref(layout.css.scroll-linked-animations.enabled,true) test-pref(layout.css.zoom-transform-hack.enabled,true) test-pref(media.autoplay.default,0) test-pref(media.autoplay.enabled.user-gestures-needed,false) test-pref(media.eme.enabled,true) test-pref(media.eme.hdcp-policy-check.enabled,true) test-pref(media.gmp-manager.url.override,'http://127.0.0.1:6/dummy-gmp-manager.xml') test-pref(media.mediasource.webm.enabled,true) test-pref(media.navigator.permission.disabled,true) test-pref(media.navigator.video.red_ulpfec_enabled,true) test-pref(media.setsinkid.enabled,true) test-pref(midi.prompt.testing,true) test-pref(midi.testing,true) test-pref(network.captive-portal-service.enabled,false) test-pref(network.connectivity-service.enabled,false) test-pref(network.http.response.timeout,1) test-pref(network.http.spdy.enabled,false) test-pref(network.manage-offline-status,false) test-pref(network.prefetch-next,false) test-pref(network.protocol-handler.external.mailto,false) test-pref(network.proxy.allow_bypass,false) test-pref(network.proxy.autoconfig_url,"data:text/base64,ZnVuY3Rpb24gRmluZFByb3h5Rm9yVVJMKHVybCwgaG9zdCkgeyBpZiAoaG9zdCA9PSAnbG9jYWxob3N0JyB8fCBob3N0ID09ICcxMjcuMC4wLjEnKSB7IHJldHVybiAnRElSRUNUJzsgfSBlbHNlIHsgcmV0dXJuICdQUk9YWSAxMjcuMC4wLjE6Nic7IH0gfQ==") test-pref(network.proxy.failover_direct,false) test-pref(network.proxy.share_proxy_settings,true) test-pref(network.proxy.type,2) test-pref(network.websocket.allowInsecureFromHTTPS,true) test-pref(network.websocket.delay-failed-reconnects,false) test-pref(nglayout.debug.disable_xul_cache,false) test-pref(notification.prompt.testing,true) test-pref(notification.prompt.testing.allow,true) test-pref(pdfjs.firstRun,false) test-pref(pdfjs.previousHandler.alwaysAskBeforeHandling,true) test-pref(pdfjs.previousHandler.preferredAction,4) test-pref(permissions.default.camera,1) test-pref(permissions.default.geo,1) test-pref(permissions.default.microphone,1) test-pref(plugin.disable,true) test-pref(print.always_print_silent,true) test-pref(print.print_to_file,true) test-pref(print.show_print_progress,true) test-pref(security.OCSP.enabled,0) test-pref(security.data_uri.unique_opaque_origin,false) test-pref(security.default_personal_cert,'Select\u0020Automatically') test-pref(security.fileuri.strict_origin_policy,false) test-pref(security.webauth.webauthn_enable_softtoken,true) test-pref(security.webauth.webauthn_enable_usbtoken,false) test-pref(svg.context-properties.content.enabled,true) test-pref(toolkit.cosmeticAnimations.enabled,false) test-pref(toolkit.startup.max_resumed_crashes,-1) test-pref(toolkit.telemetry.enabled,false) test-pref(toolkit.telemetry.server,'') test-pref(webgl.enable-privileged-extensions,true) test-pref(webgl.max-warnings-per-context,0) test-pref(webgl.prefer-native-gl,false) load 1851829.html # WebMIDI is not supported on Android diff -Nru firefox-esr-115.3.1esr+build1/dom/midi/MIDIInput.cpp firefox-esr-115.4.0esr+build1/dom/midi/MIDIInput.cpp --- firefox-esr-115.3.1esr+build1/dom/midi/MIDIInput.cpp 2023-09-29 10:59:42.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/midi/MIDIInput.cpp 2023-10-17 05:45:45.000000000 +0000 @@ -16,18 +16,18 @@ namespace mozilla::dom { -MIDIInput::MIDIInput(nsPIDOMWindowInner* aWindow, MIDIAccess* aMIDIAccessParent) - : MIDIPort(aWindow, aMIDIAccessParent), mKeepAlive(false) {} +MIDIInput::MIDIInput(nsPIDOMWindowInner* aWindow) + : MIDIPort(aWindow), mKeepAlive(false) {} // static -MIDIInput* MIDIInput::Create(nsPIDOMWindowInner* aWindow, - MIDIAccess* aMIDIAccessParent, - const MIDIPortInfo& aPortInfo, - const bool aSysexEnabled) { +RefPtr MIDIInput::Create(nsPIDOMWindowInner* aWindow, + MIDIAccess* aMIDIAccessParent, + const MIDIPortInfo& aPortInfo, + const bool aSysexEnabled) { MOZ_ASSERT(static_cast(aPortInfo.type()) == MIDIPortType::Input); - auto* port = new MIDIInput(aWindow, aMIDIAccessParent); - if (!port->Initialize(aPortInfo, aSysexEnabled)) { + RefPtr port = new MIDIInput(aWindow); + if (!port->Initialize(aPortInfo, aSysexEnabled, aMIDIAccessParent)) { return nullptr; } return port; diff -Nru firefox-esr-115.3.1esr+build1/dom/midi/MIDIInput.h firefox-esr-115.4.0esr+build1/dom/midi/MIDIInput.h --- firefox-esr-115.3.1esr+build1/dom/midi/MIDIInput.h 2023-09-29 10:59:42.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/midi/MIDIInput.h 2023-10-17 05:45:45.000000000 +0000 @@ -21,10 +21,10 @@ */ class MIDIInput final : public MIDIPort { public: - static MIDIInput* Create(nsPIDOMWindowInner* aWindow, - MIDIAccess* aMIDIAccessParent, - const MIDIPortInfo& aPortInfo, - const bool aSysexEnabled); + static RefPtr Create(nsPIDOMWindowInner* aWindow, + MIDIAccess* aMIDIAccessParent, + const MIDIPortInfo& aPortInfo, + const bool aSysexEnabled); ~MIDIInput() = default; JSObject* WrapObject(JSContext* aCx, @@ -37,7 +37,7 @@ void DisconnectFromOwner() override; private: - MIDIInput(nsPIDOMWindowInner* aWindow, MIDIAccess* aMIDIAccessParent); + explicit MIDIInput(nsPIDOMWindowInner* aWindow); // Takes an array of IPC MIDIMessage objects and turns them into // MIDIMessageEvents, which it then fires. void Receive(const nsTArray& aMsgs) override; diff -Nru firefox-esr-115.3.1esr+build1/dom/midi/MIDIOutput.cpp firefox-esr-115.4.0esr+build1/dom/midi/MIDIOutput.cpp --- firefox-esr-115.3.1esr+build1/dom/midi/MIDIOutput.cpp 2023-09-29 10:59:42.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/midi/MIDIOutput.cpp 2023-10-17 05:45:45.000000000 +0000 @@ -18,19 +18,18 @@ using namespace mozilla; using namespace mozilla::dom; -MIDIOutput::MIDIOutput(nsPIDOMWindowInner* aWindow, - MIDIAccess* aMIDIAccessParent) - : MIDIPort(aWindow, aMIDIAccessParent) {} +MIDIOutput::MIDIOutput(nsPIDOMWindowInner* aWindow) : MIDIPort(aWindow) {} // static -MIDIOutput* MIDIOutput::Create(nsPIDOMWindowInner* aWindow, - MIDIAccess* aMIDIAccessParent, - const MIDIPortInfo& aPortInfo, - const bool aSysexEnabled) { +RefPtr MIDIOutput::Create(nsPIDOMWindowInner* aWindow, + MIDIAccess* aMIDIAccessParent, + const MIDIPortInfo& aPortInfo, + const bool aSysexEnabled) { MOZ_ASSERT(static_cast(aPortInfo.type()) == MIDIPortType::Output); - auto* port = new MIDIOutput(aWindow, aMIDIAccessParent); - if (NS_WARN_IF(!port->Initialize(aPortInfo, aSysexEnabled))) { + RefPtr port = new MIDIOutput(aWindow); + if (NS_WARN_IF( + !port->Initialize(aPortInfo, aSysexEnabled, aMIDIAccessParent))) { return nullptr; } return port; diff -Nru firefox-esr-115.3.1esr+build1/dom/midi/MIDIOutput.h firefox-esr-115.4.0esr+build1/dom/midi/MIDIOutput.h --- firefox-esr-115.3.1esr+build1/dom/midi/MIDIOutput.h 2023-09-29 10:59:42.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/midi/MIDIOutput.h 2023-10-17 05:45:45.000000000 +0000 @@ -27,10 +27,10 @@ */ class MIDIOutput final : public MIDIPort { public: - static MIDIOutput* Create(nsPIDOMWindowInner* aWindow, - MIDIAccess* aMIDIAccessParent, - const MIDIPortInfo& aPortInfo, - const bool aSysexEnabled); + static RefPtr Create(nsPIDOMWindowInner* aWindow, + MIDIAccess* aMIDIAccessParent, + const MIDIPortInfo& aPortInfo, + const bool aSysexEnabled); ~MIDIOutput() = default; JSObject* WrapObject(JSContext* aCx, @@ -43,7 +43,7 @@ void Clear(); private: - MIDIOutput(nsPIDOMWindowInner* aWindow, MIDIAccess* aMIDIAccessParent); + explicit MIDIOutput(nsPIDOMWindowInner* aWindow); }; } // namespace dom diff -Nru firefox-esr-115.3.1esr+build1/dom/midi/MIDIPort.cpp firefox-esr-115.4.0esr+build1/dom/midi/MIDIPort.cpp --- firefox-esr-115.3.1esr+build1/dom/midi/MIDIPort.cpp 2023-09-29 10:59:42.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/midi/MIDIPort.cpp 2023-10-17 05:45:45.000000000 +0000 @@ -36,12 +36,11 @@ NS_IMPL_ADDREF_INHERITED(MIDIPort, DOMEventTargetHelper) NS_IMPL_RELEASE_INHERITED(MIDIPort, DOMEventTargetHelper) -MIDIPort::MIDIPort(nsPIDOMWindowInner* aWindow, MIDIAccess* aMIDIAccessParent) +MIDIPort::MIDIPort(nsPIDOMWindowInner* aWindow) : DOMEventTargetHelper(aWindow), - mMIDIAccessParent(aMIDIAccessParent), + mMIDIAccessParent(nullptr), mKeepAlive(false) { MOZ_ASSERT(aWindow); - MOZ_ASSERT(aMIDIAccessParent); Document* aDoc = GetOwner()->GetExtantDoc(); if (aDoc) { @@ -62,8 +61,19 @@ } } -bool MIDIPort::Initialize(const MIDIPortInfo& aPortInfo, bool aSysexEnabled) { - nsIURI* uri = GetDocumentIfCurrent()->GetDocumentURI(); +bool MIDIPort::Initialize(const MIDIPortInfo& aPortInfo, bool aSysexEnabled, + MIDIAccess* aMIDIAccessParent) { + MOZ_ASSERT(aMIDIAccessParent); + nsCOMPtr document = GetDocumentIfCurrent(); + if (!document) { + return false; + } + + nsCOMPtr uri = document->GetDocumentURI(); + if (!uri) { + return false; + } + nsAutoCString origin; nsresult rv = nsContentUtils::GetASCIIOrigin(uri, origin); if (NS_FAILED(rv)) { @@ -90,6 +100,8 @@ aSysexEnabled)) { return false; } + + mMIDIAccessParent = aMIDIAccessParent; mPortHolder.Init(port.forget()); LOG("MIDIPort::Initialize (%s, %s)", NS_ConvertUTF16toUTF8(Port()->Name()).get(), @@ -245,8 +257,10 @@ } void MIDIPort::DisconnectFromOwner() { + if (Port()) { + Port()->SendClose(); + } DontKeepAliveOnStatechange(); - Port()->SendClose(); DOMEventTargetHelper::DisconnectFromOwner(); } diff -Nru firefox-esr-115.3.1esr+build1/dom/midi/MIDIPort.h firefox-esr-115.4.0esr+build1/dom/midi/MIDIPort.h --- firefox-esr-115.3.1esr+build1/dom/midi/MIDIPort.h 2023-09-29 10:59:42.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/midi/MIDIPort.h 2023-10-17 05:45:45.000000000 +0000 @@ -38,8 +38,9 @@ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(MIDIPort, DOMEventTargetHelper) protected: - MIDIPort(nsPIDOMWindowInner* aWindow, MIDIAccess* aMIDIAccessParent); - bool Initialize(const MIDIPortInfo& aPortInfo, bool aSysexEnabled); + explicit MIDIPort(nsPIDOMWindowInner* aWindow); + bool Initialize(const MIDIPortInfo& aPortInfo, bool aSysexEnabled, + MIDIAccess* aMIDIAccessParent); virtual ~MIDIPort(); public: diff -Nru firefox-esr-115.3.1esr+build1/dom/workers/WorkerPrivate.cpp firefox-esr-115.4.0esr+build1/dom/workers/WorkerPrivate.cpp --- firefox-esr-115.3.1esr+build1/dom/workers/WorkerPrivate.cpp 2023-09-29 10:59:43.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/workers/WorkerPrivate.cpp 2023-10-17 05:45:46.000000000 +0000 @@ -200,6 +200,7 @@ virtual bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { nsresult rv = mWrappedRunnable->Run(); + mWrappedRunnable = nullptr; if (NS_FAILED(rv)) { if (!JS_IsExceptionPending(aCx)) { Throw(aCx, rv); @@ -218,6 +219,7 @@ do_QueryInterface(mWrappedRunnable); MOZ_ASSERT(doomed); // We checked this earlier! doomed->OnDiscard(); + mWrappedRunnable = nullptr; return NS_OK; } }; diff -Nru firefox-esr-115.3.1esr+build1/dom/worklet/WorkletFetchHandler.cpp firefox-esr-115.4.0esr+build1/dom/worklet/WorkletFetchHandler.cpp --- firefox-esr-115.3.1esr+build1/dom/worklet/WorkletFetchHandler.cpp 2023-09-29 11:00:31.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/worklet/WorkletFetchHandler.cpp 2023-10-17 05:45:46.000000000 +0000 @@ -6,6 +6,7 @@ #include "WorkletFetchHandler.h" #include "js/loader/ModuleLoadRequest.h" +#include "js/ContextOptions.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/Fetch.h" #include "mozilla/dom/Request.h" @@ -24,6 +25,7 @@ #include "mozilla/TaskQueue.h" #include "nsIInputStreamPump.h" #include "nsIThreadRetargetableRequest.h" +#include "xpcpublic.h" using JS::loader::ModuleLoadRequest; using JS::loader::ScriptFetchOptions; @@ -49,6 +51,7 @@ JS_GetParentRuntime(CycleCollectedJSContext::Get()->Context())) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mParentRuntime); + xpc::SetPrefableContextOptions(mContextOptions); } ~StartModuleLoadRunnable() = default; @@ -64,6 +67,7 @@ nsCOMPtr mReferrer; const nsTArray& mLocalizedStrs; JSRuntime* mParentRuntime; + JS::ContextOptions mContextOptions; }; NS_IMETHODIMP @@ -78,7 +82,7 @@ NS_IMETHODIMP StartModuleLoadRunnable::RunOnWorkletThread() { // This can be called on a GraphRunner thread or a DOM Worklet thread. - WorkletThread::EnsureCycleCollectedJSContext(mParentRuntime); + WorkletThread::EnsureCycleCollectedJSContext(mParentRuntime, mContextOptions); WorkletGlobalScope* globalScope = mWorkletImpl->GetGlobalScope(); if (!globalScope) { diff -Nru firefox-esr-115.3.1esr+build1/dom/worklet/WorkletThread.cpp firefox-esr-115.4.0esr+build1/dom/worklet/WorkletThread.cpp --- firefox-esr-115.3.1esr+build1/dom/worklet/WorkletThread.cpp 2023-09-29 11:00:31.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/worklet/WorkletThread.cpp 2023-10-17 05:45:45.000000000 +0000 @@ -17,6 +17,7 @@ #include "mozilla/CycleCollectedJSRuntime.h" #include "mozilla/EventQueue.h" #include "mozilla/ThreadEventQueue.h" +#include "js/ContextOptions.h" #include "js/Exception.h" #include "js/Initialization.h" #include "XPCSelfHostedShmem.h" @@ -346,7 +347,8 @@ } // static -void WorkletThread::EnsureCycleCollectedJSContext(JSRuntime* aParentRuntime) { +void WorkletThread::EnsureCycleCollectedJSContext( + JSRuntime* aParentRuntime, const JS::ContextOptions& aOptions) { CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get(); if (ccjscx) { MOZ_ASSERT(ccjscx->GetAsWorkletJSContext()); @@ -360,6 +362,8 @@ return; } + JS::ContextOptionsRef(context->Context()) = aOptions; + JS_SetGCParameter(context->Context(), JSGC_MAX_BYTES, uint32_t(-1)); // FIXME: JS_SetDefaultLocale diff -Nru firefox-esr-115.3.1esr+build1/dom/worklet/WorkletThread.h firefox-esr-115.4.0esr+build1/dom/worklet/WorkletThread.h --- firefox-esr-115.3.1esr+build1/dom/worklet/WorkletThread.h 2023-09-29 11:00:31.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/dom/worklet/WorkletThread.h 2023-10-17 05:45:46.000000000 +0000 @@ -17,6 +17,10 @@ class nsIRunnable; +namespace JS { +class ContextOptions; +}; // namespace JS + namespace mozilla::dom { class WorkletThread final : public nsThread, public nsIObserver { @@ -29,7 +33,8 @@ // Threads that call EnsureCycleCollectedJSContext must call // DeleteCycleCollectedJSContext::Get() before terminating. Clients of // Create() do not need to do this as Terminate() will ensure this happens. - static void EnsureCycleCollectedJSContext(JSRuntime* aParentRuntime); + static void EnsureCycleCollectedJSContext(JSRuntime* aParentRuntime, + const JS::ContextOptions& aOptions); static void DeleteCycleCollectedJSContext(); static bool IsOnWorkletThread(); diff -Nru firefox-esr-115.3.1esr+build1/extensions/spellcheck/hunspell/patches/bug1838113.patch firefox-esr-115.4.0esr+build1/extensions/spellcheck/hunspell/patches/bug1838113.patch --- firefox-esr-115.3.1esr+build1/extensions/spellcheck/hunspell/patches/bug1838113.patch 1970-01-01 00:00:00.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/extensions/spellcheck/hunspell/patches/bug1838113.patch 2023-10-17 05:45:46.000000000 +0000 @@ -0,0 +1,20 @@ +diff --git a/extensions/spellcheck/hunspell/src/csutil.cxx b/extensions/spellcheck/hunspell/src/csutil.cxx +index 48e58ff4b2677..39a54d38023c8 100644 +--- a/extensions/spellcheck/hunspell/src/csutil.cxx ++++ b/extensions/spellcheck/hunspell/src/csutil.cxx +@@ -108,6 +108,7 @@ static struct unicode_info2* utf_tbl = NULL; + static int utf_tbl_count = + 0; // utf_tbl can be used by multiple Hunspell instances + ++#ifndef MOZILLA_CLIENT + void myopen(std::ifstream& stream, const char* path, std::ios_base::openmode mode) + { + #if defined(_WIN32) && defined(_MSC_VER) +@@ -127,6 +128,7 @@ void myopen(std::ifstream& stream, const char* path, std::ios_base::openmode mod + #endif + stream.open(path, mode); + } ++#endif + + std::string& u16_u8(std::string& dest, const std::vector& src) { + dest.clear(); diff -Nru firefox-esr-115.3.1esr+build1/extensions/spellcheck/hunspell/src/csutil.cxx firefox-esr-115.4.0esr+build1/extensions/spellcheck/hunspell/src/csutil.cxx --- firefox-esr-115.3.1esr+build1/extensions/spellcheck/hunspell/src/csutil.cxx 2023-09-29 10:59:43.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/extensions/spellcheck/hunspell/src/csutil.cxx 2023-10-17 05:45:45.000000000 +0000 @@ -108,6 +108,7 @@ static int utf_tbl_count = 0; // utf_tbl can be used by multiple Hunspell instances +#ifndef MOZILLA_CLIENT void myopen(std::ifstream& stream, const char* path, std::ios_base::openmode mode) { #if defined(_WIN32) && defined(_MSC_VER) @@ -127,6 +128,7 @@ #endif stream.open(path, mode); } +#endif std::string& u16_u8(std::string& dest, const std::vector& src) { dest.clear(); diff -Nru firefox-esr-115.3.1esr+build1/extensions/spellcheck/hunspell/update.sh firefox-esr-115.4.0esr+build1/extensions/spellcheck/hunspell/update.sh --- firefox-esr-115.3.1esr+build1/extensions/spellcheck/hunspell/update.sh 2023-09-29 10:59:43.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/extensions/spellcheck/hunspell/update.sh 2023-10-17 05:45:46.000000000 +0000 @@ -31,3 +31,4 @@ patch -p5 < ../patches/bug1410214.patch patch -p5 < ../patches/bug1653659.patch patch -p5 < ../patches/bug1739761.patch +patch -p5 < ../patches/bug1838113.patch diff -Nru firefox-esr-115.3.1esr+build1/gfx/2d/moz.build firefox-esr-115.4.0esr+build1/gfx/2d/moz.build --- firefox-esr-115.3.1esr+build1/gfx/2d/moz.build 2023-09-29 10:59:43.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/gfx/2d/moz.build 2023-10-17 05:45:46.000000000 +0000 @@ -66,6 +66,8 @@ if CONFIG["MOZ_WIDGET_TOOLKIT"] in ("cocoa", "uikit"): EXPORTS.mozilla.gfx += [ "MacIOSurface.h", + "ScaledFontBase.h", + "ScaledFontMac.h", "UnscaledFontMac.h", ] UNIFIED_SOURCES += [ diff -Nru firefox-esr-115.3.1esr+build1/gfx/2d/ScaledFontMac.cpp firefox-esr-115.4.0esr+build1/gfx/2d/ScaledFontMac.cpp --- firefox-esr-115.3.1esr+build1/gfx/2d/ScaledFontMac.cpp 2023-09-29 10:59:42.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/gfx/2d/ScaledFontMac.cpp 2023-10-17 05:45:46.000000000 +0000 @@ -7,6 +7,7 @@ #include "ScaledFontMac.h" #include "UnscaledFontMac.h" #include "mozilla/webrender/WebRenderTypes.h" +#include "nsCocoaFeatures.h" #include "PathSkia.h" #include "skia/include/core/SkPaint.h" #include "skia/include/core/SkPath.h" @@ -46,11 +47,14 @@ } } - void operator=(T aObject) { - if (mObject) { - CFRelease(mObject); + AutoRelease& operator=(const T& aObject) { + if (aObject != mObject) { + if (mObject) { + CFRelease(mObject); + } + mObject = aObject; } - mObject = aObject; + return *this; } operator T() { return mObject; } @@ -66,51 +70,50 @@ }; // Helper to create a CTFont from a CGFont, copying any variations that were -// set on the original CGFont. -static CTFontRef CreateCTFontFromCGFontWithVariations(CGFontRef aCGFont, - CGFloat aSize, - bool aInstalledFont) { - // Avoid calling potentially buggy variation APIs on pre-Sierra macOS - // versions (see bug 1331683). - // - // And on HighSierra, CTFontCreateWithGraphicsFont properly carries over - // variation settings from the CGFont to CTFont, so we don't need to do - // the extra work here -- and this seems to avoid Core Text crashiness - // seen in bug 1454094. - // - // However, for installed fonts it seems we DO need to copy the variations - // explicitly even on 10.13, otherwise fonts fail to render (as in bug - // 1455494) when non-default values are used. Fortunately, the crash - // mentioned above occurs with data fonts, not (AFAICT) with system- - // installed fonts. - // - // So we only need to do this "the hard way" on Sierra, and for installed - // fonts on HighSierra+; otherwise, just let the standard CTFont function - // do its thing. - // - // NOTE in case this ever needs further adjustment: there is similar logic - // in four places in the tree (sadly): - // CreateCTFontFromCGFontWithVariations in gfxMacFont.cpp - // CreateCTFontFromCGFontWithVariations in ScaledFontMac.cpp - // CreateCTFontFromCGFontWithVariations in cairo-quartz-font.c - // ctfont_create_exact_copy in SkFontHost_mac.cpp +// set on the CGFont, and applying attributes from (optional) aFontDesc. +CTFontRef CreateCTFontFromCGFontWithVariations(CGFontRef aCGFont, CGFloat aSize, + bool aInstalledFont, + CTFontDescriptorRef aFontDesc) { + // New implementation (see bug 1856035) for macOS 13+. + if (nsCocoaFeatures::OnVenturaOrLater()) { + // Create CTFont, applying any descriptor that was passed (used by + // gfxCoreTextShaper to set features). + AutoRelease ctFont( + CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, aFontDesc)); + AutoRelease vars(CGFontCopyVariations(aCGFont)); + if (vars) { + // Create an attribute dictionary containing the variations. + AutoRelease attrs(CFDictionaryCreate( + nullptr, (const void**)&kCTFontVariationAttribute, + (const void**)&vars, 1, &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)); + // Get the original descriptor from the CTFont, then add the variations + // attribute to it. + AutoRelease desc(CTFontCopyFontDescriptor(ctFont)); + desc = CTFontDescriptorCreateCopyWithAttributes(desc, attrs); + // Return a copy of the font that has the variations added. + return CTFontCreateCopyWithAttributes(ctFont, 0.0, nullptr, desc); + } + // No variations to set, just return the default CTFont. + return ctFont.forget(); + } + + // Older implementation used up to macOS 12. CTFontRef ctFont; - if (nsCocoaFeatures::OnSierraExactly() || - (aInstalledFont && nsCocoaFeatures::OnHighSierraOrLater())) { - CFDictionaryRef vars = CGFontCopyVariations(aCGFont); + if (aInstalledFont) { + AutoRelease vars(CGFontCopyVariations(aCGFont)); if (vars) { - CFDictionaryRef varAttr = CFDictionaryCreate( + AutoRelease varAttr(CFDictionaryCreate( nullptr, (const void**)&kCTFontVariationAttribute, (const void**)&vars, 1, &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - CFRelease(vars); + &kCFTypeDictionaryValueCallBacks)); - CTFontDescriptorRef varDesc = - CTFontDescriptorCreateWithAttributes(varAttr); - CFRelease(varAttr); + AutoRelease varDesc( + aFontDesc + ? ::CTFontDescriptorCreateCopyWithAttributes(aFontDesc, varAttr) + : ::CTFontDescriptorCreateWithAttributes(varAttr)); ctFont = CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, varDesc); - CFRelease(varDesc); } else { ctFont = CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, nullptr); } diff -Nru firefox-esr-115.3.1esr+build1/gfx/2d/ScaledFontMac.h firefox-esr-115.4.0esr+build1/gfx/2d/ScaledFontMac.h --- firefox-esr-115.3.1esr+build1/gfx/2d/ScaledFontMac.h 2023-09-29 10:59:43.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/gfx/2d/ScaledFontMac.h 2023-10-17 05:45:46.000000000 +0000 @@ -21,22 +21,28 @@ namespace mozilla { namespace gfx { +// Utility to create a CTFont from a CGFont, copying any variations that were +// set on the original CGFont, and applying additional attributes from aDesc +// (which may be NULL). +// Exposed here because it is also used by gfxMacFont and gfxCoreTextShaper. +CTFontRef CreateCTFontFromCGFontWithVariations(CGFontRef aCGFont, CGFloat aSize, + bool aInstalledFont, + CTFontDescriptorRef aFontDesc = nullptr); + class UnscaledFontMac; class ScaledFontMac : public ScaledFontBase { public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontMac, override) - ScaledFontMac( - CGFontRef aFont, const RefPtr& aUnscaledFont, Float aSize, - bool aOwnsFont = false, - const DeviceColor& aFontSmoothingBackgroundColor = DeviceColor(), - bool aUseFontSmoothing = true, bool aApplySyntheticBold = false, - bool aHasColorGlyphs = false); - ScaledFontMac( - CTFontRef aFont, const RefPtr& aUnscaledFont, - const DeviceColor& aFontSmoothingBackgroundColor = DeviceColor(), - bool aUseFontSmoothing = true, bool aApplySyntheticBold = false, - bool aHasColorGlyphs = false); + ScaledFontMac(CGFontRef aFont, const RefPtr& aUnscaledFont, Float aSize, + bool aOwnsFont = false, + const DeviceColor& aFontSmoothingBackgroundColor = DeviceColor(), + bool aUseFontSmoothing = true, bool aApplySyntheticBold = false, + bool aHasColorGlyphs = false); + ScaledFontMac(CTFontRef aFont, const RefPtr& aUnscaledFont, + const DeviceColor& aFontSmoothingBackgroundColor = DeviceColor(), + bool aUseFontSmoothing = true, bool aApplySyntheticBold = false, + bool aHasColorGlyphs = false); ~ScaledFontMac(); FontType GetType() const override { return FontType::MAC; } @@ -47,10 +53,9 @@ bool GetFontInstanceData(FontInstanceDataOutput aCb, void* aBaton) override; - bool GetWRFontInstanceOptions( - Maybe* aOutOptions, - Maybe* aOutPlatformOptions, - std::vector* aOutVariations) override; + bool GetWRFontInstanceOptions(Maybe* aOutOptions, + Maybe* aOutPlatformOptions, + std::vector* aOutVariations) override; bool CanSerialize() override { return true; } @@ -58,20 +63,16 @@ bool UseSubpixelPosition() const override { return true; } - DeviceColor FontSmoothingBackgroundColor() { - return mFontSmoothingBackgroundColor; - } + DeviceColor FontSmoothingBackgroundColor() { return mFontSmoothingBackgroundColor; } - cairo_font_face_t* CreateCairoFontFace( - cairo_font_options_t* aFontOptions) override; + cairo_font_face_t* CreateCairoFontFace(cairo_font_options_t* aFontOptions) override; private: friend class DrawTargetSkia; friend class UnscaledFontMac; CGFontRef mFont; - CTFontRef - mCTFont; // only created if CTFontDrawGlyphs is available, otherwise null + CTFontRef mCTFont; // only created if CTFontDrawGlyphs is available, otherwise null DeviceColor mFontSmoothingBackgroundColor; bool mUseFontSmoothing; @@ -80,8 +81,7 @@ struct InstanceData { explicit InstanceData(ScaledFontMac* aScaledFont) - : mFontSmoothingBackgroundColor( - aScaledFont->mFontSmoothingBackgroundColor), + : mFontSmoothingBackgroundColor(aScaledFont->mFontSmoothingBackgroundColor), mUseFontSmoothing(aScaledFont->mUseFontSmoothing), mApplySyntheticBold(aScaledFont->mApplySyntheticBold), mHasColorGlyphs(aScaledFont->mHasColorGlyphs) {} diff -Nru firefox-esr-115.3.1esr+build1/gfx/config/gfxVars.h firefox-esr-115.4.0esr+build1/gfx/config/gfxVars.h --- firefox-esr-115.3.1esr+build1/gfx/config/gfxVars.h 2023-09-29 10:59:43.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/gfx/config/gfxVars.h 2023-10-17 05:45:46.000000000 +0000 @@ -157,6 +157,12 @@ size_t mIndex; }; + // Whether the gfxVars singleton instance has been initialized. Most gfx code + // doesn't need to check this, but code that can potentially run before + // gfxPlatform initialization can use this to check whether gfxVars are + // available yet. + static bool IsInitialized() { return sInstance != nullptr; } + private: static StaticAutoPtr sInstance; static StaticAutoPtr> sVarList; diff -Nru firefox-esr-115.3.1esr+build1/gfx/gl/GLContext.cpp firefox-esr-115.4.0esr+build1/gfx/gl/GLContext.cpp --- firefox-esr-115.3.1esr+build1/gfx/gl/GLContext.cpp 2023-09-29 10:59:44.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/gfx/gl/GLContext.cpp 2023-10-17 05:45:46.000000000 +0000 @@ -70,7 +70,8 @@ using namespace mozilla::gfx; using namespace mozilla::layers; -MOZ_THREAD_LOCAL(uintptr_t) GLContext::sCurrentContext; +// Zero-initialized after init(). +MOZ_THREAD_LOCAL(const GLContext*) GLContext::sCurrentContext; // If adding defines, don't forget to undefine symbols. See #undef block below. // clang-format off @@ -288,10 +289,7 @@ mSharedContext(sharedContext), mOwningThreadId(Some(PlatformThread::CurrentId())), mWorkAroundDriverBugs( - StaticPrefs::gfx_work_around_driver_bugs_AtStartup()) { - MOZ_ALWAYS_TRUE(sCurrentContext.init()); - sCurrentContext.set(0); -} + StaticPrefs::gfx_work_around_driver_bugs_AtStartup()) {} GLContext::~GLContext() { NS_ASSERTION( @@ -307,6 +305,11 @@ ReportOutstandingNames(); } #endif + // Ensure we clear sCurrentContext if we were the last context set and avoid + // the memory getting reused. + if (sCurrentContext.init() && sCurrentContext.get() == this) { + sCurrentContext.set(nullptr); + } } /*static*/ @@ -2434,8 +2437,8 @@ if (MOZ_LIKELY(!aForce)) { bool isCurrent; - if (mUseTLSIsCurrent) { - isCurrent = (sCurrentContext.get() == reinterpret_cast(this)); + if (mUseTLSIsCurrent && sCurrentContext.init()) { + isCurrent = (sCurrentContext.get() == this); } else { isCurrent = IsCurrentImpl(); } @@ -2454,7 +2457,9 @@ } if (!MakeCurrentImpl()) return false; - sCurrentContext.set(reinterpret_cast(this)); + if (sCurrentContext.init()) { + sCurrentContext.set(this); + } return true; } diff -Nru firefox-esr-115.3.1esr+build1/gfx/gl/GLContext.h firefox-esr-115.4.0esr+build1/gfx/gl/GLContext.h --- firefox-esr-115.3.1esr+build1/gfx/gl/GLContext.h 2023-09-29 10:59:44.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/gfx/gl/GLContext.h 2023-10-17 05:45:46.000000000 +0000 @@ -178,7 +178,7 @@ class GLContext : public GenericAtomicRefCounted, public SupportsWeakPtr { public: - static MOZ_THREAD_LOCAL(uintptr_t) sCurrentContext; + static MOZ_THREAD_LOCAL(const GLContext*) sCurrentContext; const GLContextDesc mDesc; diff -Nru firefox-esr-115.3.1esr+build1/gfx/thebes/gfxCoreTextShaper.cpp firefox-esr-115.4.0esr+build1/gfx/thebes/gfxCoreTextShaper.cpp --- firefox-esr-115.3.1esr+build1/gfx/thebes/gfxCoreTextShaper.cpp 2023-09-29 10:59:44.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/gfx/thebes/gfxCoreTextShaper.cpp 2023-10-17 05:45:46.000000000 +0000 @@ -9,6 +9,7 @@ #include "gfxFontUtils.h" #include "gfxTextRun.h" #include "mozilla/gfx/2D.h" +#include "mozilla/gfx/ScaledFontMac.h" #include "mozilla/UniquePtrExtensions.h" #include @@ -16,6 +17,7 @@ #include using namespace mozilla; +using namespace mozilla::gfx; // standard font descriptors that we construct the first time they're needed CTFontDescriptorRef gfxCoreTextShaper::sFeaturesDescriptor[kMaxFontInstances]; @@ -634,8 +636,8 @@ const gfxFontEntry* fe = mFont->GetFontEntry(); bool isInstalledFont = !fe->IsUserFont() || fe->IsLocalUserFont(); CGFontRef cgFont = static_cast(mFont)->GetCGFontRef(); - return gfxMacFont::CreateCTFontFromCGFontWithVariations( - cgFont, aSize, isInstalledFont, aDescriptor); + return CreateCTFontFromCGFontWithVariations(cgFont, aSize, isInstalledFont, + aDescriptor); } void gfxCoreTextShaper::Shutdown() // [static] diff -Nru firefox-esr-115.3.1esr+build1/gfx/thebes/gfxDWriteFonts.cpp firefox-esr-115.4.0esr+build1/gfx/thebes/gfxDWriteFonts.cpp --- firefox-esr-115.3.1esr+build1/gfx/thebes/gfxDWriteFonts.cpp 2023-09-29 10:59:44.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/gfx/thebes/gfxDWriteFonts.cpp 2023-10-17 05:45:46.000000000 +0000 @@ -135,6 +135,10 @@ void gfxDWriteFont::UpdateSystemTextVars() { MOZ_ASSERT(XRE_IsParentProcess()); + if (!gfxVars::IsInitialized()) { + return; + } + BYTE newQuality = GetSystemTextQuality(); if (gfxVars::SystemTextQuality() != newQuality) { gfxVars::SetSystemTextQuality(newQuality); diff -Nru firefox-esr-115.3.1esr+build1/gfx/thebes/gfxMacFont.cpp firefox-esr-115.4.0esr+build1/gfx/thebes/gfxMacFont.cpp --- firefox-esr-115.3.1esr+build1/gfx/thebes/gfxMacFont.cpp 2023-09-29 10:59:44.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/gfx/thebes/gfxMacFont.cpp 2023-10-17 05:45:46.000000000 +0000 @@ -8,6 +8,7 @@ #include "mozilla/MemoryReporting.h" #include "mozilla/Sprintf.h" #include "mozilla/StaticPrefs_gfx.h" +#include "mozilla/gfx/ScaledFontMac.h" #include "gfxCoreTextShaper.h" #include @@ -435,62 +436,6 @@ return 0; } -/* static */ -CTFontRef gfxMacFont::CreateCTFontFromCGFontWithVariations( - CGFontRef aCGFont, CGFloat aSize, bool aInstalledFont, - CTFontDescriptorRef aFontDesc) { - // Avoid calling potentially buggy variation APIs on pre-Sierra macOS - // versions (see bug 1331683). - // - // And on HighSierra, CTFontCreateWithGraphicsFont properly carries over - // variation settings from the CGFont to CTFont, so we don't need to do - // the extra work here -- and this seems to avoid Core Text crashiness - // seen in bug 1454094. - // - // However, for installed fonts it seems we DO need to copy the variations - // explicitly even on 10.13, otherwise fonts fail to render (as in bug - // 1455494) when non-default values are used. Fortunately, the crash - // mentioned above occurs with data fonts, not (AFAICT) with system- - // installed fonts. - // - // So we only need to do this "the hard way" on Sierra, and on HighSierra - // for system-installed fonts; in other cases just let the standard CTFont - // function do its thing. - // - // NOTE in case this ever needs further adjustment: there is similar logic - // in four places in the tree (sadly): - // CreateCTFontFromCGFontWithVariations in gfxMacFont.cpp - // CreateCTFontFromCGFontWithVariations in ScaledFontMac.cpp - // CreateCTFontFromCGFontWithVariations in cairo-quartz-font.c - // ctfont_create_exact_copy in SkFontHost_mac.cpp - - CTFontRef ctFont; - if (nsCocoaFeatures::OnSierraExactly() || - (aInstalledFont && nsCocoaFeatures::OnHighSierraOrLater())) { - AutoCFRelease variations = ::CGFontCopyVariations(aCGFont); - if (variations) { - AutoCFRelease varAttr = ::CFDictionaryCreate( - nullptr, (const void**)&kCTFontVariationAttribute, - (const void**)&variations, 1, &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - - AutoCFRelease varDesc = - aFontDesc - ? ::CTFontDescriptorCreateCopyWithAttributes(aFontDesc, varAttr) - : ::CTFontDescriptorCreateWithAttributes(varAttr); - - ctFont = ::CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, varDesc); - } else { - ctFont = - ::CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, aFontDesc); - } - } else { - ctFont = ::CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, aFontDesc); - } - - return ctFont; -} - int32_t gfxMacFont::GetGlyphWidth(uint16_t aGID) { if (mVariationFont) { // Avoid a potential Core Text crash (bug 1450209) by using diff -Nru firefox-esr-115.3.1esr+build1/gfx/thebes/gfxMacFont.h firefox-esr-115.4.0esr+build1/gfx/thebes/gfxMacFont.h --- firefox-esr-115.3.1esr+build1/gfx/thebes/gfxMacFont.h 2023-09-29 10:59:44.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/gfx/thebes/gfxMacFont.h 2023-10-17 05:45:46.000000000 +0000 @@ -16,21 +16,24 @@ class gfxMacFont final : public gfxFont { public: - gfxMacFont(const RefPtr& aUnscaledFont, MacOSFontEntry* aFontEntry, - const gfxFontStyle* aFontStyle); + gfxMacFont(const RefPtr& aUnscaledFont, + MacOSFontEntry* aFontEntry, const gfxFontStyle* aFontStyle); CGFontRef GetCGFontRef() const { return mCGFont; } /* override Measure to add padding for antialiasing */ RunMetrics Measure(const gfxTextRun* aTextRun, uint32_t aStart, uint32_t aEnd, - BoundingBoxType aBoundingBoxType, DrawTarget* aDrawTargetForTightBoundingBox, - Spacing* aSpacing, mozilla::gfx::ShapedTextFlags aOrientation) override; + BoundingBoxType aBoundingBoxType, + DrawTarget* aDrawTargetForTightBoundingBox, + Spacing* aSpacing, + mozilla::gfx::ShapedTextFlags aOrientation) override; // We need to provide hinted (non-linear) glyph widths if using a font // with embedded color bitmaps (Apple Color Emoji), as Core Text renders // the glyphs with non-linear scaling at small pixel sizes. bool ProvidesGlyphWidths() const override { - return mVariationFont || mFontEntry->HasFontTable(TRUETYPE_TAG('s', 'b', 'i', 'x')); + return mVariationFont || + mFontEntry->HasFontTable(TRUETYPE_TAG('s', 'b', 'i', 'x')); } int32_t GetGlyphWidth(uint16_t aGID) override; @@ -51,21 +54,15 @@ bool UseNativeColrFontSupport() const override; - // Helper to create a CTFont from a CGFont, with optional font descriptor - // (for features), and copying any variations that were set on the CGFont. - // This is public so that gfxCoreTextShaper can also use it. - static CTFontRef CreateCTFontFromCGFontWithVariations(CGFontRef aCGFont, CGFloat aSize, - bool aInstalledFont, - CTFontDescriptorRef aFontDesc = nullptr); - protected: ~gfxMacFont() override; const Metrics& GetHorizontalMetrics() const override { return mMetrics; } // override to prefer CoreText shaping with fonts that depend on AAT - bool ShapeText(DrawTarget* aDrawTarget, const char16_t* aText, uint32_t aOffset, uint32_t aLength, - Script aScript, nsAtom* aLanguage, bool aVertical, RoundingFlags aRounding, + bool ShapeText(DrawTarget* aDrawTarget, const char16_t* aText, + uint32_t aOffset, uint32_t aLength, Script aScript, + nsAtom* aLanguage, bool aVertical, RoundingFlags aRounding, gfxShapedText* aShapedText) override; void InitMetrics(); diff -Nru firefox-esr-115.3.1esr+build1/gfx/thebes/gfxPlatformFontList.cpp firefox-esr-115.4.0esr+build1/gfx/thebes/gfxPlatformFontList.cpp --- firefox-esr-115.3.1esr+build1/gfx/thebes/gfxPlatformFontList.cpp 2023-09-29 10:59:44.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/gfx/thebes/gfxPlatformFontList.cpp 2023-10-17 05:45:46.000000000 +0000 @@ -1310,6 +1310,7 @@ }; void gfxPlatformFontList::StartCmapLoadingFromFamily(uint32_t aStartIndex) { + AutoLock lock(mLock); if (aStartIndex > mStartedLoadingCmapsFrom) { // We already initiated cmap-loading from somewhere earlier in the list; // no need to do it again here. diff -Nru firefox-esr-115.3.1esr+build1/gfx/thebes/gfxPlatformFontList.h firefox-esr-115.4.0esr+build1/gfx/thebes/gfxPlatformFontList.h --- firefox-esr-115.3.1esr+build1/gfx/thebes/gfxPlatformFontList.h 2023-09-29 10:59:44.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/gfx/thebes/gfxPlatformFontList.h 2023-10-17 05:45:46.000000000 +0000 @@ -1022,9 +1022,9 @@ nsTHashtable mShmemCharMaps MOZ_GUARDED_BY(mLock); // data used as part of the font cmap loading process - nsTArray> mFontFamiliesToLoad; - uint32_t mStartIndex = 0; - uint32_t mNumFamilies = 0; + nsTArray> mFontFamiliesToLoad MOZ_GUARDED_BY(mLock); + uint32_t mStartIndex MOZ_GUARDED_BY(mLock) = 0; + uint32_t mNumFamilies MOZ_GUARDED_BY(mLock) = 0; // xxx - info for diagnosing no default font aborts // see bugs 636957, 1070983, 1189129 @@ -1055,7 +1055,7 @@ RefPtr mDefaultFontEntry MOZ_GUARDED_BY(mLock); RefPtr mLoadCmapsRunnable; - uint32_t mStartedLoadingCmapsFrom = 0xffffffffu; + uint32_t mStartedLoadingCmapsFrom MOZ_GUARDED_BY(mLock) = 0xffffffffu; bool mFontFamilyWhitelistActive = false; diff -Nru firefox-esr-115.3.1esr+build1/gfx/wr/webrender/res/cs_border_solid.glsl firefox-esr-115.4.0esr+build1/gfx/wr/webrender/res/cs_border_solid.glsl --- firefox-esr-115.3.1esr+build1/gfx/wr/webrender/res/cs_border_solid.glsl 2023-09-29 10:59:44.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/gfx/wr/webrender/res/cs_border_solid.glsl 2023-10-17 05:45:46.000000000 +0000 @@ -15,7 +15,7 @@ // A point + tangent defining the line where the edge // transition occurs. Used for corners only. -flat varying mediump vec4 vColorLine; +flat varying highp vec4 vColorLine; // A boolean indicating that we should be mixing between edge colors. // Packed in to a vector to work around bug 1630356. diff -Nru firefox-esr-115.3.1esr+build1/intl/icu/source/common/putil.cpp firefox-esr-115.4.0esr+build1/intl/icu/source/common/putil.cpp --- firefox-esr-115.3.1esr+build1/intl/icu/source/common/putil.cpp 2023-09-29 10:59:44.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/intl/icu/source/common/putil.cpp 2023-10-17 05:45:46.000000000 +0000 @@ -1175,6 +1175,21 @@ if (ret != nullptr && uprv_strcmp(TZDEFAULT, gTimeZoneBuffer) != 0) { int32_t tzZoneInfoTailLen = uprv_strlen(TZZONEINFOTAIL); const char *tzZoneInfoTailPtr = uprv_strstr(gTimeZoneBuffer, TZZONEINFOTAIL); + // MacOS14 has the realpath as something like + // /usr/share/zoneinfo.default/Australia/Melbourne + // which will not have "/zoneinfo/" in the path. + // Therefore if we fail, we fall back to read the link which is + // /var/db/timezone/zoneinfo/Australia/Melbourne + // We also fall back to reading the link if the realpath leads to something like + // /usr/share/zoneinfo/posixrules + if (tzZoneInfoTailPtr == nullptr || + uprv_strcmp(tzZoneInfoTailPtr + tzZoneInfoTailLen, "posixrules") == 0) { + ssize_t size = readlink(TZDEFAULT, gTimeZoneBuffer, sizeof(gTimeZoneBuffer)-1); + if (size > 0) { + gTimeZoneBuffer[size] = 0; + tzZoneInfoTailPtr = uprv_strstr(gTimeZoneBuffer, TZZONEINFOTAIL); + } + } if (tzZoneInfoTailPtr != nullptr) { tzZoneInfoTailPtr += tzZoneInfoTailLen; skipZoneIDPrefix(&tzZoneInfoTailPtr); diff -Nru firefox-esr-115.3.1esr+build1/intl/icu-patches/bug-1856428-ICU-22541.diff firefox-esr-115.4.0esr+build1/intl/icu-patches/bug-1856428-ICU-22541.diff --- firefox-esr-115.3.1esr+build1/intl/icu-patches/bug-1856428-ICU-22541.diff 1970-01-01 00:00:00.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/intl/icu-patches/bug-1856428-ICU-22541.diff 2023-10-17 05:45:46.000000000 +0000 @@ -0,0 +1,39 @@ +# Fix MacOS 14 default timezone issue +# +# ICU bug: https://unicode-org.atlassian.net/browse/ICU-22541 + +diff --git a/intl/icu/source/common/putil.cpp b/intl/icu/source/common/putil.cpp +--- a/intl/icu/source/common/putil.cpp ++++ b/intl/icu/source/common/putil.cpp +@@ -1170,16 +1170,31 @@ uprv_tzname(int n) + This is a trick to look at the name of the link to get the Olson ID + because the tzfile contents is underspecified. + This isn't guaranteed to work because it may not be a symlink. + */ + char *ret = realpath(TZDEFAULT, gTimeZoneBuffer); + if (ret != nullptr && uprv_strcmp(TZDEFAULT, gTimeZoneBuffer) != 0) { + int32_t tzZoneInfoTailLen = uprv_strlen(TZZONEINFOTAIL); + const char *tzZoneInfoTailPtr = uprv_strstr(gTimeZoneBuffer, TZZONEINFOTAIL); ++ // MacOS14 has the realpath as something like ++ // /usr/share/zoneinfo.default/Australia/Melbourne ++ // which will not have "/zoneinfo/" in the path. ++ // Therefore if we fail, we fall back to read the link which is ++ // /var/db/timezone/zoneinfo/Australia/Melbourne ++ // We also fall back to reading the link if the realpath leads to something like ++ // /usr/share/zoneinfo/posixrules ++ if (tzZoneInfoTailPtr == nullptr || ++ uprv_strcmp(tzZoneInfoTailPtr + tzZoneInfoTailLen, "posixrules") == 0) { ++ ssize_t size = readlink(TZDEFAULT, gTimeZoneBuffer, sizeof(gTimeZoneBuffer)-1); ++ if (size > 0) { ++ gTimeZoneBuffer[size] = 0; ++ tzZoneInfoTailPtr = uprv_strstr(gTimeZoneBuffer, TZZONEINFOTAIL); ++ } ++ } + if (tzZoneInfoTailPtr != nullptr) { + tzZoneInfoTailPtr += tzZoneInfoTailLen; + skipZoneIDPrefix(&tzZoneInfoTailPtr); + if (isValidOlsonID(tzZoneInfoTailPtr)) { + return (gTimeZoneBufferPtr = tzZoneInfoTailPtr); + } + } + } else { diff -Nru firefox-esr-115.3.1esr+build1/intl/update-icu.sh firefox-esr-115.4.0esr+build1/intl/update-icu.sh --- firefox-esr-115.3.1esr+build1/intl/update-icu.sh 2023-09-29 10:59:45.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/intl/update-icu.sh 2023-10-17 05:45:46.000000000 +0000 @@ -59,6 +59,7 @@ bug-1706949-wasi-workaround.diff \ bug-1790071-ICU-22132-standardize-vtzone-output.diff \ bug-1838173-ICU-22412-start-time-iso8601.diff \ + bug-1856428-ICU-22541.diff \ ; do echo "Applying local patch $patch" patch -d ${icu_dir}/../../ -p1 --no-backup-if-mismatch < ${icu_dir}/../icu-patches/$patch diff -Nru firefox-esr-115.3.1esr+build1/js/src/builtin/WeakRefObject.cpp firefox-esr-115.4.0esr+build1/js/src/builtin/WeakRefObject.cpp --- firefox-esr-115.3.1esr+build1/js/src/builtin/WeakRefObject.cpp 2023-09-29 10:59:46.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/js/src/builtin/WeakRefObject.cpp 2023-10-17 05:45:47.000000000 +0000 @@ -8,6 +8,7 @@ #include "jsapi.h" +#include "gc/FinalizationObservers.h" #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* #include "vm/GlobalObject.h" #include "vm/JSContext.h" @@ -243,7 +244,7 @@ MOZ_ASSERT(cx->runtime()->hasReleasedWrapperCallback); bool wasReleased = cx->runtime()->hasReleasedWrapperCallback(obj); if (wasReleased) { - self->clearTarget(); + obj->zone()->finalizationObservers()->removeWeakRefTarget(obj, self); return; } } diff -Nru firefox-esr-115.3.1esr+build1/js/src/gc/FinalizationObservers.cpp firefox-esr-115.4.0esr+build1/js/src/gc/FinalizationObservers.cpp --- firefox-esr-115.3.1esr+build1/js/src/gc/FinalizationObservers.cpp 2023-09-29 10:59:46.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/js/src/gc/FinalizationObservers.cpp 2023-10-17 05:45:46.000000000 +0000 @@ -360,6 +360,30 @@ return true; } +static WeakRefObject* UnwrapWeakRef(JSObject* obj) { + MOZ_ASSERT(!JS_IsDeadWrapper(obj)); + obj = UncheckedUnwrapWithoutExpose(obj); + return &obj->as(); +} + +void FinalizationObservers::removeWeakRefTarget( + Handle target, Handle weakRef) { + MOZ_ASSERT(target); + + WeakRefHeapPtrVector& weakRefs = weakRefMap.lookup(target)->value(); + JSObject* wrapper = nullptr; + weakRefs.eraseIf([weakRef, &wrapper](JSObject* obj) { + if (UnwrapWeakRef(obj) == weakRef) { + wrapper = obj; + return true; + } + return false; + }); + + MOZ_ASSERT(wrapper); + updateForRemovedWeakRef(wrapper, weakRef); +} + void GCRuntime::nukeWeakRefWrapper(JSObject* wrapper, WeakRefObject* weakRef) { // WeakRef wrappers can exist independently of the ones we create for the // weakRefMap so don't assume |wrapper| is in the same zone as the WeakRef @@ -405,12 +429,6 @@ } } -static WeakRefObject* UnwrapWeakRef(JSObject* obj) { - MOZ_ASSERT(!JS_IsDeadWrapper(obj)); - obj = UncheckedUnwrapWithoutExpose(obj); - return &obj->as(); -} - void FinalizationObservers::traceWeakWeakRefEdges(JSTracer* trc) { for (WeakRefMap::Enum e(weakRefMap); !e.empty(); e.popFront()) { // If target is dying, clear the target field of all weakRefs, and remove diff -Nru firefox-esr-115.3.1esr+build1/js/src/gc/FinalizationObservers.h firefox-esr-115.4.0esr+build1/js/src/gc/FinalizationObservers.h --- firefox-esr-115.3.1esr+build1/js/src/gc/FinalizationObservers.h 2023-09-29 10:59:45.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/js/src/gc/FinalizationObservers.h 2023-10-17 05:45:47.000000000 +0000 @@ -78,7 +78,9 @@ FinalizationRecordObject* record); // WeakRef support: - bool addWeakRefTarget(HandleObject target, HandleObject weakRef); + bool addWeakRefTarget(Handle target, Handle weakRef); + void removeWeakRefTarget(Handle target, + Handle weakRef); void unregisterWeakRefWrapper(JSObject* wrapper, WeakRefObject* weakRef); diff -Nru firefox-esr-115.3.1esr+build1/js/src/jit-test/tests/gc/bug-1852729.js firefox-esr-115.4.0esr+build1/js/src/jit-test/tests/gc/bug-1852729.js --- firefox-esr-115.3.1esr+build1/js/src/jit-test/tests/gc/bug-1852729.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/js/src/jit-test/tests/gc/bug-1852729.js 2023-10-17 05:45:47.000000000 +0000 @@ -0,0 +1,5 @@ +const g = newGlobal({newCompartment: true}); +const domObj = this.transplantableObject().object; +const bar = new g.WeakRef(domObj); +bar.deref(); +this.nukeAllCCWs(); diff -Nru firefox-esr-115.3.1esr+build1/js/src/vm/NativeObject.h firefox-esr-115.4.0esr+build1/js/src/vm/NativeObject.h --- firefox-esr-115.3.1esr+build1/js/src/vm/NativeObject.h 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/js/src/vm/NativeObject.h 2023-10-17 05:45:50.000000000 +0000 @@ -461,6 +461,13 @@ static inline size_t allocCount(size_t slotCount) { static_assert(sizeof(ObjectSlots) == ObjectSlots::VALUES_PER_HEADER * sizeof(HeapSlot)); +#ifdef MOZ_VALGRIND + if (slotCount == 0) { + // Add an extra unused slot so that NativeObject::slots_ always points + // into the allocation otherwise valgrind thinks this is a leak. + slotCount = 1; + } +#endif return slotCount + VALUES_PER_HEADER; } diff -Nru firefox-esr-115.3.1esr+build1/layout/base/nsCSSFrameConstructor.cpp firefox-esr-115.4.0esr+build1/layout/base/nsCSSFrameConstructor.cpp --- firefox-esr-115.3.1esr+build1/layout/base/nsCSSFrameConstructor.cpp 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/base/nsCSSFrameConstructor.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -3714,6 +3714,11 @@ return &sCanvasData; } +static MOZ_NEVER_INLINE void DestroyFramesInList(PresShell* aPs, nsFrameList& aList) { + nsIFrame::DestroyContext context(aPs); + aList.DestroyFrames(context); +} + void nsCSSFrameConstructor::ConstructFrameFromItemInternal( FrameConstructionItem& aItem, nsFrameConstructorState& aState, nsContainerFrame* aParentFrame, nsFrameList& aFrameList) { @@ -3923,7 +3928,7 @@ if (childList.NotEmpty()) { // an error must have occurred, delete unprocessed frames - childList.DestroyFrames(); + DestroyFramesInList(mPresShell, childList); } childList = std::move(newList); @@ -7079,7 +7084,9 @@ nsIContent* const nextSibling = aStartChild->GetNextSibling(); MOZ_ASSERT(nextSibling && nextSibling->IsText(), "expected a text node after the list-style-image image"); - RemoveFrame(FrameChildListID::Principal, nextSibling->GetPrimaryFrame()); + DestroyContext context(mPresShell); + RemoveFrame(context, FrameChildListID::Principal, + nextSibling->GetPrimaryFrame()); auto* const container = aStartChild->GetParent()->AsElement(); nsIContent* firstNewChild = nullptr; auto InsertChild = [this, container, nextSibling, @@ -7557,7 +7564,9 @@ parentFrame = childFrame->GetParent(); } - RemoveFrame(nsLayoutUtils::GetChildListNameFor(childFrame), childFrame); + DestroyContext context(mPresShell); + RemoveFrame(context, nsLayoutUtils::GetChildListNameFor(childFrame), + childFrame); // NOTE(emilio): aChild could be dead here already if it is a ::before or // ::after pseudo-element (since in that case it was owned by childFrame, @@ -10195,20 +10204,22 @@ WrapFramesInFirstLetterFrame( aBlockFrame, aBlockFrame, aBlockFrame, aBlockFrames.FirstChild(), &parentFrame, &textFrame, &prevFrame, letterFrames, &stopLooking); - if (parentFrame) { - if (parentFrame == aBlockFrame) { - // Take textFrame out of the block's frame list and substitute the - // letter frame(s) instead. - aBlockFrames.DestroyFrame(textFrame); - aBlockFrames.InsertFrames(nullptr, prevFrame, std::move(letterFrames)); - } else { - // Take the old textFrame out of the inline parent's child list - RemoveFrame(FrameChildListID::Principal, textFrame); + if (!parentFrame) { + return; + } + DestroyContext context(mPresShell); + if (parentFrame == aBlockFrame) { + // Take textFrame out of the block's frame list and substitute the + // letter frame(s) instead. + aBlockFrames.DestroyFrame(context, textFrame); + aBlockFrames.InsertFrames(nullptr, prevFrame, std::move(letterFrames)); + } else { + // Take the old textFrame out of the inline parent's child list + RemoveFrame(context, FrameChildListID::Principal, textFrame); - // Insert in the letter frame(s) - parentFrame->InsertFrames(FrameChildListID::Principal, prevFrame, nullptr, - std::move(letterFrames)); - } + // Insert in the letter frame(s) + parentFrame->InsertFrames(FrameChildListID::Principal, prevFrame, nullptr, + std::move(letterFrames)); } } @@ -10348,9 +10359,10 @@ // Destroy the old text frame's continuations (the old text frame // will be destroyed when its letter frame is destroyed). nsIFrame* frameToDelete = textFrame->LastContinuation(); + DestroyContext context(mPresShell); while (frameToDelete != textFrame) { nsIFrame* nextFrameToDelete = frameToDelete->GetPrevContinuation(); - RemoveFrame(FrameChildListID::Principal, frameToDelete); + RemoveFrame(context, FrameChildListID::Principal, frameToDelete); frameToDelete = nextFrameToDelete; } @@ -10365,7 +10377,7 @@ #endif // Remove placeholder frame and the float - RemoveFrame(FrameChildListID::Principal, placeholderFrame); + RemoveFrame(context, FrameChildListID::Principal, placeholderFrame); // Now that the old frames are gone, we can start pointing to our // new primary frame. @@ -10414,8 +10426,10 @@ textFrame = NS_NewTextFrame(aPresShell, newSC); textFrame->Init(textContent, aFrame, nullptr); + DestroyContext context(mPresShell); + // Next rip out the kid and replace it with the text frame - RemoveFrame(FrameChildListID::Principal, kid); + RemoveFrame(context, FrameChildListID::Principal, kid); // Now that the old frames are gone, we can start pointing to our // new primary frame. @@ -10440,7 +10454,8 @@ "should have the first continuation here"); aBlockFrame->RemoveStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD); break; - } else if (IsInlineFrame(kid)) { + } + if (IsInlineFrame(kid)) { nsContainerFrame* kidAsContainerFrame = do_QueryFrame(kid); if (kidAsContainerFrame) { // Look inside child inline frame for the letter frame. @@ -10501,14 +10516,16 @@ static_cast(continuation->GetNextContinuation()); } while (continuation); - if (parentFrame) { - // Take the old textFrame out of the parent's child list - RemoveFrame(FrameChildListID::Principal, textFrame); - - // Insert in the letter frame(s) - parentFrame->InsertFrames(FrameChildListID::Principal, prevFrame, nullptr, - std::move(letterFrames)); + if (!parentFrame) { + return; } + // Take the old textFrame out of the parent's child list + DestroyContext context(mPresShell); + RemoveFrame(context, FrameChildListID::Principal, textFrame); + + // Insert in the letter frame(s) + parentFrame->InsertFrames(FrameChildListID::Principal, prevFrame, nullptr, + std::move(letterFrames)); } //---------------------------------------------------------------------- @@ -10931,7 +10948,8 @@ // associated out-of-flow frames properly, we need to manually flush all the // out-of-flow frames in aState to their container frames. aState.ProcessFrameInsertionsForAllLists(); - aFrameList.DestroyFrames(); + DestroyContext context(mPresShell); + aFrameList.DestroyFrames(context); RecreateFramesForContent( GetMultiColumnContainingBlockFor(aParentFrame)->GetContent(), InsertionKind::Async); diff -Nru firefox-esr-115.3.1esr+build1/layout/base/nsFrameManager.cpp firefox-esr-115.4.0esr+build1/layout/base/nsFrameManager.cpp --- firefox-esr-115.3.1esr+build1/layout/base/nsFrameManager.cpp 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/base/nsFrameManager.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -50,7 +50,8 @@ mPresShell->SetIgnoreFrameDestruction(true); if (mRootFrame) { - mRootFrame->Destroy(); + FrameDestroyContext context(mPresShell); + mRootFrame->Destroy(context); mRootFrame = nullptr; } @@ -92,7 +93,8 @@ } } -void nsFrameManager::RemoveFrame(FrameChildListID aListID, +void nsFrameManager::RemoveFrame(DestroyContext& aContext, + FrameChildListID aListID, nsIFrame* aOldFrame) { // In case the reflow doesn't invalidate anything since it just leaves // a gap where the old frame was, we invalidate it here. (This is @@ -113,10 +115,10 @@ nsContainerFrame* parentFrame = aOldFrame->GetParent(); if (parentFrame->IsAbsoluteContainer() && aListID == parentFrame->GetAbsoluteListID()) { - parentFrame->GetAbsoluteContainingBlock()->RemoveFrame(parentFrame, aListID, + parentFrame->GetAbsoluteContainingBlock()->RemoveFrame(aContext, aListID, aOldFrame); } else { - parentFrame->RemoveFrame(aListID, aOldFrame); + parentFrame->RemoveFrame(aContext, aListID, aOldFrame); } } diff -Nru firefox-esr-115.3.1esr+build1/layout/base/nsFrameManager.h firefox-esr-115.4.0esr+build1/layout/base/nsFrameManager.h --- firefox-esr-115.3.1esr+build1/layout/base/nsFrameManager.h 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/base/nsFrameManager.h 2023-10-17 05:45:50.000000000 +0000 @@ -20,6 +20,7 @@ class nsWindowSizes; namespace mozilla { +struct FrameDestroyContext; class PresShell; } // namespace mozilla @@ -31,6 +32,8 @@ */ class nsFrameManager { public: + using DestroyContext = mozilla::FrameDestroyContext; + explicit nsFrameManager(mozilla::PresShell* aPresShell) : mPresShell(aPresShell), mRootFrame(nullptr) { MOZ_ASSERT(mPresShell, "need a pres shell"); @@ -64,7 +67,7 @@ mozilla::FrameChildListID aListID, nsIFrame* aPrevFrame, nsFrameList&& aFrameList); - void RemoveFrame(mozilla::FrameChildListID aListID, nsIFrame* aOldFrame); + void RemoveFrame(DestroyContext&, mozilla::FrameChildListID, nsIFrame*); /* * Capture/restore frame state for the frame subtree rooted at aFrame. diff -Nru firefox-esr-115.3.1esr+build1/layout/base/nsPresContext.cpp firefox-esr-115.4.0esr+build1/layout/base/nsPresContext.cpp --- firefox-esr-115.3.1esr+build1/layout/base/nsPresContext.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/base/nsPresContext.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -249,6 +249,7 @@ mFramesReflowed(0), mInterruptChecksToSkip(0), mNextFrameRateMultiplier(0), + mMeasuredTicksSinceLoading(0), mViewportScrollStyles(StyleOverflow::Auto, StyleOverflow::Auto), // mImageAnimationMode is initialised below, in constructor body mImageAnimationModePref(imgIContainer::kNormalAnimMode), @@ -285,6 +286,7 @@ mHadFirstContentfulPaint(false), mHadNonTickContentfulPaint(false), mHadContentfulPaintComposite(false), + mUserInputEventsAllowed(false), #ifdef DEBUG mInitialized(false), #endif @@ -1219,6 +1221,83 @@ return pc->IsRoot() ? static_cast(pc) : nullptr; } +bool nsPresContext::UserInputEventsAllowed() { + MOZ_ASSERT(IsRoot()); + if (mUserInputEventsAllowed) { + return true; + } + + // Special document + if (Document()->IsInitialDocument()) { + return true; + } + + if (mRefreshDriver->IsThrottled()) { + MOZ_ASSERT(!mPresShell->IsVisible()); + // This implies that the BC is not visibile and users can't + // interact with it, so we are okay with handling user inputs here. + return true; + } + + if (mMeasuredTicksSinceLoading < + StaticPrefs::dom_input_events_security_minNumTicks()) { + return false; + } + + if (!StaticPrefs::dom_input_events_security_minTimeElapsedInMS()) { + return true; + } + + dom::Document* doc = Document(); + + MOZ_ASSERT_IF(StaticPrefs::dom_input_events_security_minNumTicks(), + doc->GetReadyStateEnum() >= Document::READYSTATE_LOADING); + + TimeStamp loadingOrRestoredFromBFCacheTime = + doc->GetLoadingOrRestoredFromBFCacheTimeStamp(); + MOZ_ASSERT(!loadingOrRestoredFromBFCacheTime.IsNull()); + + TimeDuration elapsed = TimeStamp::Now() - loadingOrRestoredFromBFCacheTime; + if (elapsed.ToMilliseconds() >= + StaticPrefs::dom_input_events_security_minTimeElapsedInMS()) { + mUserInputEventsAllowed = true; + return true; + } + + return false; +} + +void nsPresContext::MaybeIncreaseMeasuredTicksSinceLoading() { + MOZ_ASSERT(IsRoot()); + if (mMeasuredTicksSinceLoading >= + StaticPrefs::dom_input_events_security_minNumTicks()) { + return; + } + + // We consider READYSTATE_LOADING is the point when the page + // becomes interactive + if (Document()->GetReadyStateEnum() >= Document::READYSTATE_LOADING || + Document()->IsInitialDocument()) { + ++mMeasuredTicksSinceLoading; + } + + if (mMeasuredTicksSinceLoading < + StaticPrefs::dom_input_events_security_minNumTicks()) { + // Here we are forcing refresh driver to run because we can't always + // guarantee refresh driver will run enough times to meet the minNumTicks + // requirement. i.e. about:blank. + if (!RefreshDriver()->HasPendingTick()) { + RefreshDriver()->InitializeTimer(); + } + } +} + +bool nsPresContext::NeedsMoreTicksForUserInput() const { + MOZ_ASSERT(IsRoot()); + return mMeasuredTicksSinceLoading < + StaticPrefs::dom_input_events_security_minNumTicks(); +} + // Helper function for setting Anim Mode on image static void SetImgAnimModeOnImgReq(imgIRequest* aImgReq, uint16_t aMode) { if (aImgReq) { diff -Nru firefox-esr-115.3.1esr+build1/layout/base/nsPresContext.h firefox-esr-115.4.0esr+build1/layout/base/nsPresContext.h --- firefox-esr-115.3.1esr+build1/layout/base/nsPresContext.h 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/base/nsPresContext.h 2023-10-17 05:45:50.000000000 +0000 @@ -257,7 +257,7 @@ */ nsRootPresContext* GetRootPresContext() const; - virtual bool IsRoot() { return false; } + virtual bool IsRoot() const { return false; } mozilla::dom::Document* Document() const { #ifdef DEBUG @@ -512,6 +512,18 @@ nsDeviceContext* DeviceContext() const { return mDeviceContext; } mozilla::EventStateManager* EventStateManager() { return mEventManager; } + bool UserInputEventsAllowed(); + + void MaybeIncreaseMeasuredTicksSinceLoading(); + + bool NeedsMoreTicksForUserInput() const; + + void ResetUserInputEventsAllowed() { + MOZ_ASSERT(IsRoot()); + mMeasuredTicksSinceLoading = 0; + mUserInputEventsAllowed = false; + } + // Get the text zoom factor in use. float TextZoom() const { return mTextZoom; } @@ -1253,6 +1265,8 @@ // During page load we use slower frame rate. uint32_t mNextFrameRateMultiplier; + uint32_t mMeasuredTicksSinceLoading; + nsTArray> mManagedPostRefreshObservers; @@ -1346,6 +1360,7 @@ // Has NotifyDidPaintForSubtree been called for a contentful paint? unsigned mHadContentfulPaintComposite : 1; + unsigned mUserInputEventsAllowed : 1; #ifdef DEBUG unsigned mInitialized : 1; #endif @@ -1378,7 +1393,7 @@ class nsRootPresContext final : public nsPresContext { public: nsRootPresContext(mozilla::dom::Document* aDocument, nsPresContextType aType); - virtual bool IsRoot() override { return true; } + virtual bool IsRoot() const override { return true; } /** * Add a runnable that will get called before the next paint. They will get diff -Nru firefox-esr-115.3.1esr+build1/layout/base/nsRefreshDriver.cpp firefox-esr-115.4.0esr+build1/layout/base/nsRefreshDriver.cpp --- firefox-esr-115.3.1esr+build1/layout/base/nsRefreshDriver.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/base/nsRefreshDriver.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -1950,6 +1950,10 @@ if (!mVisualViewportScrollEvents.IsEmpty()) { reasons |= TickReasons::eHasVisualViewportScrollEvents; } + if (mPresContext && mPresContext->IsRoot() && + mPresContext->NeedsMoreTicksForUserInput()) { + reasons |= TickReasons::eRootNeedsMoreTicksForUserInput; + } return reasons; } @@ -1983,6 +1987,9 @@ if (aReasons & TickReasons::eHasVisualViewportScrollEvents) { aStr.AppendLiteral(" HasVisualViewportScrollEvents"); } + if (aReasons & TickReasons::eRootNeedsMoreTicksForUserInput) { + aStr.AppendLiteral(" RootNeedsMoreTicksForUserInput"); + } } bool nsRefreshDriver:: @@ -2154,6 +2161,12 @@ } } +void nsRefreshDriver::MaybeIncreaseMeasuredTicksSinceLoading() { + if (mPresContext && mPresContext->IsRoot()) { + mPresContext->MaybeIncreaseMeasuredTicksSinceLoading(); + } +} + void nsRefreshDriver::CancelFlushAutoFocus(Document* aDocument) { mAutoFocusFlushDocuments.RemoveElement(aDocument); } @@ -2604,6 +2617,7 @@ DispatchAnimationEvents(); RunFullscreenSteps(); RunFrameRequestCallbacks(aNowTime); + MaybeIncreaseMeasuredTicksSinceLoading(); if (mPresContext && mPresContext->GetPresShell()) { AutoTArray observers; diff -Nru firefox-esr-115.3.1esr+build1/layout/base/nsRefreshDriver.h firefox-esr-115.4.0esr+build1/layout/base/nsRefreshDriver.h --- firefox-esr-115.3.1esr+build1/layout/base/nsRefreshDriver.h 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/base/nsRefreshDriver.h 2023-10-17 05:45:50.000000000 +0000 @@ -440,6 +440,7 @@ eHasVisualViewportResizeEvents = 1 << 4, eHasScrollEvents = 1 << 5, eHasVisualViewportScrollEvents = 1 << 6, + eRootNeedsMoreTicksForUserInput = 1 << 9, }; void AddForceNotifyContentfulPaintPresContext(nsPresContext* aPresContext); @@ -486,6 +487,7 @@ void RunFrameRequestCallbacks(mozilla::TimeStamp aNowTime); void UpdateIntersectionObservations(mozilla::TimeStamp aNowTime); void UpdateRelevancyOfContentVisibilityAutoFrames(); + void MaybeIncreaseMeasuredTicksSinceLoading(); enum class IsExtraTick { No, diff -Nru firefox-esr-115.3.1esr+build1/layout/base/PresShell.cpp firefox-esr-115.4.0esr+build1/layout/base/PresShell.cpp --- firefox-esr-115.3.1esr+build1/layout/base/PresShell.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/base/PresShell.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -1983,6 +1983,27 @@ return true; } +bool PresShell::CanHandleUserInputEvents(WidgetGUIEvent* aGUIEvent) { + if (XRE_IsParentProcess()) { + return true; + } + + if (aGUIEvent->mFlags.mIsSynthesizedForTests && + !StaticPrefs::dom_input_events_security_isUserInputHandlingDelayTest()) { + return true; + } + + if (!aGUIEvent->IsUserAction()) { + return true; + } + + if (nsPresContext* rootPresContext = mPresContext->GetRootPresContext()) { + return rootPresContext->UserInputEventsAllowed(); + } + + return true; +} + void PresShell::AddResizeEventFlushObserverIfNeeded() { if (!mIsDestroying && !mResizeEventPending && MOZ_LIKELY(!mDocument->GetBFCacheEntry())) { @@ -6864,6 +6885,17 @@ aGUIEvent->AsMouseEvent()->mReason == WidgetMouseEvent::eSynthesized) { return NS_OK; } + + // Here we are granting some delays to ensure that user input events are + // created while the page content may not be visible to the user are not + // processed. + // The main purpose of this is to avoid user inputs are handled in the + // new document where as the user inputs were originally targeting some + // content in the old document. + if (!CanHandleUserInputEvents(aGUIEvent)) { + return NS_OK; + } + EventHandler eventHandler(*this); return eventHandler.HandleEvent(aFrameForPresShell, aGUIEvent, aDontRetargetEvents, aEventStatus); @@ -9285,6 +9317,10 @@ if (presContext->RefreshDriver()->GetPresContext() == presContext) { presContext->RefreshDriver()->Freeze(); } + + if (nsPresContext* rootPresContext = presContext->GetRootPresContext()) { + rootPresContext->ResetUserInputEventsAllowed(); + } } mFrozen = true; @@ -9343,6 +9379,16 @@ UpdateImageLockingState(); UnsuppressPainting(); + + // In case the above UnsuppressPainting call didn't start the + // refresh driver, we manually start the refresh driver to + // ensure nsPresContext::MaybeIncreaseMeasuredTicksSinceLoading + // can be called for user input events handling. + if (presContext && presContext->IsRoot()) { + if (!presContext->RefreshDriver()->HasPendingTick()) { + presContext->RefreshDriver()->InitializeTimer(); + } + } } //-------------------------------------------------------- diff -Nru firefox-esr-115.3.1esr+build1/layout/base/PresShell.h firefox-esr-115.4.0esr+build1/layout/base/PresShell.h --- firefox-esr-115.3.1esr+build1/layout/base/PresShell.h 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/base/PresShell.h 2023-10-17 05:45:50.000000000 +0000 @@ -385,6 +385,8 @@ */ bool SimpleResizeReflow(nscoord aWidth, nscoord aHeight); + bool CanHandleUserInputEvents(WidgetGUIEvent* aGUIEvent); + public: /** * Updates pending layout, assuming reasonable (up-to-date, or mid-update for diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsColorControlFrame.cpp firefox-esr-115.4.0esr+build1/layout/forms/nsColorControlFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/forms/nsColorControlFrame.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsColorControlFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -38,10 +38,9 @@ NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator) NS_QUERYFRAME_TAIL_INHERITING(nsHTMLButtonControlFrame) -void nsColorControlFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { - aPostDestroyData.AddAnonymousContent(mColorContent.forget()); - nsHTMLButtonControlFrame::DestroyFrom(aDestructRoot, aPostDestroyData); +void nsColorControlFrame::Destroy(DestroyContext& aContext) { + aContext.AddAnonymousContent(mColorContent.forget()); + nsHTMLButtonControlFrame::Destroy(aContext); } #ifdef DEBUG_FRAME_DUMP diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsColorControlFrame.h firefox-esr-115.4.0esr+build1/layout/forms/nsColorControlFrame.h --- firefox-esr-115.3.1esr+build1/layout/forms/nsColorControlFrame.h 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsColorControlFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -27,8 +27,7 @@ friend nsIFrame* NS_NewColorControlFrame(mozilla::PresShell* aPresShell, ComputedStyle* aStyle); - virtual void DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) override; + void Destroy(DestroyContext&) override; NS_DECL_QUERYFRAME NS_DECL_FRAMEARENA_HELPERS(nsColorControlFrame) diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsComboboxControlFrame.cpp firefox-esr-115.4.0esr+build1/layout/forms/nsComboboxControlFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/forms/nsComboboxControlFrame.cpp 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsComboboxControlFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -845,17 +845,16 @@ return mDisplayFrame; } -void nsComboboxControlFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { +void nsComboboxControlFrame::Destroy(DestroyContext& aContext) { // Revoke any pending RedisplayTextEvent mRedisplayTextEvent.Revoke(); mEventListener->Detach(); // Cleanup frames in popup child list - aPostDestroyData.AddAnonymousContent(mDisplayContent.forget()); - aPostDestroyData.AddAnonymousContent(mButtonContent.forget()); - nsBlockFrame::DestroyFrom(aDestructRoot, aPostDestroyData); + aContext.AddAnonymousContent(mDisplayContent.forget()); + aContext.AddAnonymousContent(mButtonContent.forget()); + nsBlockFrame::Destroy(aContext); } const nsFrameList& nsComboboxControlFrame::GetChildList( diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsComboboxControlFrame.h firefox-esr-115.4.0esr+build1/layout/forms/nsComboboxControlFrame.h --- firefox-esr-115.3.1esr+build1/layout/forms/nsComboboxControlFrame.h 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsComboboxControlFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -105,8 +105,8 @@ #ifdef DEBUG_FRAME_DUMP nsresult GetFrameName(nsAString& aResult) const final; #endif - void DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) final; + void Destroy(DestroyContext&) final; + void SetInitialChildList(ChildListID aListID, nsFrameList&& aChildList) final; const nsFrameList& GetChildList(ChildListID aListID) const final; void GetChildLists(nsTArray* aLists) const final; diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsFieldSetFrame.cpp firefox-esr-115.4.0esr+build1/layout/forms/nsFieldSetFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/forms/nsFieldSetFrame.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsFieldSetFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -109,7 +109,7 @@ } MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayFieldSetBorder) - virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override; + void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override; bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, @@ -824,7 +824,7 @@ } #ifdef DEBUG -void nsFieldSetFrame::RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) { +void nsFieldSetFrame::RemoveFrame(DestroyContext&, ChildListID, nsIFrame*) { MOZ_CRASH("nsFieldSetFrame::RemoveFrame not supported"); } #endif @@ -894,7 +894,9 @@ if (aStatus.IsFullyComplete()) { if (nif) { // NOTE: we want to avoid our DEBUG version of RemoveFrame above. - nsContainerFrame::RemoveFrame(FrameChildListID::NoReflowPrincipal, nif); + DestroyContext context(PresShell()); + nsContainerFrame::RemoveFrame(context, + FrameChildListID::NoReflowPrincipal, nif); MOZ_ASSERT(!aChild->GetNextInFlow()); } } else { diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsFieldSetFrame.h firefox-esr-115.4.0esr+build1/layout/forms/nsFieldSetFrame.h --- firefox-esr-115.3.1esr+build1/layout/forms/nsFieldSetFrame.h 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsFieldSetFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -23,18 +23,18 @@ nscoord GetIntrinsicISize(gfxContext* aRenderingContext, mozilla::IntrinsicISizeType); - virtual nscoord GetMinISize(gfxContext* aRenderingContext) override; - virtual nscoord GetPrefISize(gfxContext* aRenderingContext) override; + nscoord GetMinISize(gfxContext* aRenderingContext) override; + nscoord GetPrefISize(gfxContext* aRenderingContext) override; /** * The area to paint box-shadows around. It's the border rect except * when there's a we offset the y-position to the center of it. */ - virtual nsRect VisualBorderRectRelativeToSelf() const override; + nsRect VisualBorderRectRelativeToSelf() const override; - virtual void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, - const ReflowInput& aReflowInput, - nsReflowStatus& aStatus) override; + void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, + const ReflowInput& aReflowInput, + nsReflowStatus& aStatus) override; nscoord SynthesizeFallbackBaseline( mozilla::WritingMode aWM, @@ -44,8 +44,8 @@ mozilla::WritingMode aWM, BaselineSharingGroup aBaselineGroup, BaselineExportContext aExportContext) const override; - virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, - const nsDisplayListSet& aLists) override; + void BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsDisplayListSet& aLists) override; ImgDrawResult PaintBorder(nsDisplayListBuilder* aBuilder, gfxContext& aRenderingContext, nsPoint aPt, @@ -58,10 +58,11 @@ const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) override; #ifdef DEBUG - virtual void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) override; + void RemoveFrame(DestroyContext&, ChildListID aListID, + nsIFrame* aOldFrame) override; #endif - virtual bool IsFrameOfType(uint32_t aFlags) const override { + bool IsFrameOfType(uint32_t aFlags) const override { return nsContainerFrame::IsFrameOfType( aFlags & ~nsIFrame::eCanContainOverflowContainers); } diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsFileControlFrame.cpp firefox-esr-115.4.0esr+build1/layout/forms/nsFileControlFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/forms/nsFileControlFrame.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsFileControlFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -59,8 +59,7 @@ mMouseListener = new DnDListener(this); } -void nsFileControlFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { +void nsFileControlFrame::Destroy(DestroyContext& aContext) { NS_ENSURE_TRUE_VOID(mContent); // Remove the events. @@ -69,11 +68,11 @@ mContent->RemoveSystemEventListener(u"dragover"_ns, mMouseListener, false); } - aPostDestroyData.AddAnonymousContent(mTextContent.forget()); - aPostDestroyData.AddAnonymousContent(mBrowseFilesOrDirs.forget()); + aContext.AddAnonymousContent(mTextContent.forget()); + aContext.AddAnonymousContent(mBrowseFilesOrDirs.forget()); mMouseListener->ForgetFrame(); - nsBlockFrame::DestroyFrom(aDestructRoot, aPostDestroyData); + nsBlockFrame::Destroy(aContext); } static already_AddRefed MakeAnonButton( diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsFileControlFrame.h firefox-esr-115.4.0esr+build1/layout/forms/nsFileControlFrame.h --- firefox-esr-115.3.1esr+build1/layout/forms/nsFileControlFrame.h 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsFileControlFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -39,8 +39,7 @@ nsresult SetFormProperty(nsAtom* aName, const nsAString& aValue) override; void SetFocus(bool aOn, bool aRepaint) override; - void DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) override; + void Destroy(DestroyContext&) override; #ifdef DEBUG_FRAME_DUMP nsresult GetFrameName(nsAString& aResult) const override; diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsGfxButtonControlFrame.cpp firefox-esr-115.4.0esr+build1/layout/forms/nsGfxButtonControlFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/forms/nsGfxButtonControlFrame.cpp 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsGfxButtonControlFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -26,10 +26,9 @@ NS_IMPL_FRAMEARENA_HELPERS(nsGfxButtonControlFrame) -void nsGfxButtonControlFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { - aPostDestroyData.AddAnonymousContent(mTextContent.forget()); - nsHTMLButtonControlFrame::DestroyFrom(aDestructRoot, aPostDestroyData); +void nsGfxButtonControlFrame::Destroy(DestroyContext& aContext) { + aContext.AddAnonymousContent(mTextContent.forget()); + nsHTMLButtonControlFrame::Destroy(aContext); } #ifdef DEBUG_FRAME_DUMP diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsGfxButtonControlFrame.h firefox-esr-115.4.0esr+build1/layout/forms/nsGfxButtonControlFrame.h --- firefox-esr-115.3.1esr+build1/layout/forms/nsGfxButtonControlFrame.h 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsGfxButtonControlFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -27,8 +27,7 @@ explicit nsGfxButtonControlFrame(ComputedStyle* aStyle, nsPresContext* aPresContext); - virtual void DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) override; + void Destroy(DestroyContext&) override; virtual nsresult HandleEvent(nsPresContext* aPresContext, mozilla::WidgetGUIEvent* aEvent, diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsHTMLButtonControlFrame.cpp firefox-esr-115.4.0esr+build1/layout/forms/nsHTMLButtonControlFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/forms/nsHTMLButtonControlFrame.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsHTMLButtonControlFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -391,8 +391,8 @@ MOZ_CRASH("unsupported operation"); } -void nsHTMLButtonControlFrame::RemoveFrame(ChildListID aListID, - nsIFrame* aOldFrame) { +void nsHTMLButtonControlFrame::RemoveFrame(DestroyContext&, ChildListID, + nsIFrame*) { MOZ_CRASH("unsupported operation"); } #endif diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsHTMLButtonControlFrame.h firefox-esr-115.4.0esr+build1/layout/forms/nsHTMLButtonControlFrame.h --- firefox-esr-115.3.1esr+build1/layout/forms/nsHTMLButtonControlFrame.h 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsHTMLButtonControlFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -27,27 +27,27 @@ NS_DECL_QUERYFRAME NS_DECL_FRAMEARENA_HELPERS(nsHTMLButtonControlFrame) - virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, - const nsDisplayListSet& aLists) override; + void BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsDisplayListSet& aLists) override; - virtual nscoord GetMinISize(gfxContext* aRenderingContext) override; + nscoord GetMinISize(gfxContext* aRenderingContext) override; - virtual nscoord GetPrefISize(gfxContext* aRenderingContext) override; + nscoord GetPrefISize(gfxContext* aRenderingContext) override; - virtual void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, - const ReflowInput& aReflowInput, - nsReflowStatus& aStatus) override; + void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, + const ReflowInput& aReflowInput, + nsReflowStatus& aStatus) override; Maybe GetNaturalBaselineBOffset( mozilla::WritingMode aWM, BaselineSharingGroup aBaselineGroup, BaselineExportContext aExportContext) const override; - virtual nsresult HandleEvent(nsPresContext* aPresContext, - mozilla::WidgetGUIEvent* aEvent, - nsEventStatus* aEventStatus) override; + nsresult HandleEvent(nsPresContext* aPresContext, + mozilla::WidgetGUIEvent* aEvent, + nsEventStatus* aEventStatus) override; - virtual void Init(nsIContent* aContent, nsContainerFrame* aParent, - nsIFrame* aPrevInFlow) override; + void Init(nsIContent* aContent, nsContainerFrame* aParent, + nsIFrame* aPrevInFlow) override; ComputedStyle* GetAdditionalComputedStyle(int32_t aIndex) const override; void SetAdditionalComputedStyle(int32_t aIndex, @@ -58,30 +58,29 @@ void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame, const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) override; - virtual void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) override; + void RemoveFrame(DestroyContext&, ChildListID, nsIFrame*) override; #endif #ifdef ACCESSIBILITY - virtual mozilla::a11y::AccType AccessibleType() override; + mozilla::a11y::AccType AccessibleType() override; #endif #ifdef DEBUG_FRAME_DUMP - virtual nsresult GetFrameName(nsAString& aResult) const override { + nsresult GetFrameName(nsAString& aResult) const override { return MakeFrameName(u"HTMLButtonControl"_ns, aResult); } #endif // nsIFormControlFrame void SetFocus(bool aOn, bool aRepaint) override; - virtual nsresult SetFormProperty(nsAtom* aName, - const nsAString& aValue) override; + nsresult SetFormProperty(nsAtom* aName, const nsAString& aValue) override; // Inserted child content gets its frames parented by our child block - virtual nsContainerFrame* GetContentInsertionFrame() override { + nsContainerFrame* GetContentInsertionFrame() override { return PrincipalChildList().FirstChild()->GetContentInsertionFrame(); } - virtual bool IsFrameOfType(uint32_t aFlags) const override { + bool IsFrameOfType(uint32_t aFlags) const override { return nsContainerFrame::IsFrameOfType( aFlags & ~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock)); } diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsListControlFrame.cpp firefox-esr-115.4.0esr+build1/layout/forms/nsListControlFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/forms/nsListControlFrame.cpp 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsListControlFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -77,8 +77,7 @@ return Nothing{}; } // for Bug 47302 (remove this comment later) -void nsListControlFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { +void nsListControlFrame::Destroy(DestroyContext& aContext) { // get the receiver interface from the browser button's content node NS_ENSURE_TRUE_VOID(mContent); @@ -86,7 +85,7 @@ // event listener can outlive the frame. mEventListener->Detach(); - nsHTMLScrollFrame::DestroyFrom(aDestructRoot, aPostDestroyData); + nsHTMLScrollFrame::Destroy(aContext); } void nsListControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsListControlFrame.h firefox-esr-115.4.0esr+build1/layout/forms/nsListControlFrame.h --- firefox-esr-115.3.1esr+build1/layout/forms/nsListControlFrame.h 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsListControlFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -77,8 +77,7 @@ void DidReflow(nsPresContext* aPresContext, const ReflowInput* aReflowInput) final; - void DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) final; + void Destroy(DestroyContext&) override; void BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) final; diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsMeterFrame.cpp firefox-esr-115.4.0esr+build1/layout/forms/nsMeterFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/forms/nsMeterFrame.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsMeterFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -38,13 +38,12 @@ nsMeterFrame::~nsMeterFrame() = default; -void nsMeterFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { +void nsMeterFrame::Destroy(DestroyContext& aContext) { NS_ASSERTION(!GetPrevContinuation(), "nsMeterFrame should not have continuations; if it does we " "need to call RegUnregAccessKey only for the first."); - aPostDestroyData.AddAnonymousContent(mBarDiv.forget()); - nsContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData); + aContext.AddAnonymousContent(mBarDiv.forget()); + nsContainerFrame::Destroy(aContext); } nsresult nsMeterFrame::CreateAnonymousContent( diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsMeterFrame.h firefox-esr-115.4.0esr+build1/layout/forms/nsMeterFrame.h --- firefox-esr-115.3.1esr+build1/layout/forms/nsMeterFrame.h 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsMeterFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -26,8 +26,7 @@ explicit nsMeterFrame(ComputedStyle* aStyle, nsPresContext* aPresContext); virtual ~nsMeterFrame(); - virtual void DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) override; + void Destroy(DestroyContext&) override; virtual void Reflow(nsPresContext* aCX, ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsNumberControlFrame.cpp firefox-esr-115.4.0esr+build1/layout/forms/nsNumberControlFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/forms/nsNumberControlFrame.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsNumberControlFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -41,10 +41,9 @@ nsPresContext* aPresContext) : nsTextControlFrame(aStyle, aPresContext, kClassID) {} -void nsNumberControlFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { - aPostDestroyData.AddAnonymousContent(mSpinBox.forget()); - nsTextControlFrame::DestroyFrom(aDestructRoot, aPostDestroyData); +void nsNumberControlFrame::Destroy(DestroyContext& aContext) { + aContext.AddAnonymousContent(mSpinBox.forget()); + nsTextControlFrame::Destroy(aContext); } nsresult nsNumberControlFrame::CreateAnonymousContent( diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsNumberControlFrame.h firefox-esr-115.4.0esr+build1/layout/forms/nsNumberControlFrame.h --- firefox-esr-115.3.1esr+build1/layout/forms/nsNumberControlFrame.h 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsNumberControlFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -49,7 +49,7 @@ NS_DECL_QUERYFRAME NS_DECL_FRAMEARENA_HELPERS(nsNumberControlFrame) - void DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData&) override; + void Destroy(DestroyContext&) override; #ifdef ACCESSIBILITY mozilla::a11y::AccType AccessibleType() override; diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsProgressFrame.cpp firefox-esr-115.4.0esr+build1/layout/forms/nsProgressFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/forms/nsProgressFrame.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsProgressFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -37,13 +37,12 @@ nsProgressFrame::~nsProgressFrame() = default; -void nsProgressFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { +void nsProgressFrame::Destroy(DestroyContext& aContext) { NS_ASSERTION(!GetPrevContinuation(), "nsProgressFrame should not have continuations; if it does we " "need to call RegUnregAccessKey only for the first."); - aPostDestroyData.AddAnonymousContent(mBarDiv.forget()); - nsContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData); + aContext.AddAnonymousContent(mBarDiv.forget()); + nsContainerFrame::Destroy(aContext); } nsresult nsProgressFrame::CreateAnonymousContent( diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsProgressFrame.h firefox-esr-115.4.0esr+build1/layout/forms/nsProgressFrame.h --- firefox-esr-115.3.1esr+build1/layout/forms/nsProgressFrame.h 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsProgressFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -28,8 +28,7 @@ explicit nsProgressFrame(ComputedStyle* aStyle, nsPresContext* aPresContext); virtual ~nsProgressFrame(); - virtual void DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) override; + void Destroy(DestroyContext&) override; virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) override; diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsRangeFrame.cpp firefox-esr-115.4.0esr+build1/layout/forms/nsRangeFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/forms/nsRangeFrame.cpp 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsRangeFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -69,8 +69,7 @@ NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator) NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) -void nsRangeFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { +void nsRangeFrame::Destroy(DestroyContext& aContext) { NS_ASSERTION(!GetPrevContinuation() && !GetNextContinuation(), "nsRangeFrame should not have continuations; if it does we " "need to call RegUnregAccessKey only for the first."); @@ -78,10 +77,10 @@ if (mListMutationObserver) { mListMutationObserver->Detach(); } - aPostDestroyData.AddAnonymousContent(mTrackDiv.forget()); - aPostDestroyData.AddAnonymousContent(mProgressDiv.forget()); - aPostDestroyData.AddAnonymousContent(mThumbDiv.forget()); - nsContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData); + aContext.AddAnonymousContent(mTrackDiv.forget()); + aContext.AddAnonymousContent(mProgressDiv.forget()); + aContext.AddAnonymousContent(mThumbDiv.forget()); + nsContainerFrame::Destroy(aContext); } nsresult nsRangeFrame::MakeAnonymousDiv(Element** aResult, diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsRangeFrame.h firefox-esr-115.4.0esr+build1/layout/forms/nsRangeFrame.h --- firefox-esr-115.3.1esr+build1/layout/forms/nsRangeFrame.h 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsRangeFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -48,8 +48,7 @@ NS_DECL_FRAMEARENA_HELPERS(nsRangeFrame) // nsIFrame overrides - virtual void DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) override; + void Destroy(DestroyContext&) override; void BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) override; diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsSearchControlFrame.cpp firefox-esr-115.4.0esr+build1/layout/forms/nsSearchControlFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/forms/nsSearchControlFrame.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsSearchControlFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -39,10 +39,9 @@ nsPresContext* aPresContext) : nsTextControlFrame(aStyle, aPresContext, kClassID) {} -void nsSearchControlFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { - aPostDestroyData.AddAnonymousContent(mClearButton.forget()); - nsTextControlFrame::DestroyFrom(aDestructRoot, aPostDestroyData); +void nsSearchControlFrame::Destroy(DestroyContext& aContext) { + aContext.AddAnonymousContent(mClearButton.forget()); + nsTextControlFrame::Destroy(aContext); } nsresult nsSearchControlFrame::CreateAnonymousContent( diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsSearchControlFrame.h firefox-esr-115.4.0esr+build1/layout/forms/nsSearchControlFrame.h --- firefox-esr-115.3.1esr+build1/layout/forms/nsSearchControlFrame.h 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsSearchControlFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -40,8 +40,7 @@ NS_DECL_QUERYFRAME NS_DECL_FRAMEARENA_HELPERS(nsSearchControlFrame) - void DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) override; + void Destroy(DestroyContext&) override; // nsIAnonymousContentCreator nsresult CreateAnonymousContent(nsTArray& aElements) override; diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsTextControlFrame.cpp firefox-esr-115.4.0esr+build1/layout/forms/nsTextControlFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/forms/nsTextControlFrame.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsTextControlFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -126,8 +126,7 @@ return do_QueryFrame(mRootNode->GetPrimaryFrame()); } -void nsTextControlFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { +void nsTextControlFrame::Destroy(DestroyContext& aContext) { RemoveProperty(TextControlInitializer()); // Unbind the text editor state object from the frame. The editor will live @@ -166,12 +165,12 @@ // If we're a subclass like nsNumberControlFrame, then it owns the root of the // anonymous subtree where mRootNode is. - aPostDestroyData.AddAnonymousContent(mRootNode.forget()); - aPostDestroyData.AddAnonymousContent(mPlaceholderDiv.forget()); - aPostDestroyData.AddAnonymousContent(mPreviewDiv.forget()); - aPostDestroyData.AddAnonymousContent(mRevealButton.forget()); + aContext.AddAnonymousContent(mRootNode.forget()); + aContext.AddAnonymousContent(mPlaceholderDiv.forget()); + aContext.AddAnonymousContent(mPreviewDiv.forget()); + aContext.AddAnonymousContent(mRevealButton.forget()); - nsContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData); + nsContainerFrame::Destroy(aContext); } LogicalSize nsTextControlFrame::CalcIntrinsicSize( diff -Nru firefox-esr-115.3.1esr+build1/layout/forms/nsTextControlFrame.h firefox-esr-115.4.0esr+build1/layout/forms/nsTextControlFrame.h --- firefox-esr-115.3.1esr+build1/layout/forms/nsTextControlFrame.h 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/forms/nsTextControlFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -49,16 +49,14 @@ virtual ~nsTextControlFrame(); /** - * DestroyFrom() causes preparing to destroy editor and that may cause - * running selection listeners of specllchecker selection and document - * state listeners. Not sure whether the former does something or not, - * but nobody should run content script. The latter is currently only - * FinderHighlighter to clean up its fields at destruction. Thus, the - * latter won't run content script too. Therefore, this won't run - * unsafe script. + * Destroy() causes preparing to destroy editor and that may cause running + * selection listeners of spellchecker selection and document state listeners. + * Not sure whether the former does something or not, but nobody should run + * content script. The latter is currently only FinderHighlighter to clean up + * its fields at destruction. Thus, the latter won't run content script too. + * Therefore, this won't run unsafe script. */ - MOZ_CAN_RUN_SCRIPT_BOUNDARY void DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData&) override; + MOZ_CAN_RUN_SCRIPT_BOUNDARY void Destroy(DestroyContext&) override; nsIScrollableFrame* GetScrollTargetFrame() const override; diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/ColumnSetWrapperFrame.cpp firefox-esr-115.4.0esr+build1/layout/generic/ColumnSetWrapperFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/ColumnSetWrapperFrame.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/ColumnSetWrapperFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -136,10 +136,11 @@ std::move(aFrameList)); } -void ColumnSetWrapperFrame::RemoveFrame(ChildListID aListID, +void ColumnSetWrapperFrame::RemoveFrame(DestroyContext& aContext, + ChildListID aListID, nsIFrame* aOldFrame) { MOZ_ASSERT_UNREACHABLE("Unsupported operation!"); - nsBlockFrame::RemoveFrame(aListID, aOldFrame); + nsBlockFrame::RemoveFrame(aContext, aListID, aOldFrame); } void ColumnSetWrapperFrame::MarkIntrinsicISizesDirty() { diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/ColumnSetWrapperFrame.h firefox-esr-115.4.0esr+build1/layout/generic/ColumnSetWrapperFrame.h --- firefox-esr-115.3.1esr+build1/layout/generic/ColumnSetWrapperFrame.h 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/ColumnSetWrapperFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -51,7 +51,7 @@ const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) override; - void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) override; + void RemoveFrame(DestroyContext&, ChildListID, nsIFrame*) override; void MarkIntrinsicISizesDirty() override; diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/MiddleCroppingBlockFrame.cpp firefox-esr-115.4.0esr+build1/layout/generic/MiddleCroppingBlockFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/MiddleCroppingBlockFrame.cpp 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/MiddleCroppingBlockFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -198,10 +198,9 @@ aContent.AppendElement(mTextNode); } -void MiddleCroppingBlockFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { - aPostDestroyData.AddAnonymousContent(mTextNode.forget()); - nsBlockFrame::DestroyFrom(aDestructRoot, aPostDestroyData); +void MiddleCroppingBlockFrame::Destroy(DestroyContext& aContext) { + aContext.AddAnonymousContent(mTextNode.forget()); + nsBlockFrame::Destroy(aContext); } } // namespace mozilla diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/MiddleCroppingBlockFrame.h firefox-esr-115.4.0esr+build1/layout/generic/MiddleCroppingBlockFrame.h --- firefox-esr-115.3.1esr+build1/layout/generic/MiddleCroppingBlockFrame.h 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/MiddleCroppingBlockFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -52,8 +52,7 @@ * Updates the displayed value by using aValue. */ void UpdateDisplayedValue(const nsAString& aValue, bool aNotify); - void DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) override; + void Destroy(DestroyContext&) override; RefPtr mTextNode; }; diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsAbsoluteContainingBlock.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsAbsoluteContainingBlock.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsAbsoluteContainingBlock.cpp 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsAbsoluteContainingBlock.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -93,16 +93,14 @@ aDelegatingFrame, IntrinsicDirty::None, NS_FRAME_HAS_DIRTY_CHILDREN); } -void nsAbsoluteContainingBlock::RemoveFrame(nsIFrame* aDelegatingFrame, +void nsAbsoluteContainingBlock::RemoveFrame(FrameDestroyContext& aContext, FrameChildListID aListID, nsIFrame* aOldFrame) { NS_ASSERTION(mChildListID == aListID, "unexpected child list"); - nsIFrame* nif = aOldFrame->GetNextInFlow(); - if (nif) { - nif->GetParent()->DeleteNextInFlowChild(nif, false); + if (nsIFrame* nif = aOldFrame->GetNextInFlow()) { + nif->GetParent()->DeleteNextInFlowChild(aContext, nif, false); } - - mAbsoluteFrames.DestroyFrame(aOldFrame); + mAbsoluteFrames.DestroyFrame(aContext, aOldFrame); } static void MaybeMarkAncestorsAsHavingDescendantDependentOnItsStaticPos( @@ -238,12 +236,11 @@ // to keep continuations within an nsAbsoluteContainingBlock eventually. tracker.Insert(nextFrame, kidStatus); reflowStatus.MergeCompletionStatusFrom(kidStatus); - } else { + } else if (nextFrame) { // Delete any continuations - if (nextFrame) { - nsOverflowContinuationTracker::AutoFinish fini(&tracker, kidFrame); - nextFrame->GetParent()->DeleteNextInFlowChild(nextFrame, true); - } + nsOverflowContinuationTracker::AutoFinish fini(&tracker, kidFrame); + FrameDestroyContext context(aPresContext->PresShell()); + nextFrame->GetParent()->DeleteNextInFlowChild(context, nextFrame, true); } } else { tracker.Skip(kidFrame, reflowStatus); @@ -396,10 +393,8 @@ return false; } -void nsAbsoluteContainingBlock::DestroyFrames( - nsIFrame* aDelegatingFrame, nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { - mAbsoluteFrames.DestroyFramesFrom(aDestructRoot, aPostDestroyData); +void nsAbsoluteContainingBlock::DestroyFrames(DestroyContext& aContext) { + mAbsoluteFrames.DestroyFrames(aContext); } void nsAbsoluteContainingBlock::MarkSizeDependentFramesDirty() { diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsAbsoluteContainingBlock.h firefox-esr-115.4.0esr+build1/layout/generic/nsAbsoluteContainingBlock.h --- firefox-esr-115.3.1esr+build1/layout/generic/nsAbsoluteContainingBlock.h 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsAbsoluteContainingBlock.h 2023-10-17 05:45:50.000000000 +0000 @@ -63,8 +63,8 @@ void InsertFrames(nsIFrame* aDelegatingFrame, mozilla::FrameChildListID aListID, nsIFrame* aPrevFrame, nsFrameList&& aFrameList); - void RemoveFrame(nsIFrame* aDelegatingFrame, - mozilla::FrameChildListID aListID, nsIFrame* aOldFrame); + void RemoveFrame(mozilla::FrameDestroyContext&, mozilla::FrameChildListID, + nsIFrame*); enum class AbsPosReflowFlags { ConstrainHeight = 0x1, @@ -95,9 +95,8 @@ const nsRect& aContainingBlock, AbsPosReflowFlags aFlags, mozilla::OverflowAreas* aOverflowAreas); - using PostDestroyData = nsIFrame::PostDestroyData; - void DestroyFrames(nsIFrame* aDelegatingFrame, nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData); + using DestroyContext = nsIFrame::DestroyContext; + void DestroyFrames(DestroyContext&); bool HasAbsoluteFrames() const { return mAbsoluteFrames.NotEmpty(); } diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsBlockFrame.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsBlockFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsBlockFrame.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsBlockFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -472,43 +472,39 @@ } } -void nsBlockFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { +void nsBlockFrame::Destroy(DestroyContext& aContext) { ClearLineCursors(); - DestroyAbsoluteFrames(aDestructRoot, aPostDestroyData); - mFloats.DestroyFramesFrom(aDestructRoot, aPostDestroyData); + DestroyAbsoluteFrames(aContext); + mFloats.DestroyFrames(aContext); nsPresContext* presContext = PresContext(); mozilla::PresShell* presShell = presContext->PresShell(); - nsLineBox::DeleteLineList(presContext, mLines, aDestructRoot, &mFrames, - aPostDestroyData); + nsLineBox::DeleteLineList(presContext, mLines, &mFrames, aContext); if (HasPushedFloats()) { - SafelyDestroyFrameListProp(aDestructRoot, aPostDestroyData, presShell, - PushedFloatProperty()); + SafelyDestroyFrameListProp(aContext, presShell, PushedFloatProperty()); RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS); } // destroy overflow lines now FrameLines* overflowLines = RemoveOverflowLines(); if (overflowLines) { - nsLineBox::DeleteLineList(presContext, overflowLines->mLines, aDestructRoot, - &overflowLines->mFrames, aPostDestroyData); + nsLineBox::DeleteLineList(presContext, overflowLines->mLines, + &overflowLines->mFrames, aContext); delete overflowLines; } if (HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) { - SafelyDestroyFrameListProp(aDestructRoot, aPostDestroyData, presShell, + SafelyDestroyFrameListProp(aContext, presShell, OverflowOutOfFlowsProperty()); RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS); } if (HasOutsideMarker()) { - SafelyDestroyFrameListProp(aDestructRoot, aPostDestroyData, presShell, - OutsideMarkerProperty()); + SafelyDestroyFrameListProp(aContext, presShell, OutsideMarkerProperty()); RemoveStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER); } - nsContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData); + nsContainerFrame::Destroy(aContext); } /* virtual */ @@ -5833,7 +5829,8 @@ } } -void nsBlockFrame::RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) { +void nsBlockFrame::RemoveFrame(DestroyContext& aContext, ChildListID aListID, + nsIFrame* aOldFrame) { #ifdef NOISY_REFLOW_REASON ListTag(stdout); printf(": remove "); @@ -5843,7 +5840,7 @@ if (aListID == FrameChildListID::Principal) { bool hasFloats = BlockHasAnyFloats(aOldFrame); - DoRemoveFrame(aOldFrame, REMOVE_FIXED_CONTINUATIONS); + DoRemoveFrame(aContext, aOldFrame, REMOVE_FIXED_CONTINUATIONS); if (hasFloats) { MarkSameFloatManagerLinesDirty(this); } @@ -5859,10 +5856,10 @@ MarkSameFloatManagerLinesDirty( static_cast(f->GetParent())); } - DoRemoveOutOfFlowFrame(aOldFrame); + DoRemoveOutOfFlowFrame(aContext, aOldFrame); } else if (FrameChildListID::NoReflowPrincipal == aListID) { // Skip the call to |FrameNeedsReflow| below by returning now. - DoRemoveFrame(aOldFrame, REMOVE_FIXED_CONTINUATIONS); + DoRemoveFrame(aContext, aOldFrame, REMOVE_FIXED_CONTINUATIONS); return; } else { MOZ_CRASH("unexpected child list"); @@ -6116,7 +6113,8 @@ } } -void nsBlockFrame::DoRemoveOutOfFlowFrame(nsIFrame* aFrame) { +void nsBlockFrame::DoRemoveOutOfFlowFrame(DestroyContext& aContext, + nsIFrame* aFrame) { // The containing block is always the parent of aFrame. nsBlockFrame* block = (nsBlockFrame*)aFrame->GetParent(); @@ -6124,17 +6122,16 @@ if (aFrame->IsAbsolutelyPositioned()) { // This also deletes the next-in-flows block->GetAbsoluteContainingBlock()->RemoveFrame( - block, FrameChildListID::Absolute, aFrame); + aContext, FrameChildListID::Absolute, aFrame); } else { // First remove aFrame's next-in-flows. - nsIFrame* nif = aFrame->GetNextInFlow(); - if (nif) { - nif->GetParent()->DeleteNextInFlowChild(nif, false); + if (nsIFrame* nif = aFrame->GetNextInFlow()) { + nif->GetParent()->DeleteNextInFlowChild(aContext, nif, false); } // Now remove aFrame from its child list and Destroy it. block->RemoveFloatFromFloatCache(aFrame); block->RemoveFloat(aFrame); - aFrame->Destroy(); + aFrame->Destroy(aContext); } } @@ -6415,9 +6412,8 @@ // aDeletedFrame and remove aDeletedFrame from that line. But here we // start by locating aDeletedFrame and then scanning from that point // on looking for continuations. -void nsBlockFrame::DoRemoveFrameInternal(nsIFrame* aDeletedFrame, - uint32_t aFlags, - PostDestroyData& aPostDestroyData) { +void nsBlockFrame::DoRemoveFrame(DestroyContext& aContext, + nsIFrame* aDeletedFrame, uint32_t aFlags) { // Clear our line cursor, since our lines may change. ClearLineCursors(); @@ -6426,9 +6422,10 @@ if (!aDeletedFrame->GetPrevInFlow()) { NS_ASSERTION(aDeletedFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW), "Expected out-of-flow frame"); - DoRemoveOutOfFlowFrame(aDeletedFrame); + DoRemoveOutOfFlowFrame(aContext, aDeletedFrame); } else { - nsContainerFrame::DeleteNextInFlowChild(aDeletedFrame, + // FIXME(emilio): aContext is lost here, maybe it's not a big deal? + nsContainerFrame::DeleteNextInFlowChild(aContext, aDeletedFrame, (aFlags & FRAMES_ARE_EMPTY) != 0); } return; @@ -6536,14 +6533,15 @@ #endif // If next-in-flow is an overflow container, must remove it first. + // FIXME: Can we do this unconditionally? if (deletedNextContinuation && deletedNextContinuation->HasAnyStateBits( NS_FRAME_IS_OVERFLOW_CONTAINER)) { deletedNextContinuation->GetParent()->DeleteNextInFlowChild( - deletedNextContinuation, false); + aContext, deletedNextContinuation, false); deletedNextContinuation = nullptr; } - aDeletedFrame->DestroyFrom(aDeletedFrame, aPostDestroyData); + aDeletedFrame->Destroy(aContext); aDeletedFrame = deletedNextContinuation; bool haveAdvancedToNextLine = false; @@ -6650,7 +6648,7 @@ nsBlockFrame* nextBlock = do_QueryFrame(aDeletedFrame->GetParent()); NS_ASSERTION(nextBlock, "Our child's continuation's parent is not a block?"); uint32_t flags = (aFlags & REMOVE_FIXED_CONTINUATIONS); - nextBlock->DoRemoveFrameInternal(aDeletedFrame, flags, aPostDestroyData); + nextBlock->DoRemoveFrame(aContext, aDeletedFrame, flags); } static bool FindBlockLineFor(nsIFrame* aChild, nsLineList::iterator aBegin, @@ -6751,20 +6749,23 @@ } } -void nsBlockFrame::DeleteNextInFlowChild(nsIFrame* aNextInFlow, +void nsBlockFrame::DeleteNextInFlowChild(DestroyContext& aContext, + nsIFrame* aNextInFlow, bool aDeletingEmptyFrames) { MOZ_ASSERT(aNextInFlow->GetPrevInFlow(), "bad next-in-flow"); if (aNextInFlow->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW | NS_FRAME_IS_OVERFLOW_CONTAINER)) { - nsContainerFrame::DeleteNextInFlowChild(aNextInFlow, aDeletingEmptyFrames); + nsContainerFrame::DeleteNextInFlowChild(aContext, aNextInFlow, + aDeletingEmptyFrames); } else { #ifdef DEBUG if (aDeletingEmptyFrames) { nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow); } #endif - DoRemoveFrame(aNextInFlow, aDeletingEmptyFrames ? FRAMES_ARE_EMPTY : 0); + DoRemoveFrame(aContext, aNextInFlow, + aDeletingEmptyFrames ? FRAMES_ARE_EMPTY : 0); } } diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsBlockFrame.h firefox-esr-115.4.0esr+build1/layout/generic/nsBlockFrame.h --- firefox-esr-115.3.1esr+build1/layout/generic/nsBlockFrame.h 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsBlockFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -121,7 +121,7 @@ void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame, const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) override; - void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) override; + void RemoveFrame(DestroyContext&, ChildListID, nsIFrame* aOldFrame) override; nsContainerFrame* GetContentInsertionFrame() override; void AppendDirectlyOwnedAnonBoxes(nsTArray& aResult) override; const nsFrameList& GetChildList(ChildListID aListID) const override; @@ -136,8 +136,8 @@ mozilla::WritingMode aWM, BaselineSharingGroup aBaselineGroup, BaselineExportContext aExportContext) const override; nscoord GetCaretBaseline() const override; - void DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) override; + void Destroy(DestroyContext&) override; + bool IsFloatContainingBlock() const override; void BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) override; @@ -323,7 +323,7 @@ void StealFrame(nsIFrame* aChild) override; - void DeleteNextInFlowChild(nsIFrame* aNextInFlow, + void DeleteNextInFlowChild(DestroyContext&, nsIFrame* aNextInFlow, bool aDeletingEmptyFrames) override; /** @@ -551,19 +551,14 @@ * -- destroys all removed frames */ enum { REMOVE_FIXED_CONTINUATIONS = 0x02, FRAMES_ARE_EMPTY = 0x04 }; - void DoRemoveFrame(nsIFrame* aDeletedFrame, uint32_t aFlags) { - AutoPostDestroyData data(PresContext()); - DoRemoveFrameInternal(aDeletedFrame, aFlags, data.mData); - } + void DoRemoveFrame(DestroyContext&, nsIFrame* aDeletedFrame, uint32_t aFlags); void ReparentFloats(nsIFrame* aFirstFrame, nsBlockFrame* aOldParent, bool aReparentSiblings); - virtual bool ComputeCustomOverflow( - mozilla::OverflowAreas& aOverflowAreas) override; + bool ComputeCustomOverflow(mozilla::OverflowAreas&) override; - virtual void UnionChildOverflow( - mozilla::OverflowAreas& aOverflowAreas) override; + void UnionChildOverflow(mozilla::OverflowAreas&) override; /** * Load all of aFrame's floats into the float manager iff aFrame is not a @@ -601,10 +596,6 @@ bool IsInLineClampContext() const; protected: - /** @see DoRemoveFrame */ - void DoRemoveFrameInternal(nsIFrame* aDeletedFrame, uint32_t aFlags, - PostDestroyData& data); - /** grab overflow lines from this block's prevInFlow, and make them * part of this block's mLines list. * @return true if any lines were drained. @@ -677,7 +668,7 @@ bool aCollectFromSiblings); // Remove a float, abs, rel positioned frame from the appropriate block's list - static void DoRemoveOutOfFlowFrame(nsIFrame* aFrame); + static void DoRemoveOutOfFlowFrame(DestroyContext&, nsIFrame*); /** set up the conditions necessary for an resize reflow * the primary task is to mark the minimumly sufficient lines dirty. diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsBlockReflowContext.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsBlockReflowContext.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsBlockReflowContext.cpp 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsBlockReflowContext.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -330,7 +330,9 @@ // which detaches the placeholder from the float. nsOverflowContinuationTracker::AutoFinish fini(aState.mOverflowTracker, mFrame); - kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true); + nsIFrame::DestroyContext context(mPresContext->PresShell()); + kidNextInFlow->GetParent()->DeleteNextInFlowChild(context, kidNextInFlow, + true); } } } diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsCanvasFrame.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsCanvasFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsCanvasFrame.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsCanvasFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -198,19 +198,18 @@ } } -void nsCanvasFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { +void nsCanvasFrame::Destroy(DestroyContext& aContext) { nsIScrollableFrame* sf = PresContext()->GetPresShell()->GetRootScrollFrameAsScrollable(); if (sf) { sf->RemoveScrollPositionListener(this); } - aPostDestroyData.AddAnonymousContent(mCustomContentContainer.forget()); + aContext.AddAnonymousContent(mCustomContentContainer.forget()); if (mTooltipContent) { - aPostDestroyData.AddAnonymousContent(mTooltipContent.forget()); + aContext.AddAnonymousContent(mTooltipContent.forget()); } - nsContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData); + nsContainerFrame::Destroy(aContext); } void nsCanvasFrame::ScrollPositionWillChange(nscoord aX, nscoord aY) { @@ -273,9 +272,10 @@ } #ifdef DEBUG -void nsCanvasFrame::RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) { +void nsCanvasFrame::RemoveFrame(DestroyContext& aContext, ChildListID aListID, + nsIFrame* aOldFrame) { MOZ_ASSERT(aListID == FrameChildListID::Principal, "unexpected child list"); - nsContainerFrame::RemoveFrame(aListID, aOldFrame); + nsContainerFrame::RemoveFrame(aContext, aListID, aOldFrame); } #endif diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsCanvasFrame.h firefox-esr-115.4.0esr+build1/layout/generic/nsCanvasFrame.h --- firefox-esr-115.3.1esr+build1/layout/generic/nsCanvasFrame.h 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsCanvasFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -46,8 +46,7 @@ Element* GetDefaultTooltip() override; - void DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) override; + void Destroy(DestroyContext&) override; void SetInitialChildList(ChildListID aListID, nsFrameList&& aChildList) override; @@ -56,7 +55,7 @@ const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) override; #ifdef DEBUG - void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) override; + void RemoveFrame(DestroyContext&, ChildListID, nsIFrame*) override; #endif nscoord GetMinISize(gfxContext* aRenderingContext) override; diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsColumnSetFrame.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsColumnSetFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsColumnSetFrame.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsColumnSetFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -1347,7 +1347,7 @@ MOZ_CRASH("unsupported operation"); } -void nsColumnSetFrame::RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) { +void nsColumnSetFrame::RemoveFrame(DestroyContext&, ChildListID, nsIFrame*) { MOZ_CRASH("unsupported operation"); } #endif diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsColumnSetFrame.h firefox-esr-115.4.0esr+build1/layout/generic/nsColumnSetFrame.h --- firefox-esr-115.3.1esr+build1/layout/generic/nsColumnSetFrame.h 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsColumnSetFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -36,7 +36,7 @@ void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame, const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) override; - void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) override; + void RemoveFrame(DestroyContext&, ChildListID, nsIFrame*) override; #endif nscoord GetMinISize(gfxContext* aRenderingContext) override; diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsContainerFrame.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsContainerFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsContainerFrame.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsContainerFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -153,7 +153,8 @@ } } -void nsContainerFrame::RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) { +void nsContainerFrame::RemoveFrame(DestroyContext& aContext, + ChildListID aListID, nsIFrame* aOldFrame) { MOZ_ASSERT(aListID == FrameChildListID::Principal || aListID == FrameChildListID::NoReflowPrincipal, "unexpected child list"); @@ -175,7 +176,7 @@ // Request a reflow on the parent frames involved unless we were explicitly // told not to (FrameChildListID::NoReflowPrincipal). const bool generateReflowCommand = - (FrameChildListID::NoReflowPrincipal != aListID); + aListID != FrameChildListID::NoReflowPrincipal; for (nsIFrame* continuation : Reversed(continuations)) { nsContainerFrame* parent = continuation->GetParent(); @@ -183,7 +184,7 @@ // We really MUST use StealFrame() and nothing else here. // @see nsInlineFrame::StealFrame for details. parent->StealFrame(continuation); - continuation->Destroy(); + continuation->Destroy(aContext); if (generateReflowCommand && parent != lastParent) { presShell->FrameNeedsReflow(parent, IntrinsicDirty::FrameAndAncestors, NS_FRAME_HAS_DIRTY_CHILDREN); @@ -192,25 +193,23 @@ } } -void nsContainerFrame::DestroyAbsoluteFrames( - nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData) { +void nsContainerFrame::DestroyAbsoluteFrames(DestroyContext& aContext) { if (IsAbsoluteContainer()) { - GetAbsoluteContainingBlock()->DestroyFrames(this, aDestructRoot, - aPostDestroyData); + GetAbsoluteContainingBlock()->DestroyFrames(aContext); MarkAsNotAbsoluteContainingBlock(); } } void nsContainerFrame::SafelyDestroyFrameListProp( - nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData, - mozilla::PresShell* aPresShell, FrameListPropertyDescriptor aProp) { + DestroyContext& aContext, mozilla::PresShell* aPresShell, + FrameListPropertyDescriptor aProp) { // Note that the last frame can be removed through another route and thus // delete the property -- that's why we fetch the property again before // removing each frame rather than fetching it once and iterating the list. while (nsFrameList* frameList = GetProperty(aProp)) { nsIFrame* frame = frameList->RemoveFirstChild(); if (MOZ_LIKELY(frame)) { - frame->DestroyFrom(aDestructRoot, aPostDestroyData); + frame->Destroy(aContext); } else { Unused << TakeProperty(aProp); frameList->Delete(aPresShell); @@ -219,17 +218,16 @@ } } -void nsContainerFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { +void nsContainerFrame::Destroy(DestroyContext& aContext) { // Prevent event dispatch during destruction. if (HasView()) { GetView()->SetFrame(nullptr); } - DestroyAbsoluteFrames(aDestructRoot, aPostDestroyData); + DestroyAbsoluteFrames(aContext); // Destroy frames on the principal child list. - mFrames.DestroyFramesFrom(aDestructRoot, aPostDestroyData); + mFrames.DestroyFrames(aContext); // If we have any IB split siblings, clear their references to us. if (HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) { @@ -275,19 +273,18 @@ nsPresContext* pc = PresContext(); mozilla::PresShell* presShell = pc->PresShell(); if (hasO) { - SafelyDestroyFrameListProp(aDestructRoot, aPostDestroyData, presShell, - OverflowProperty()); + SafelyDestroyFrameListProp(aContext, presShell, OverflowProperty()); } MOZ_ASSERT( IsFrameOfType(eCanContainOverflowContainers) || !(hasOC || hasEOC), "this type of frame shouldn't have overflow containers"); if (hasOC) { - SafelyDestroyFrameListProp(aDestructRoot, aPostDestroyData, presShell, + SafelyDestroyFrameListProp(aContext, presShell, OverflowContainersProperty()); } if (hasEOC) { - SafelyDestroyFrameListProp(aDestructRoot, aPostDestroyData, presShell, + SafelyDestroyFrameListProp(aContext, presShell, ExcessOverflowContainersProperty()); } @@ -295,12 +292,11 @@ StyleDisplay()->mTopLayer != StyleTopLayer::None, "only top layer frame may have backdrop"); if (hasBackdrop) { - SafelyDestroyFrameListProp(aDestructRoot, aPostDestroyData, presShell, - BackdropProperty()); + SafelyDestroyFrameListProp(aContext, presShell, BackdropProperty()); } } - nsSplittableFrame::DestroyFrom(aDestructRoot, aPostDestroyData); + nsSplittableFrame::Destroy(aContext); } ///////////////////////////////////////////////////////////////////////////// @@ -896,13 +892,14 @@ // but only if the NoDeleteNextInFlowChild flag isn't set. if (!aStatus.IsInlineBreakBefore() && aStatus.IsFullyComplete() && !(aFlags & ReflowChildFlags::NoDeleteNextInFlowChild)) { - nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow(); - if (kidNextInFlow) { + if (nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow()) { // Remove all of the childs next-in-flows. Make sure that we ask // the right parent to do the removal (it's possible that the // parent is not this because we are executing pullup code) nsOverflowContinuationTracker::AutoFinish fini(aTracker, aKidFrame); - kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true); + DestroyContext context(PresShell()); + kidNextInFlow->GetParent()->DeleteNextInFlowChild(context, kidNextInFlow, + true); } } } @@ -936,13 +933,14 @@ // but only if the NoDeleteNextInFlowChild flag isn't set. if (aStatus.IsFullyComplete() && !(aFlags & ReflowChildFlags::NoDeleteNextInFlowChild)) { - nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow(); - if (kidNextInFlow) { + if (nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow()) { // Remove all of the childs next-in-flows. Make sure that we ask // the right parent to do the removal (it's possible that the // parent is not this because we are executing pullup code) nsOverflowContinuationTracker::AutoFinish fini(aTracker, aKidFrame); - kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true); + DestroyContext context(PresShell()); + kidNextInFlow->GetParent()->DeleteNextInFlowChild(context, kidNextInFlow, + true); } } } @@ -1363,7 +1361,8 @@ * Remove and delete aNextInFlow and its next-in-flows. Updates the sibling and * flow pointers */ -void nsContainerFrame::DeleteNextInFlowChild(nsIFrame* aNextInFlow, +void nsContainerFrame::DeleteNextInFlowChild(DestroyContext& aContext, + nsIFrame* aNextInFlow, bool aDeletingEmptyFrames) { #ifdef DEBUG nsIFrame* prevInFlow = aNextInFlow->GetPrevInFlow(); @@ -1381,8 +1380,8 @@ frames.AppendElement(f); } for (nsIFrame* delFrame : Reversed(frames)) { - delFrame->GetParent()->DeleteNextInFlowChild(delFrame, - aDeletingEmptyFrames); + nsContainerFrame* parent = delFrame->GetParent(); + parent->DeleteNextInFlowChild(aContext, delFrame, aDeletingEmptyFrames); } } @@ -1397,7 +1396,7 @@ // Delete the next-in-flow frame and its descendants. This will also // remove it from its next-in-flow/prev-in-flow chain. - aNextInFlow->Destroy(); + aNextInFlow->Destroy(aContext); MOZ_ASSERT(!prevInFlow->GetNextInFlow(), "non null next-in-flow"); } diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsContainerFrame.h firefox-esr-115.4.0esr+build1/layout/generic/nsContainerFrame.h --- firefox-esr-115.3.1esr+build1/layout/generic/nsContainerFrame.h 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsContainerFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -46,19 +46,19 @@ NS_DECL_QUERYFRAME // nsIFrame overrides - virtual void Init(nsIContent* aContent, nsContainerFrame* aParent, - nsIFrame* aPrevInFlow) override; - virtual nsContainerFrame* GetContentInsertionFrame() override { return this; } - - virtual const nsFrameList& GetChildList(ChildListID aList) const override; - virtual void GetChildLists(nsTArray* aLists) const override; - virtual void DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) override; - virtual void ChildIsDirty(nsIFrame* aChild) override; - - virtual FrameSearchResult PeekOffsetNoAmount(bool aForward, - int32_t* aOffset) override; - virtual FrameSearchResult PeekOffsetCharacter( + void Init(nsIContent* aContent, nsContainerFrame* aParent, + nsIFrame* aPrevInFlow) override; + nsContainerFrame* GetContentInsertionFrame() override { return this; } + + const nsFrameList& GetChildList(ChildListID aList) const override; + void GetChildLists(nsTArray* aLists) const override; + void Destroy(DestroyContext&) override; + + void ChildIsDirty(nsIFrame* aChild) override; + + FrameSearchResult PeekOffsetNoAmount(bool aForward, + int32_t* aOffset) override; + FrameSearchResult PeekOffsetCharacter( bool aForward, int32_t* aOffset, PeekOffsetCharacterOptions aOptions = PeekOffsetCharacterOptions()) override; @@ -123,12 +123,9 @@ * This method is responsible for removing a frame in the frame * list. The implementation should do something with the removed frame * and then generate a reflow command. The implementation is responsible - * for destroying aOldFrame (the caller mustn't destroy aOldFrame). - * - * @param aListID the child list identifier. - * @param aOldFrame the frame to remove + * for destroying the frame (the caller mustn't destroy it). */ - virtual void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame); + virtual void RemoveFrame(DestroyContext&, ChildListID, nsIFrame*); /** * Helper method to create next-in-flows if necessary. If aFrame @@ -149,7 +146,7 @@ * content was complete before aNextInFlow, so aNextInFlow and its * next-in-flows no longer map any real content. */ - virtual void DeleteNextInFlowChild(nsIFrame* aNextInFlow, + virtual void DeleteNextInFlowChild(DestroyContext&, nsIFrame* aNextInFlow, bool aDeletingEmptyFrames); // Positions the frame's view based on the frame's origin @@ -523,8 +520,7 @@ * Derived classes must do that too, if they destroy such frame lists. * See nsBlockFrame::DestroyFrom for an example. */ - void DestroyAbsoluteFrames(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData); + void DestroyAbsoluteFrames(DestroyContext&); /** * Helper for StealFrame. Returns true if aChild was removed from its list. @@ -852,8 +848,7 @@ * frame then remove the property and delete the frame list. * Nothing happens if the property doesn't exist. */ - void SafelyDestroyFrameListProp(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData, + void SafelyDestroyFrameListProp(DestroyContext&, mozilla::PresShell* aPresShell, FrameListPropertyDescriptor aProp); diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsFirstLetterFrame.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsFirstLetterFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsFirstLetterFrame.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsFirstLetterFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -302,10 +302,11 @@ if (aReflowInput.mLineLayout) { aReflowInput.mLineLayout->SetFirstLetterStyleOK(false); } - nsIFrame* kidNextInFlow = kid->GetNextInFlow(); - if (kidNextInFlow) { + if (nsIFrame* kidNextInFlow = kid->GetNextInFlow()) { + DestroyContext context(PresShell()); // Remove all of the childs next-in-flows - kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true); + kidNextInFlow->GetParent()->DeleteNextInFlowChild(context, + kidNextInFlow, true); } } else { // Create a continuation for the child frame if it doesn't already diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsFlexContainerFrame.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsFlexContainerFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsFlexContainerFrame.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsFlexContainerFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -1224,7 +1224,8 @@ std::move(aFrameList)); } -void nsFlexContainerFrame::RemoveFrame(ChildListID aListID, +void nsFlexContainerFrame::RemoveFrame(DestroyContext& aContext, + ChildListID aListID, nsIFrame* aOldFrame) { MOZ_ASSERT(aListID == FrameChildListID::Principal, "unexpected child list"); @@ -1232,7 +1233,7 @@ SetDidPushItemsBitIfNeeded(aListID, aOldFrame); #endif - nsContainerFrame::RemoveFrame(aListID, aOldFrame); + nsContainerFrame::RemoveFrame(aContext, aListID, aOldFrame); } StyleAlignFlags nsFlexContainerFrame::CSSAlignmentForAbsPosChild( diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsFlexContainerFrame.h firefox-esr-115.4.0esr+build1/layout/generic/nsFlexContainerFrame.h --- firefox-esr-115.3.1esr+build1/layout/generic/nsFlexContainerFrame.h 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsFlexContainerFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -181,7 +181,7 @@ void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame, const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) override; - void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) override; + void RemoveFrame(DestroyContext&, ChildListID, nsIFrame*) override; mozilla::StyleAlignFlags CSSAlignmentForAbsPosChild( const ReflowInput& aChildRI, mozilla::LogicalAxis aLogicalAxis) const override; diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsFrameList.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsFrameList.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsFrameList.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsFrameList.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -35,19 +35,9 @@ aPresShell->FreeByObjectID(eArenaObjectID_nsFrameList, this); } -void nsFrameList::DestroyFrames() { +void nsFrameList::DestroyFrames(FrameDestroyContext& aContext) { while (nsIFrame* frame = RemoveFirstChild()) { - frame->Destroy(); - } - mLastChild = nullptr; -} - -void nsFrameList::DestroyFramesFrom(nsIFrame* aDestructRoot, - PostFrameDestroyData& aPostDestroyData) { - MOZ_ASSERT(aDestructRoot, "Missing destruct root"); - - while (nsIFrame* frame = RemoveFirstChild()) { - frame->DestroyFrom(aDestructRoot, aPostDestroyData); + frame->Destroy(aContext); } mLastChild = nullptr; } @@ -105,10 +95,11 @@ return nullptr; } -void nsFrameList::DestroyFrame(nsIFrame* aFrame) { +void nsFrameList::DestroyFrame(FrameDestroyContext& aContext, + nsIFrame* aFrame) { MOZ_ASSERT(aFrame, "null ptr"); RemoveFrame(aFrame); - aFrame->Destroy(); + aFrame->Destroy(aContext); } nsFrameList::Slice nsFrameList::InsertFrames(nsContainerFrame* aParent, diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsFrameList.h firefox-esr-115.4.0esr+build1/layout/generic/nsFrameList.h --- firefox-esr-115.3.1esr+build1/layout/generic/nsFrameList.h 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsFrameList.h 2023-10-17 05:45:50.000000000 +0000 @@ -26,6 +26,9 @@ class nsPresContext; namespace mozilla { + +struct FrameDestroyContext; + class PresShell; class FrameChildList; enum class FrameChildListID { @@ -49,17 +52,6 @@ NoReflowPrincipal, }; -// A helper class for nsIFrame::Destroy[From]. It's defined here because -// nsFrameList needs it and we can't use nsIFrame here. -struct PostFrameDestroyData { - PostFrameDestroyData(const PostFrameDestroyData&) = delete; - PostFrameDestroyData() = default; - - AutoTArray, 100> mAnonymousContent; - void AddAnonymousContent(already_AddRefed&& aContent) { - mAnonymousContent.AppendElement(aContent); - } -}; } // namespace mozilla // Uncomment this to enable expensive frame-list integrity checking @@ -135,16 +127,9 @@ /** * For each frame in this list: remove it from the list then call - * Destroy() on it. - */ - void DestroyFrames(); - - /** - * For each frame in this list: remove it from the list then call - * DestroyFrom(aDestructRoot, aPostDestroyData) on it. + * Destroy() on it with the passed context as an argument. */ - void DestroyFramesFrom(nsIFrame* aDestructRoot, - mozilla::PostFrameDestroyData& aPostDestroyData); + void DestroyFrames(mozilla::FrameDestroyContext&); void Clear() { mFirstChild = mLastChild = nullptr; } @@ -227,10 +212,10 @@ inline bool ContinueRemoveFrame(nsIFrame* aFrame); /** - * Take aFrame out of the frame list and then destroy it. + * Take a frame out of the frame list and then destroy it. * The frame must be non-null and present on this list. */ - void DestroyFrame(nsIFrame* aFrame); + void DestroyFrame(mozilla::FrameDestroyContext&, nsIFrame*); /** * Insert aFrame right after aPrevSibling, or prepend it to this diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsGfxScrollFrame.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsGfxScrollFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsGfxScrollFrame.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsGfxScrollFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -314,9 +314,8 @@ } } -void nsHTMLScrollFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { - DestroyAbsoluteFrames(aDestructRoot, aPostDestroyData); +void nsHTMLScrollFrame::Destroy(DestroyContext& aContext) { + DestroyAbsoluteFrames(aContext); if (mIsRoot) { PresShell()->ResetVisualViewportOffset(); } @@ -329,10 +328,10 @@ } // Unbind the content created in CreateAnonymousContent later... - aPostDestroyData.AddAnonymousContent(mHScrollbarContent.forget()); - aPostDestroyData.AddAnonymousContent(mVScrollbarContent.forget()); - aPostDestroyData.AddAnonymousContent(mScrollCornerContent.forget()); - aPostDestroyData.AddAnonymousContent(mResizerContent.forget()); + aContext.AddAnonymousContent(mHScrollbarContent.forget()); + aContext.AddAnonymousContent(mVScrollbarContent.forget()); + aContext.AddAnonymousContent(mScrollCornerContent.forget()); + aContext.AddAnonymousContent(mResizerContent.forget()); if (mPostedReflowCallback) { PresShell()->CancelReflowCallback(this); @@ -361,7 +360,7 @@ if (mScrollEndEvent) { mScrollEndEvent->Revoke(); } - nsContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData); + nsContainerFrame::Destroy(aContext); } void nsHTMLScrollFrame::SetInitialChildList(ChildListID aListID, @@ -389,10 +388,11 @@ ReloadChildFrames(); } -void nsHTMLScrollFrame::RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) { +void nsHTMLScrollFrame::RemoveFrame(DestroyContext& aContext, + ChildListID aListID, nsIFrame* aOldFrame) { NS_ASSERTION(aListID == FrameChildListID::Principal, "Only main list supported"); - mFrames.DestroyFrame(aOldFrame); + mFrames.DestroyFrame(aContext, aOldFrame); ReloadChildFrames(); } diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsGfxScrollFrame.h firefox-esr-115.4.0esr+build1/layout/generic/nsGfxScrollFrame.h --- firefox-esr-115.3.1esr+build1/layout/generic/nsGfxScrollFrame.h 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsGfxScrollFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -146,11 +146,11 @@ void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame, const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) final; - void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) final; + void RemoveFrame(DestroyContext&, ChildListID, nsIFrame*) final; void DidSetComputedStyle(ComputedStyle* aOldComputedStyle) final; - void DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData&) override; + void Destroy(DestroyContext&) override; nsIScrollableFrame* GetScrollTargetFrame() const final { return const_cast(this); diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsGridContainerFrame.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsGridContainerFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsGridContainerFrame.cpp 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsGridContainerFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -9483,7 +9483,8 @@ std::move(aFrameList)); } -void nsGridContainerFrame::RemoveFrame(ChildListID aListID, +void nsGridContainerFrame::RemoveFrame(DestroyContext& aContext, + ChildListID aListID, nsIFrame* aOldFrame) { MOZ_ASSERT(aListID == FrameChildListID::Principal, "unexpected child list"); @@ -9491,7 +9492,7 @@ SetDidPushItemsBitIfNeeded(aListID, aOldFrame); #endif - nsContainerFrame::RemoveFrame(aListID, aOldFrame); + nsContainerFrame::RemoveFrame(aContext, aListID, aOldFrame); } StyleAlignFlags nsGridContainerFrame::CSSAlignmentForAbsPosChild( diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsGridContainerFrame.h firefox-esr-115.4.0esr+build1/layout/generic/nsGridContainerFrame.h --- firefox-esr-115.3.1esr+build1/layout/generic/nsGridContainerFrame.h 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsGridContainerFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -151,7 +151,7 @@ void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame, const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) override; - void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) override; + void RemoveFrame(DestroyContext&, ChildListID, nsIFrame*) override; mozilla::StyleAlignFlags CSSAlignmentForAbsPosChild( const ReflowInput& aChildRI, LogicalAxis aLogicalAxis) const override; diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsHTMLCanvasFrame.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsHTMLCanvasFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsHTMLCanvasFrame.cpp 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsHTMLCanvasFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -346,12 +346,11 @@ NS_IMPL_FRAMEARENA_HELPERS(nsHTMLCanvasFrame) -void nsHTMLCanvasFrame::DestroyFrom(nsIFrame* aDestroyRoot, - PostDestroyData& aPostDestroyData) { +void nsHTMLCanvasFrame::Destroy(DestroyContext& aContext) { if (IsPrimaryFrame()) { HTMLCanvasElement::FromNode(*mContent)->ResetPrintCallback(); } - nsContainerFrame::DestroyFrom(aDestroyRoot, aPostDestroyData); + nsContainerFrame::Destroy(aContext); } nsHTMLCanvasFrame::~nsHTMLCanvasFrame() = default; diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsHTMLCanvasFrame.h firefox-esr-115.4.0esr+build1/layout/generic/nsHTMLCanvasFrame.h --- firefox-esr-115.3.1esr+build1/layout/generic/nsHTMLCanvasFrame.h 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsHTMLCanvasFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -43,7 +43,7 @@ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) override; - void DestroyFrom(nsIFrame*, PostDestroyData&) override; + void Destroy(DestroyContext&) override; bool UpdateWebRenderCanvasData(nsDisplayListBuilder* aBuilder, WebRenderCanvasData* aCanvasData); diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsIFrame.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsIFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsIFrame.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsIFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -217,11 +217,9 @@ } } -/* static */ -void nsIFrame::DestroyAnonymousContent( - nsPresContext* aPresContext, already_AddRefed&& aContent) { - if (nsCOMPtr content = aContent) { - aPresContext->PresShell()->NativeAnonymousContentRemoved(content); +FrameDestroyContext::~FrameDestroyContext() { + for (auto& content : mozilla::Reversed(mAnonymousContent)) { + mPresShell->NativeAnonymousContentRemoved(content); content->UnbindFromTree(); } } @@ -777,13 +775,11 @@ HandleLastRememberedSize(); } -void nsIFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { +void nsIFrame::Destroy(DestroyContext& aContext) { NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), "destroy called on frame while scripts not blocked"); NS_ASSERTION(!GetNextSibling() && !GetPrevSibling(), "Frames should be removed before destruction."); - NS_ASSERTION(aDestructRoot, "Must specify destruct root"); MOZ_ASSERT(!HasAbsolutelyPositionedChildren()); MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT), "NS_FRAME_PART_OF_IBSPLIT set on non-nsContainerFrame?"); @@ -807,15 +803,7 @@ nsPresContext* presContext = PresContext(); mozilla::PresShell* presShell = presContext->GetPresShell(); if (HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) { - nsPlaceholderFrame* placeholder = GetPlaceholderFrame(); - NS_ASSERTION( - !placeholder || (aDestructRoot != this), - "Don't call Destroy() on OOFs, call Destroy() on the placeholder."); - NS_ASSERTION(!placeholder || nsLayoutUtils::IsProperAncestorFrame( - aDestructRoot, placeholder), - "Placeholder relationship should have been torn down already; " - "this might mean we have a stray placeholder in the tree."); - if (placeholder) { + if (nsPlaceholderFrame* placeholder = GetPlaceholderFrame()) { placeholder->SetOutOfFlowFrame(nullptr); } } @@ -882,7 +870,7 @@ // aPostDestroyData to unbind it after frame destruction is done. if (HasAnyStateBits(NS_FRAME_GENERATED_CONTENT) && mContent->IsRootOfNativeAnonymousSubtree()) { - aPostDestroyData.AddAnonymousContent(mContent.forget()); + aContext.AddAnonymousContent(mContent.forget()); } } diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsIFrame.h firefox-esr-115.4.0esr+build1/layout/generic/nsIFrame.h --- firefox-esr-115.3.1esr+build1/layout/generic/nsIFrame.h 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsIFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -494,6 +494,35 @@ //---------------------------------------------------------------------- +namespace mozilla { + +// A simple class to group stuff that we need to keep around when tearing down +// a frame tree. +// +// Native anonymous content created by the frames need to get unbound _after_ +// the frame has been destroyed, see bug 1400618. +// +// We destroy the anonymous content bottom-up (so, in reverse order), because +// it's a bit simpler, though we generally don't have that much nested anonymous +// content (except for scrollbars). +struct MOZ_RAII FrameDestroyContext { + explicit FrameDestroyContext(PresShell* aPs) : mPresShell(aPs) {} + + void AddAnonymousContent(already_AddRefed&& aContent) { + if (RefPtr content = aContent) { + mAnonymousContent.AppendElement(std::move(content)); + } + } + + ~FrameDestroyContext(); + + private: + PresShell* const mPresShell; + AutoTArray, 100> mAnonymousContent; +}; + +} // namespace mozilla + /** * A frame in the layout model. This interface is supported by all frame * objects. @@ -632,31 +661,7 @@ void* operator new(size_t, mozilla::PresShell*) MOZ_MUST_OVERRIDE; - using PostDestroyData = mozilla::PostFrameDestroyData; - struct MOZ_RAII AutoPostDestroyData { - explicit AutoPostDestroyData(nsPresContext* aPresContext) - : mPresContext(aPresContext) {} - ~AutoPostDestroyData() { - for (auto& content : mozilla::Reversed(mData.mAnonymousContent)) { - nsIFrame::DestroyAnonymousContent(mPresContext, content.forget()); - } - } - nsPresContext* mPresContext; - PostDestroyData mData; - }; - /** - * Destroys this frame and each of its child frames (recursively calls - * Destroy() for each child). If this frame is a first-continuation, this - * also removes the frame from the primary frame map and clears undisplayed - * content for its content node. - * If the frame is a placeholder, it also ensures the out-of-flow frame's - * removal and destruction. - */ - void Destroy() { - AutoPostDestroyData data(PresContext()); - DestroyFrom(this, data.mData); - // Note that |this| is deleted at this point. - } + using DestroyContext = mozilla::FrameDestroyContext; /** * Flags for PeekOffsetCharacter, PeekOffsetNoAmount, PeekOffsetWord return @@ -690,32 +695,19 @@ : mRespectClusters(true), mIgnoreUserStyleAll(false) {} }; - protected: - friend class nsBlockFrame; // for access to DestroyFrom + virtual void Destroy(DestroyContext&); + protected: /** * Return true if the frame is part of a Selection. * Helper method to implement the public IsSelected() API. */ virtual bool IsFrameSelected() const; - /** - * Implements Destroy(). Do not call this directly except from within a - * DestroyFrom() implementation. - * - * @note This will always be called, so it is not necessary to override - * Destroy() in subclasses of nsFrame, just DestroyFrom(). - * - * @param aDestructRoot is the root of the subtree being destroyed - */ - virtual void DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData); - friend class nsFrameList; // needed to pass aDestructRoot through to children - friend class nsLineBox; // needed to pass aDestructRoot through to children - friend class nsContainerFrame; // needed to pass aDestructRoot through to - // children template friend class do_QueryFrameHelper; // to read mClass + friend class nsBlockFrame; // for GetCaretBaseline + friend class nsContainerFrame; // for ReparentFrameViewTo virtual ~nsIFrame(); @@ -4890,9 +4882,6 @@ void HandleLastRememberedSize(); protected: - static void DestroyAnonymousContent(nsPresContext* aPresContext, - already_AddRefed&& aContent); - /** * Reparent this frame's view if it has one. */ diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsImageFrame.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsImageFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsImageFrame.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsImageFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -485,8 +485,7 @@ #endif } -void nsImageFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { +void nsImageFrame::Destroy(DestroyContext& aContext) { MaybeSendIntrinsicSizeAndRatioToEmbedder(Nothing(), Nothing()); if (mReflowCallbackPosted) { @@ -524,7 +523,7 @@ BrokenImageIcon::RemoveObserver(this); } - nsAtomicContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData); + nsAtomicContainerFrame::Destroy(aContext); } void nsImageFrame::DeinitOwnedRequest() { diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsImageFrame.h firefox-esr-115.4.0esr+build1/layout/generic/nsImageFrame.h --- firefox-esr-115.3.1esr+build1/layout/generic/nsImageFrame.h 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsImageFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -70,7 +70,7 @@ NS_DECL_FRAMEARENA_HELPERS(nsImageFrame) NS_DECL_QUERYFRAME - void DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData&) override; + void Destroy(DestroyContext&) override; void DidSetComputedStyle(ComputedStyle* aOldStyle) final; void Init(nsIContent* aContent, nsContainerFrame* aParent, diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsInlineFrame.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsInlineFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsInlineFrame.cpp 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsInlineFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -166,8 +166,7 @@ return CONTINUE; } -void nsInlineFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { +void nsInlineFrame::Destroy(DestroyContext& aContext) { nsFrameList* overflowFrames = GetOverflowFrames(); if (overflowFrames) { // Fixup the parent pointers for any child frames on the OverflowList. @@ -175,7 +174,7 @@ // container (an ancestor). overflowFrames->ApplySetParent(this); } - nsContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData); + nsContainerFrame::Destroy(aContext); } void nsInlineFrame::StealFrame(nsIFrame* aChild) { diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsInlineFrame.h firefox-esr-115.4.0esr+build1/layout/generic/nsInlineFrame.h --- firefox-esr-115.3.1esr+build1/layout/generic/nsInlineFrame.h 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsInlineFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -68,8 +68,8 @@ PeekOffsetCharacterOptions aOptions = PeekOffsetCharacterOptions()) override; - virtual void DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) override; + void Destroy(DestroyContext&) override; + void StealFrame(nsIFrame* aChild) override; // nsIHTMLReflow overrides diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsLineBox.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsLineBox.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsLineBox.cpp 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsLineBox.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -348,8 +348,7 @@ } void nsLineBox::DeleteLineList(nsPresContext* aPresContext, nsLineList& aLines, - nsIFrame* aDestructRoot, nsFrameList* aFrames, - PostDestroyData& aPostDestroyData) { + nsFrameList* aFrames, DestroyContext& aContext) { PresShell* presShell = aPresContext->PresShell(); // Keep our line list and frame list up to date as we @@ -366,7 +365,7 @@ MOZ_DIAGNOSTIC_ASSERT(child == line->mFirstChild, "Lines out of sync"); line->mFirstChild = aFrames->FirstChild(); line->NoteFrameRemoved(child); - child->DestroyFrom(aDestructRoot, aPostDestroyData); + child->Destroy(aContext); } MOZ_DIAGNOSTIC_ASSERT(line == aLines.front(), "destroying child frames messed up our lines!"); diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsLineBox.h firefox-esr-115.4.0esr+build1/layout/generic/nsLineBox.h --- firefox-esr-115.3.1esr+build1/layout/generic/nsLineBox.h 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsLineBox.h 2023-10-17 05:45:50.000000000 +0000 @@ -396,10 +396,9 @@ mBounds.BSize(mWritingMode) = 0; } - using PostDestroyData = nsIFrame::PostDestroyData; + using DestroyContext = nsIFrame::DestroyContext; static void DeleteLineList(nsPresContext* aPresContext, nsLineList& aLines, - nsIFrame* aDestructRoot, nsFrameList* aFrames, - PostDestroyData& aPostDestroyData); + nsFrameList* aFrames, DestroyContext&); // search from end to beginning of [aBegin, aEnd) // Returns true if it found the line and false if not. diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsLineLayout.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsLineLayout.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsLineLayout.cpp 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsLineLayout.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -998,7 +998,9 @@ // Remove all of the childs next-in-flows. Make sure that we ask // the right parent to do the removal (it's possible that the // parent is not this because we are executing pullup code) - kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true); + FrameDestroyContext context(aFrame->PresShell()); + kidNextInFlow->GetParent()->DeleteNextInFlowChild(context, + kidNextInFlow, true); } } diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsPlaceholderFrame.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsPlaceholderFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsPlaceholderFrame.cpp 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsPlaceholderFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -146,27 +146,18 @@ return FrameChildListID::Float; } -void nsPlaceholderFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { - nsIFrame* oof = mOutOfFlowFrame; - if (oof) { +void nsPlaceholderFrame::Destroy(DestroyContext& aContext) { + if (nsIFrame* oof = mOutOfFlowFrame) { mOutOfFlowFrame = nullptr; oof->RemoveProperty(nsIFrame::PlaceholderFrameProperty()); - // If aDestructRoot is not an ancestor of the out-of-flow frame, - // then call RemoveFrame on it here. - // Also destroy it here if it's a popup frame. (Bug 96291) - // FIXME(emilio): Is the popup special-case still needed? - if (oof->IsMenuPopupFrame() || - !nsLayoutUtils::IsProperAncestorFrame(aDestructRoot, oof)) { - ChildListID listId = ChildListIDForOutOfFlow(GetStateBits(), oof); - nsFrameManager* fm = PresContext()->FrameConstructor(); - fm->RemoveFrame(listId, oof); - } - // else oof will be destroyed by its parent + // Destroy the out of flow now. + ChildListID listId = ChildListIDForOutOfFlow(GetStateBits(), oof); + nsFrameManager* fm = PresContext()->FrameConstructor(); + fm->RemoveFrame(aContext, listId, oof); } - nsIFrame::DestroyFrom(aDestructRoot, aPostDestroyData); + nsIFrame::Destroy(aContext); } /* virtual */ diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsPlaceholderFrame.h firefox-esr-115.4.0esr+build1/layout/generic/nsPlaceholderFrame.h --- firefox-esr-115.3.1esr+build1/layout/generic/nsPlaceholderFrame.h 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsPlaceholderFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -102,8 +102,7 @@ const ReflowInput& aReflowInput, nsReflowStatus& aStatus) override; - void DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) override; + void Destroy(DestroyContext&) override; #if defined(DEBUG) || (defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF)) void BuildDisplayList(nsDisplayListBuilder* aBuilder, diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsRubyFrame.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsRubyFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsRubyFrame.cpp 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsRubyFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -254,15 +254,15 @@ PushChildrenToOverflow(lastChild->GetNextSibling(), lastChild); aReflowInput.mLineLayout->SetDirtyNextLine(); } - } else { + } else if (rtcCount) { + DestroyContext context(PresShell()); // If the ruby base container is reflowed completely, the line // layout will remove the next-in-flows of that frame. But the // line layout is not aware of the ruby text containers, hence // it is necessary to remove them here. for (uint32_t i = 0; i < rtcCount; i++) { - nsIFrame* nextRTC = textContainers[i]->GetNextInFlow(); - if (nextRTC) { - nextRTC->GetParent()->DeleteNextInFlowChild(nextRTC, true); + if (nsIFrame* nextRTC = textContainers[i]->GetNextInFlow()) { + nextRTC->GetParent()->DeleteNextInFlowChild(context, nextRTC, true); } } } diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsRubyTextContainerFrame.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsRubyTextContainerFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsRubyTextContainerFrame.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsRubyTextContainerFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -81,9 +81,10 @@ } /* virtual */ -void nsRubyTextContainerFrame::RemoveFrame(ChildListID aListID, +void nsRubyTextContainerFrame::RemoveFrame(DestroyContext& aContext, + ChildListID aListID, nsIFrame* aOldFrame) { - nsContainerFrame::RemoveFrame(aListID, aOldFrame); + nsContainerFrame::RemoveFrame(aContext, aListID, aOldFrame); UpdateSpanFlag(); } diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsRubyTextContainerFrame.h firefox-esr-115.4.0esr+build1/layout/generic/nsRubyTextContainerFrame.h --- firefox-esr-115.3.1esr+build1/layout/generic/nsRubyTextContainerFrame.h 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsRubyTextContainerFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -28,13 +28,13 @@ NS_DECL_QUERYFRAME // nsIFrame overrides - virtual bool IsFrameOfType(uint32_t aFlags) const override; - virtual void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, - const ReflowInput& aReflowInput, - nsReflowStatus& aStatus) override; + bool IsFrameOfType(uint32_t aFlags) const override; + void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, + const ReflowInput& aReflowInput, + nsReflowStatus& aStatus) override; #ifdef DEBUG_FRAME_DUMP - virtual nsresult GetFrameName(nsAString& aResult) const override; + nsresult GetFrameName(nsAString& aResult) const override; #endif // nsContainerFrame overrides @@ -44,7 +44,7 @@ void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame, const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) override; - virtual void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) override; + void RemoveFrame(DestroyContext&, ChildListID, nsIFrame*) override; bool IsSpanContainer() const { return HasAnyStateBits(NS_RUBY_TEXT_CONTAINER_IS_SPAN); diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsSplittableFrame.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsSplittableFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsSplittableFrame.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsSplittableFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -26,15 +26,14 @@ nsIFrame::Init(aContent, aParent, aPrevInFlow); } -void nsSplittableFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { +void nsSplittableFrame::Destroy(DestroyContext& aContext) { // Disconnect from the flow list if (mPrevContinuation || mNextContinuation) { RemoveFromFlow(this); } // Let the base class destroy the frame - nsIFrame::DestroyFrom(aDestructRoot, aPostDestroyData); + nsIFrame::Destroy(aContext); } nsIFrame* nsSplittableFrame::GetPrevContinuation() const { diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsSplittableFrame.h firefox-esr-115.4.0esr+build1/layout/generic/nsSplittableFrame.h --- firefox-esr-115.3.1esr+build1/layout/generic/nsSplittableFrame.h 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsSplittableFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -23,8 +23,7 @@ void Init(nsIContent* aContent, nsContainerFrame* aParent, nsIFrame* aPrevInFlow) override; - void DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) override; + void Destroy(DestroyContext&) override; /* * Frame continuations can be either fluid or non-fluid. diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsSubDocumentFrame.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsSubDocumentFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsSubDocumentFrame.cpp 2023-09-29 10:59:55.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsSubDocumentFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -943,8 +943,7 @@ static nsView* BeginSwapDocShellsForViews(nsView* aSibling); -void nsSubDocumentFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { +void nsSubDocumentFrame::Destroy(DestroyContext& aContext) { PropagateIsUnderHiddenEmbedderElementToSubView(true); if (mPostedReflowCallback) { PresShell()->CancelReflowCallback(this); @@ -977,7 +976,7 @@ } } - nsAtomicContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData); + nsAtomicContainerFrame::Destroy(aContext); } nsFrameLoader* nsSubDocumentFrame::FrameLoader() const { diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsSubDocumentFrame.h firefox-esr-115.4.0esr+build1/layout/generic/nsSubDocumentFrame.h --- firefox-esr-115.3.1esr+build1/layout/generic/nsSubDocumentFrame.h 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsSubDocumentFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -53,8 +53,7 @@ void Init(nsIContent* aContent, nsContainerFrame* aParent, nsIFrame* aPrevInFlow) override; - void DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) override; + void Destroy(DestroyContext&) override; nscoord GetMinISize(gfxContext* aRenderingContext) override; nscoord GetPrefISize(gfxContext* aRenderingContext) override; diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsTextFrame.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsTextFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsTextFrame.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsTextFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -3868,8 +3868,7 @@ } } -void nsTextFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { +void nsTextFrame::Destroy(DestroyContext& aContext) { ClearFrameOffsetCache(); // We might want to clear NS_CREATE_FRAME_IF_NON_WHITESPACE or @@ -3880,7 +3879,7 @@ mNextContinuation->SetPrevInFlow(nullptr); } // Let the base class destroy the frame - nsIFrame::DestroyFrom(aDestructRoot, aPostDestroyData); + nsIFrame::Destroy(aContext); } nsTArray* nsTextFrame::GetContinuations() { @@ -3922,8 +3921,7 @@ void Init(nsIContent* aContent, nsContainerFrame* aParent, nsIFrame* aPrevInFlow) final; - void DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) final; + void Destroy(DestroyContext&) override; nsTextFrame* GetPrevContinuation() const final { return mPrevContinuation; } @@ -4089,8 +4087,7 @@ } // prev frame is bidi } -void nsContinuingTextFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { +void nsContinuingTextFrame::Destroy(DestroyContext& aContext) { ClearFrameOffsetCache(); // The text associated with this frame will become associated with our @@ -4113,7 +4110,7 @@ } nsSplittableFrame::RemoveFromFlow(this); // Let the base class destroy the frame - nsIFrame::DestroyFrom(aDestructRoot, aPostDestroyData); + nsIFrame::Destroy(aContext); } nsIFrame* nsContinuingTextFrame::FirstInFlow() const { @@ -8705,7 +8702,7 @@ // f is going to be destroyed soon, after it is unlinked from the // continuation chain. If its textrun is going to be destroyed we need to // do it now, before we unlink the frames to remove from the flow, - // because DestroyFrom calls ClearTextRuns() and that will start at the + // because Destroy calls ClearTextRuns() and that will start at the // first frame with the text run and walk the continuations. if (f->IsInTextRunUserData()) { f->ClearTextRuns(); @@ -8726,16 +8723,17 @@ aFrame->SetPrevInFlow(nullptr); nsContainerFrame* parent = aFrame->GetParent(); + nsIFrame::DestroyContext context(aFrame->PresShell()); nsBlockFrame* parentBlock = do_QueryFrame(parent); if (parentBlock) { // Manually call DoRemoveFrame so we can tell it that we're // removing empty frames; this will keep it from blowing away // text runs. - parentBlock->DoRemoveFrame(aFrame, nsBlockFrame::FRAMES_ARE_EMPTY); + parentBlock->DoRemoveFrame(context, aFrame, nsBlockFrame::FRAMES_ARE_EMPTY); } else { // Just remove it normally; use FrameChildListID::NoReflowPrincipal to avoid // posting new reflows. - parent->RemoveFrame(FrameChildListID::NoReflowPrincipal, aFrame); + parent->RemoveFrame(context, FrameChildListID::NoReflowPrincipal, aFrame); } } diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsTextFrame.h firefox-esr-115.4.0esr+build1/layout/generic/nsTextFrame.h --- firefox-esr-115.3.1esr+build1/layout/generic/nsTextFrame.h 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsTextFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -222,8 +222,7 @@ void Init(nsIContent* aContent, nsContainerFrame* aParent, nsIFrame* aPrevInFlow) override; - void DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) override; + void Destroy(DestroyContext&) override; mozilla::Maybe GetCursor(const nsPoint&) final; diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsVideoFrame.cpp firefox-esr-115.4.0esr+build1/layout/generic/nsVideoFrame.cpp --- firefox-esr-115.3.1esr+build1/layout/generic/nsVideoFrame.cpp 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsVideoFrame.cpp 2023-10-17 05:45:50.000000000 +0000 @@ -147,14 +147,13 @@ return mContent->GetShadowRoot()->GetFirstChild(); } -void nsVideoFrame::DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) { +void nsVideoFrame::Destroy(DestroyContext& aContext) { if (mReflowCallbackPosted) { PresShell()->CancelReflowCallback(this); } - aPostDestroyData.AddAnonymousContent(mCaptionDiv.forget()); - aPostDestroyData.AddAnonymousContent(mPosterImage.forget()); - nsContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData); + aContext.AddAnonymousContent(mCaptionDiv.forget()); + aContext.AddAnonymousContent(mPosterImage.forget()); + nsContainerFrame::Destroy(aContext); } class DispatchResizeEvent : public Runnable { diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/nsVideoFrame.h firefox-esr-115.4.0esr+build1/layout/generic/nsVideoFrame.h --- firefox-esr-115.3.1esr+build1/layout/generic/nsVideoFrame.h 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/nsVideoFrame.h 2023-10-17 05:45:50.000000000 +0000 @@ -58,8 +58,7 @@ mozilla::ComputeSizeFlags aFlags) override; nscoord GetMinISize(gfxContext* aRenderingContext) override; nscoord GetPrefISize(gfxContext* aRenderingContext) override; - void DestroyFrom(nsIFrame* aDestructRoot, - PostDestroyData& aPostDestroyData) override; + void Destroy(DestroyContext&) override; void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, diff -Nru firefox-esr-115.3.1esr+build1/layout/generic/test/test_bug394239.html firefox-esr-115.4.0esr+build1/layout/generic/test/test_bug394239.html --- firefox-esr-115.3.1esr+build1/layout/generic/test/test_bug394239.html 2023-09-29 10:59:56.000000000 +0000 +++ firefox-esr-115.4.0esr+build1/layout/generic/test/test_bug394239.html 2023-10-17 05:45:50.000000000 +0000 @@ -27,8 +27,6 @@
 
static colheader relative colheader absolute colheader