diff -Nru firefox-99.0.1+build1/accessible/android/AccessibleWrap.cpp firefox-100.0+build1/accessible/android/AccessibleWrap.cpp --- firefox-99.0.1+build1/accessible/android/AccessibleWrap.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/android/AccessibleWrap.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -28,6 +28,7 @@ #include "mozilla/a11y/PDocAccessibleChild.h" #include "mozilla/jni/GeckoBundleUtils.h" +#include "mozilla/StaticPrefs_accessibility.h" // icu TRUE conflicting with java::sdk::Boolean::TRUE() // https://searchfox.org/mozilla-central/rev/ce02064d8afc8673cef83c92896ee873bd35e7ae/intl/icu/source/common/unicode/umachine.h#265 @@ -213,6 +214,9 @@ if (state & states::BUSY) { sessionAcc->SendWindowStateChangedEvent(accessible); + if (StaticPrefs::accessibility_cache_enabled_AtStartup()) { + sessionAcc->SendWindowContentChangedEvent(); + } } break; } @@ -229,6 +233,12 @@ event->Priority()); break; } + case nsIAccessibleEvent::EVENT_REORDER: { + if (StaticPrefs::accessibility_cache_enabled_AtStartup()) { + sessionAcc->SendWindowContentChangedEvent(); + } + break; + } default: break; } @@ -294,7 +304,7 @@ return false; } -void AccessibleWrap::PivotTo(int32_t aGranularity, bool aForward, +bool AccessibleWrap::PivotTo(int32_t aGranularity, bool aForward, bool aInclusive) { a11y::Pivot pivot(RootAccessible()); TraversalRule rule(aGranularity); @@ -326,7 +336,11 @@ SessionAccessibility::GetInstanceFor(result); sessionAcc->SendAccessibilityFocusedEvent(newPosition); } + + return true; } + + return false; } void AccessibleWrap::ExploreByTouch(float aX, float aY) { diff -Nru firefox-99.0.1+build1/accessible/android/AccessibleWrap.h firefox-100.0+build1/accessible/android/AccessibleWrap.h --- firefox-99.0.1+build1/accessible/android/AccessibleWrap.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/android/AccessibleWrap.h 2022-04-26 05:44:41.000000000 +0000 @@ -37,7 +37,7 @@ virtual bool GetSelectionBounds(int32_t* aStartOffset, int32_t* aEndOffset); MOZ_CAN_RUN_SCRIPT_BOUNDARY - virtual void PivotTo(int32_t aGranularity, bool aForward, bool aInclusive); + virtual bool PivotTo(int32_t aGranularity, bool aForward, bool aInclusive); virtual void NavigateText(int32_t aGranularity, int32_t aStartOffset, int32_t aEndOffset, bool aForward, bool aSelect); diff -Nru firefox-99.0.1+build1/accessible/android/DocAccessibleWrap.cpp firefox-100.0+build1/accessible/android/DocAccessibleWrap.cpp --- firefox-99.0.1+build1/accessible/android/DocAccessibleWrap.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/android/DocAccessibleWrap.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -18,6 +18,7 @@ #include "TraversalRule.h" #include "mozilla/PresShell.h" #include "mozilla/a11y/DocAccessiblePlatformExtChild.h" +#include "mozilla/StaticPrefs_accessibility.h" using namespace mozilla; using namespace mozilla::a11y; @@ -201,6 +202,9 @@ } void DocAccessibleWrap::CacheViewport(bool aCachePivotBoundaries) { + if (StaticPrefs::accessibility_cache_enabled_AtStartup()) { + return; + } mCachePivotBoundaries |= aCachePivotBoundaries; if (VirtualViewID() == kNoID && !mCacheRefreshTimer) { NS_NewTimerWithFuncCallback(getter_AddRefs(mCacheRefreshTimer), @@ -225,6 +229,10 @@ } void DocAccessibleWrap::CacheFocusPath(AccessibleWrap* aAccessible) { + if (StaticPrefs::accessibility_cache_enabled_AtStartup()) { + return; + } + mFocusPath.Clear(); if (IPCAccessibilityActive()) { DocAccessibleChild* ipcDoc = IPCDoc(); @@ -263,6 +271,10 @@ } void DocAccessibleWrap::UpdateFocusPathBounds() { + if (StaticPrefs::accessibility_cache_enabled_AtStartup()) { + return; + } + if (!mFocusPath.Count()) { return; } diff -Nru firefox-99.0.1+build1/accessible/android/Platform.cpp firefox-100.0+build1/accessible/android/Platform.cpp --- firefox-99.0.1+build1/accessible/android/Platform.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/android/Platform.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -10,6 +10,7 @@ #include "SessionAccessibility.h" #include "mozilla/a11y/RemoteAccessible.h" #include "mozilla/Components.h" +#include "mozilla/StaticPrefs_accessibility.h" #include "nsIAccessibleEvent.h" #include "nsIAccessiblePivot.h" #include "nsIStringBundle.h" @@ -64,6 +65,13 @@ case nsIAccessibleEvent::EVENT_FOCUS: sessionAcc->SendFocusEvent(WrapperFor(aTarget)); break; + case nsIAccessibleEvent::EVENT_REORDER: + if (StaticPrefs::accessibility_cache_enabled_AtStartup()) { + sessionAcc->SendWindowContentChangedEvent(); + } + break; + default: + break; } } @@ -96,11 +104,15 @@ if (aState & states::BUSY) { sessionAcc->SendWindowStateChangedEvent(WrapperFor(aTarget)); + if (StaticPrefs::accessibility_cache_enabled_AtStartup()) { + sessionAcc->SendWindowContentChangedEvent(); + } } } void a11y::ProxyCaretMoveEvent(RemoteAccessible* aTarget, int32_t aOffset, - bool aIsSelectionCollapsed) { + bool aIsSelectionCollapsed, + int32_t aGranularity) { RefPtr sessionAcc = SessionAccessibility::GetInstanceFor(aTarget); diff -Nru firefox-99.0.1+build1/accessible/android/RemoteAccessibleWrap.cpp firefox-100.0+build1/accessible/android/RemoteAccessibleWrap.cpp --- firefox-99.0.1+build1/accessible/android/RemoteAccessibleWrap.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/android/RemoteAccessibleWrap.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -113,15 +113,16 @@ return Proxy()->SelectionBoundsAt(0, unused, aStartOffset, aEndOffset); } -void RemoteAccessibleWrap::PivotTo(int32_t aGranularity, bool aForward, +bool RemoteAccessibleWrap::PivotTo(int32_t aGranularity, bool aForward, bool aInclusive) { if (StaticPrefs::accessibility_cache_enabled_AtStartup()) { - AccessibleWrap::PivotTo(aGranularity, aForward, aInclusive); - return; + return AccessibleWrap::PivotTo(aGranularity, aForward, aInclusive); } Unused << Proxy()->Document()->GetPlatformExtension()->SendPivot( Proxy()->ID(), aGranularity, aForward, aInclusive); + + return true; } void RemoteAccessibleWrap::NavigateText(int32_t aGranularity, diff -Nru firefox-99.0.1+build1/accessible/android/RemoteAccessibleWrap.h firefox-100.0+build1/accessible/android/RemoteAccessibleWrap.h --- firefox-99.0.1+build1/accessible/android/RemoteAccessibleWrap.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/android/RemoteAccessibleWrap.h 2022-04-26 05:44:41.000000000 +0000 @@ -61,7 +61,7 @@ virtual bool GetSelectionBounds(int32_t* aStartOffset, int32_t* aEndOffset) override; - virtual void PivotTo(int32_t aGranularity, bool aForward, + virtual bool PivotTo(int32_t aGranularity, bool aForward, bool aInclusive) override; virtual void NavigateText(int32_t aGranularity, int32_t aStartOffset, diff -Nru firefox-99.0.1+build1/accessible/android/SessionAccessibility.cpp firefox-100.0+build1/accessible/android/SessionAccessibility.cpp --- firefox-99.0.1+build1/accessible/android/SessionAccessibility.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/android/SessionAccessibility.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -25,6 +25,7 @@ #include "mozilla/widget/GeckoViewSupport.h" #include "mozilla/MouseEvents.h" #include "mozilla/dom/MouseEventBinding.h" +#include "mozilla/StaticPrefs_accessibility.h" #ifdef DEBUG # include @@ -90,6 +91,10 @@ Settings::Init(); } +bool SessionAccessibility::IsCacheEnabled() { + return StaticPrefs::accessibility_cache_enabled_AtStartup(); +} + mozilla::jni::Object::LocalRef SessionAccessibility::GetNodeInfo(int32_t aID) { java::GeckoBundle::GlobalRef ret = nullptr; RefPtr self(this); @@ -129,6 +134,22 @@ FORWARD_ACTION_TO_ACCESSIBLE(DoAction, 0); } +bool SessionAccessibility::CachedPivot(int32_t aID, int32_t aGranularity, + bool aForward, bool aInclusive) { + RefPtr self(this); + bool ret = false; + nsAppShell::SyncRunEvent( + [this, self, aID, aGranularity, aForward, aInclusive, &ret] { + if (RootAccessibleWrap* rootAcc = GetRoot()) { + if (AccessibleWrap* acc = rootAcc->FindAccessibleById(aID)) { + ret = acc->PivotTo(aGranularity, aForward, aInclusive); + } + } + }); + + return ret; +} + void SessionAccessibility::Pivot(int32_t aID, int32_t aGranularity, bool aForward, bool aInclusive) { FORWARD_ACTION_TO_ACCESSIBLE(PivotTo, aGranularity, aForward, aInclusive); @@ -254,6 +275,7 @@ mSessionAccessibility->SendEvent( java::sdk::AccessibilityEvent::TYPE_VIEW_SCROLLED, virtualViewId, aAccessible->AndroidClass(), eventInfo); + SendWindowContentChangedEvent(); } void SessionAccessibility::SendWindowContentChangedEvent() { @@ -464,7 +486,6 @@ } mSessionAccessibility->UpdateCachedBounds(infos); - SendWindowContentChangedEvent(); } void SessionAccessibility::UpdateAccessibleFocusBoundaries( diff -Nru firefox-99.0.1+build1/accessible/android/SessionAccessibility.h firefox-100.0+build1/accessible/android/SessionAccessibility.h --- firefox-99.0.1+build1/accessible/android/SessionAccessibility.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/android/SessionAccessibility.h 2022-04-26 05:44:41.000000000 +0000 @@ -47,10 +47,13 @@ // Native implementations using Base::AttachNative; using Base::DisposeNative; + bool IsCacheEnabled(); jni::Object::LocalRef GetNodeInfo(int32_t aID); void SetText(int32_t aID, jni::String::Param aText); void Click(int32_t aID); void Pivot(int32_t aID, int32_t aGranularity, bool aForward, bool aInclusive); + bool CachedPivot(int32_t aID, int32_t aGranularity, bool aForward, + bool aInclusive); void ExploreByTouch(int32_t aID, float aX, float aY); void NavigateText(int32_t aID, int32_t aGranularity, int32_t aStartOffset, int32_t aEndOffset, bool aForward, bool aSelect); diff -Nru firefox-99.0.1+build1/accessible/android/TraversalRule.cpp firefox-100.0+build1/accessible/android/TraversalRule.cpp --- firefox-99.0.1+build1/accessible/android/TraversalRule.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/android/TraversalRule.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -39,12 +39,9 @@ return result; } - // Bug 1733268: Support OPAQUE1/opacity remotely - if (aAcc->IsLocal() && (state & states::OPAQUE1) == 0) { - nsIFrame* frame = aAcc->AsLocal()->GetFrame(); - if (frame && frame->StyleEffects()->mOpacity == 0.0f) { - return result | nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE; - } + auto opacity = aAcc->Opacity(); + if (opacity && *opacity == 0.0f) { + return result | nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE; } switch (mGranularity) { diff -Nru firefox-99.0.1+build1/accessible/atk/AccessibleWrap.cpp firefox-100.0+build1/accessible/atk/AccessibleWrap.cpp --- firefox-99.0.1+build1/accessible/atk/AccessibleWrap.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/atk/AccessibleWrap.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -16,8 +16,8 @@ #include "RemoteAccessible.h" #include "DocAccessibleParent.h" #include "RootAccessible.h" -#include "TableAccessible.h" -#include "TableCellAccessible.h" +#include "mozilla/a11y/TableAccessibleBase.h" +#include "mozilla/a11y/TableCellAccessibleBase.h" #include "nsMai.h" #include "nsMaiHyperlink.h" #include "nsString.h" @@ -1372,7 +1372,8 @@ } void a11y::ProxyCaretMoveEvent(RemoteAccessible* aTarget, int32_t aOffset, - bool aIsSelectionCollapsed) { + bool aIsSelectionCollapsed, + int32_t aGranularity) { AtkObject* wrapper = GetWrapperFor(aTarget); g_signal_emit_by_name(wrapper, "text_caret_moved", aOffset); } @@ -1524,13 +1525,13 @@ } // static -LocalAccessible* AccessibleWrap::GetColumnHeader(TableAccessible* aAccessible, - int32_t aColIdx) { +Accessible* AccessibleWrap::GetColumnHeader(TableAccessibleBase* aAccessible, + int32_t aColIdx) { if (!aAccessible) { return nullptr; } - LocalAccessible* cell = aAccessible->CellAt(0, aColIdx); + Accessible* cell = aAccessible->CellAt(0, aColIdx); if (!cell) { return nullptr; } @@ -1542,12 +1543,12 @@ } // otherwise get column header for the data cell at the first row. - TableCellAccessible* tableCell = cell->AsTableCell(); + TableCellAccessibleBase* tableCell = cell->AsTableCellBase(); if (!tableCell) { return nullptr; } - AutoTArray headerCells; + AutoTArray headerCells; tableCell->ColHeaderCells(&headerCells); if (headerCells.IsEmpty()) { return nullptr; @@ -1557,13 +1558,13 @@ } // static -LocalAccessible* AccessibleWrap::GetRowHeader(TableAccessible* aAccessible, - int32_t aRowIdx) { +Accessible* AccessibleWrap::GetRowHeader(TableAccessibleBase* aAccessible, + int32_t aRowIdx) { if (!aAccessible) { return nullptr; } - LocalAccessible* cell = aAccessible->CellAt(aRowIdx, 0); + Accessible* cell = aAccessible->CellAt(aRowIdx, 0); if (!cell) { return nullptr; } @@ -1575,12 +1576,12 @@ } // otherwise get row header for the data cell at the first column. - TableCellAccessible* tableCell = cell->AsTableCell(); + TableCellAccessibleBase* tableCell = cell->AsTableCellBase(); if (!tableCell) { return nullptr; } - AutoTArray headerCells; + AutoTArray headerCells; tableCell->RowHeaderCells(&headerCells); if (headerCells.IsEmpty()) { return nullptr; diff -Nru firefox-99.0.1+build1/accessible/atk/AccessibleWrap.h firefox-100.0+build1/accessible/atk/AccessibleWrap.h --- firefox-99.0.1+build1/accessible/atk/AccessibleWrap.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/atk/AccessibleWrap.h 2022-04-26 05:44:41.000000000 +0000 @@ -70,10 +70,10 @@ static void GetKeyBinding(LocalAccessible* aAccessible, nsAString& aResult); - static LocalAccessible* GetColumnHeader(TableAccessible* aAccessible, - int32_t aColIdx); - static LocalAccessible* GetRowHeader(TableAccessible* aAccessible, - int32_t aRowIdx); + static Accessible* GetColumnHeader(TableAccessibleBase* aAccessible, + int32_t aColIdx); + static Accessible* GetRowHeader(TableAccessibleBase* aAccessible, + int32_t aRowIdx); protected: nsresult FireAtkStateChangeEvent(AccEvent* aEvent, AtkObject* aObject); diff -Nru firefox-99.0.1+build1/accessible/atk/nsMaiInterfaceTableCell.cpp firefox-100.0+build1/accessible/atk/nsMaiInterfaceTableCell.cpp --- firefox-99.0.1+build1/accessible/atk/nsMaiInterfaceTableCell.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/atk/nsMaiInterfaceTableCell.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -6,40 +6,46 @@ #include "InterfaceInitFuncs.h" -#include "LocalAccessible-inl.h" -#include "AccessibleWrap.h" #include "nsAccUtils.h" -#include "TableAccessible.h" -#include "TableCellAccessible.h" +#include "mozilla/a11y/TableAccessibleBase.h" +#include "mozilla/a11y/TableCellAccessibleBase.h" +#include "mozilla/StaticPrefs_accessibility.h" #include "nsMai.h" #include "RemoteAccessible.h" #include "nsArrayUtils.h" #include "mozilla/Likely.h" +using namespace mozilla; using namespace mozilla::a11y; extern "C" { static gint GetColumnSpanCB(AtkTableCell* aCell) { - AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell)); - if (accWrap) { - return accWrap->AsTableCell()->ColExtent(); + Accessible* acc = GetInternalObj(ATK_OBJECT(aCell)); + if (!acc) { + return 0; + } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + return static_cast(acc->AsTableCellBase()->ColExtent()); } - if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aCell))) { + if (RemoteAccessible* proxy = acc->AsRemote()) { return proxy->ColExtent(); } return 0; } -static gboolean GetRowSpanCB(AtkTableCell* aCell) { - AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell)); - if (accWrap) { - return accWrap->AsTableCell()->RowExtent(); +static gint GetRowSpanCB(AtkTableCell* aCell) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aCell)); + if (!acc) { + return 0; + } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + return static_cast(acc->AsTableCellBase()->RowExtent()); } - if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aCell))) { + if (RemoteAccessible* proxy = acc->AsRemote()) { return proxy->RowExtent(); } @@ -47,8 +53,12 @@ } static gboolean GetPositionCB(AtkTableCell* aCell, gint* aRow, gint* aCol) { - if (AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell))) { - TableCellAccessible* cell = accWrap->AsTableCell(); + Accessible* acc = GetInternalObj(ATK_OBJECT(aCell)); + if (!acc) { + return false; + } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + TableCellAccessibleBase* cell = acc->AsTableCellBase(); if (!cell) { return false; } @@ -57,7 +67,7 @@ return true; } - if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aCell))) { + if (RemoteAccessible* proxy = acc->AsRemote()) { uint32_t rowIdx = 0, colIdx = 0; proxy->GetPosition(&rowIdx, &colIdx); *aCol = colIdx; @@ -70,8 +80,12 @@ static gboolean GetColumnRowSpanCB(AtkTableCell* aCell, gint* aCol, gint* aRow, gint* aColExtent, gint* aRowExtent) { - if (AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell))) { - TableCellAccessible* cellAcc = accWrap->AsTableCell(); + Accessible* acc = GetInternalObj(ATK_OBJECT(aCell)); + if (!acc) { + return false; + } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + TableCellAccessibleBase* cellAcc = acc->AsTableCellBase(); if (!cellAcc) { return false; } @@ -82,7 +96,7 @@ return true; } - if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aCell))) { + if (RemoteAccessible* proxy = acc->AsRemote()) { uint32_t colIdx = 0, rowIdx = 0, colExtent = 0, rowExtent = 0; proxy->GetColRowExtents(&colIdx, &rowIdx, &colExtent, &rowExtent); *aCol = colIdx; @@ -96,15 +110,18 @@ } static AtkObject* GetTableCB(AtkTableCell* aTableCell) { - AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTableCell)); - if (accWrap) { - TableAccessible* table = accWrap->AsTableCell()->Table(); + Accessible* acc = GetInternalObj(ATK_OBJECT(aTableCell)); + if (!acc) { + return nullptr; + } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + TableAccessibleBase* table = acc->AsTableCellBase()->Table(); if (!table) { return nullptr; } - LocalAccessible* tableAcc = table->AsAccessible(); - return tableAcc ? AccessibleWrap::GetAtkObject(tableAcc) : nullptr; + Accessible* tableAcc = table->AsAccessible(); + return tableAcc ? GetWrapperFor(tableAcc) : nullptr; } if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aTableCell))) { @@ -116,16 +133,20 @@ } static GPtrArray* GetColumnHeaderCellsCB(AtkTableCell* aCell) { - if (AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell))) { - AutoTArray headers; - accWrap->AsTableCell()->ColHeaderCells(&headers); + Accessible* acc = GetInternalObj(ATK_OBJECT(aCell)); + if (!acc) { + return nullptr; + } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + AutoTArray headers; + acc->AsTableCellBase()->ColHeaderCells(&headers); if (headers.IsEmpty()) { return nullptr; } GPtrArray* atkHeaders = g_ptr_array_sized_new(headers.Length()); - for (LocalAccessible* header : headers) { - AtkObject* atkHeader = AccessibleWrap::GetAtkObject(header); + for (Accessible* header : headers) { + AtkObject* atkHeader = AccessibleWrap::GetAtkObject(header->AsLocal()); g_object_ref(atkHeader); g_ptr_array_add(atkHeaders, atkHeader); } @@ -154,16 +175,20 @@ } static GPtrArray* GetRowHeaderCellsCB(AtkTableCell* aCell) { - if (AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell))) { - AutoTArray headers; - accWrap->AsTableCell()->RowHeaderCells(&headers); + Accessible* acc = GetInternalObj(ATK_OBJECT(aCell)); + if (!acc) { + return nullptr; + } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + AutoTArray headers; + acc->AsTableCellBase()->RowHeaderCells(&headers); if (headers.IsEmpty()) { return nullptr; } GPtrArray* atkHeaders = g_ptr_array_sized_new(headers.Length()); - for (LocalAccessible* header : headers) { - AtkObject* atkHeader = AccessibleWrap::GetAtkObject(header); + for (Accessible* header : headers) { + AtkObject* atkHeader = AccessibleWrap::GetAtkObject(header->AsLocal()); g_object_ref(atkHeader); g_ptr_array_add(atkHeaders, atkHeader); } diff -Nru firefox-99.0.1+build1/accessible/atk/nsMaiInterfaceTable.cpp firefox-100.0+build1/accessible/atk/nsMaiInterfaceTable.cpp --- firefox-99.0.1+build1/accessible/atk/nsMaiInterfaceTable.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/atk/nsMaiInterfaceTable.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -6,17 +6,18 @@ #include "InterfaceInitFuncs.h" -#include "LocalAccessible-inl.h" #include "AccessibleWrap.h" #include "nsAccUtils.h" -#include "TableAccessible.h" -#include "TableCellAccessible.h" +#include "mozilla/a11y/TableAccessibleBase.h" +#include "mozilla/a11y/TableCellAccessibleBase.h" +#include "mozilla/StaticPrefs_accessibility.h" #include "nsMai.h" #include "RemoteAccessible.h" #include "nsArrayUtils.h" #include "mozilla/Likely.h" +using namespace mozilla; using namespace mozilla::a11y; extern "C" { @@ -26,15 +27,18 @@ } AtkObject* cellAtkObj = nullptr; - AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable)); - if (accWrap) { - LocalAccessible* cell = accWrap->AsTable()->CellAt(aRowIdx, aColIdx); + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return nullptr; + } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + Accessible* cell = acc->AsTableBase()->CellAt(aRowIdx, aColIdx); if (!cell) { return nullptr; } - cellAtkObj = AccessibleWrap::GetAtkObject(cell); - } else if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) { + cellAtkObj = GetWrapperFor(cell); + } else if (RemoteAccessible* proxy = acc->AsRemote()) { RemoteAccessible* cell = proxy->TableCellAt(aRowIdx, aColIdx); if (!cell) { return nullptr; @@ -55,12 +59,15 @@ return -1; } - AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable)); - if (accWrap) { - return static_cast(accWrap->AsTable()->CellIndexAt(aRowIdx, aColIdx)); + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return -1; + } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + return static_cast(acc->AsTableBase()->CellIndexAt(aRowIdx, aColIdx)); } - if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) { + if (RemoteAccessible* proxy = acc->AsRemote()) { return static_cast(proxy->TableCellIndexAt(aRowIdx, aColIdx)); } @@ -72,12 +79,15 @@ return -1; } - AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable)); - if (accWrap) { - return static_cast(accWrap->AsTable()->ColIndexAt(aIdx)); + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return -1; + } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + return static_cast(acc->AsTableBase()->ColIndexAt(aIdx)); } - if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) { + if (RemoteAccessible* proxy = acc->AsRemote()) { return static_cast(proxy->TableColumnIndexAt(aIdx)); } @@ -89,12 +99,15 @@ return -1; } - AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable)); - if (accWrap) { - return static_cast(accWrap->AsTable()->RowIndexAt(aIdx)); + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return -1; + } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + return static_cast(acc->AsTableBase()->RowIndexAt(aIdx)); } - if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) { + if (RemoteAccessible* proxy = acc->AsRemote()) { return static_cast(proxy->TableRowIndexAt(aIdx)); } @@ -102,12 +115,15 @@ } static gint getColumnCountCB(AtkTable* aTable) { - AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable)); - if (accWrap) { - return static_cast(accWrap->AsTable()->ColCount()); + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return -1; + } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + return static_cast(acc->AsTableBase()->ColCount()); } - if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) { + if (RemoteAccessible* proxy = acc->AsRemote()) { return static_cast(proxy->TableColumnCount()); } @@ -115,12 +131,15 @@ } static gint getRowCountCB(AtkTable* aTable) { - AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable)); - if (accWrap) { - return static_cast(accWrap->AsTable()->RowCount()); + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return -1; + } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + return static_cast(acc->AsTableBase()->RowCount()); } - if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) { + if (RemoteAccessible* proxy = acc->AsRemote()) { return static_cast(proxy->TableRowCount()); } @@ -132,12 +151,15 @@ return -1; } - AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable)); - if (accWrap) { - return static_cast(accWrap->AsTable()->ColExtentAt(aRowIdx, aColIdx)); + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return -1; + } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + return static_cast(acc->AsTableBase()->ColExtentAt(aRowIdx, aColIdx)); } - if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) { + if (RemoteAccessible* proxy = acc->AsRemote()) { return static_cast(proxy->TableColumnExtentAt(aRowIdx, aColIdx)); } @@ -145,12 +167,15 @@ } static gint getRowExtentAtCB(AtkTable* aTable, gint aRowIdx, gint aColIdx) { - AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable)); - if (accWrap) { - return static_cast(accWrap->AsTable()->RowExtentAt(aRowIdx, aColIdx)); + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return -1; + } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + return static_cast(acc->AsTableBase()->RowExtentAt(aRowIdx, aColIdx)); } - if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) { + if (RemoteAccessible* proxy = acc->AsRemote()) { return static_cast(proxy->TableRowExtentAt(aRowIdx, aColIdx)); } @@ -158,13 +183,16 @@ } static AtkObject* getCaptionCB(AtkTable* aTable) { - AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable)); - if (accWrap) { - LocalAccessible* caption = accWrap->AsTable()->Caption(); - return caption ? AccessibleWrap::GetAtkObject(caption) : nullptr; + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return nullptr; + } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + Accessible* caption = acc->AsTableBase()->Caption(); + return caption ? GetWrapperFor(caption) : nullptr; } - if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) { + if (RemoteAccessible* proxy = acc->AsRemote()) { RemoteAccessible* caption = proxy->TableCaption(); return caption ? GetWrapperFor(caption) : nullptr; } @@ -174,27 +202,31 @@ static const gchar* getColumnDescriptionCB(AtkTable* aTable, gint aColumn) { nsAutoString autoStr; - AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable)); - if (accWrap) { - accWrap->AsTable()->ColDescription(aColumn, autoStr); - } else if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) { - proxy->TableColumnDescription(aColumn, autoStr); - } else { + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { return nullptr; } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + acc->AsTableBase()->ColDescription(aColumn, autoStr); + } else if (RemoteAccessible* proxy = acc->AsRemote()) { + proxy->TableColumnDescription(aColumn, autoStr); + } return AccessibleWrap::ReturnString(autoStr); } static AtkObject* getColumnHeaderCB(AtkTable* aTable, gint aColIdx) { - AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable)); - if (accWrap) { - LocalAccessible* header = - AccessibleWrap::GetColumnHeader(accWrap->AsTable(), aColIdx); - return header ? AccessibleWrap::GetAtkObject(header) : nullptr; + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return nullptr; + } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + Accessible* header = + AccessibleWrap::GetColumnHeader(acc->AsTableBase(), aColIdx); + return header ? GetWrapperFor(header) : nullptr; } - if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) { + if (RemoteAccessible* proxy = acc->AsRemote()) { RemoteAccessible* header = proxy->AtkTableColumnHeader(aColIdx); return header ? GetWrapperFor(header) : nullptr; } @@ -204,27 +236,31 @@ static const gchar* getRowDescriptionCB(AtkTable* aTable, gint aRow) { nsAutoString autoStr; - AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable)); - if (accWrap) { - accWrap->AsTable()->RowDescription(aRow, autoStr); - } else if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) { - proxy->TableRowDescription(aRow, autoStr); - } else { + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { return nullptr; } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + acc->AsTableBase()->RowDescription(aRow, autoStr); + } else if (RemoteAccessible* proxy = acc->AsRemote()) { + proxy->TableRowDescription(aRow, autoStr); + } return AccessibleWrap::ReturnString(autoStr); } static AtkObject* getRowHeaderCB(AtkTable* aTable, gint aRowIdx) { - AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable)); - if (accWrap) { - LocalAccessible* header = - AccessibleWrap::GetRowHeader(accWrap->AsTable(), aRowIdx); - return header ? AccessibleWrap::GetAtkObject(header) : nullptr; + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return nullptr; + } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + Accessible* header = + AccessibleWrap::GetRowHeader(acc->AsTableBase(), aRowIdx); + return header ? GetWrapperFor(header) : nullptr; } - if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) { + if (RemoteAccessible* proxy = acc->AsRemote()) { RemoteAccessible* header = proxy->AtkTableRowHeader(aRowIdx); return header ? GetWrapperFor(header) : nullptr; } @@ -244,14 +280,15 @@ *aSelected = nullptr; AutoTArray cols; - AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable)); - if (accWrap) { - accWrap->AsTable()->SelectedColIndices(&cols); - } else if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) { - proxy->TableSelectedColumnIndices(&cols); - } else { + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { return 0; } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + acc->AsTableBase()->SelectedColIndices(&cols); + } else if (RemoteAccessible* proxy = acc->AsRemote()) { + proxy->TableSelectedColumnIndices(&cols); + } if (cols.IsEmpty()) return 0; @@ -268,14 +305,15 @@ static gint getSelectedRowsCB(AtkTable* aTable, gint** aSelected) { AutoTArray rows; - AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable)); - if (accWrap) { - accWrap->AsTable()->SelectedRowIndices(&rows); - } else if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) { - proxy->TableSelectedRowIndices(&rows); - } else { + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { return 0; } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + acc->AsTableBase()->SelectedRowIndices(&rows); + } else if (RemoteAccessible* proxy = acc->AsRemote()) { + proxy->TableSelectedRowIndices(&rows); + } gint* atkRows = g_new(gint, rows.Length()); if (!atkRows) { @@ -289,11 +327,14 @@ } static gboolean isColumnSelectedCB(AtkTable* aTable, gint aColIdx) { - AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable)); - if (accWrap) { - return static_cast(accWrap->AsTable()->IsColSelected(aColIdx)); + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return FALSE; + } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + return static_cast(acc->AsTableBase()->IsColSelected(aColIdx)); } - if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) { + if (RemoteAccessible* proxy = acc->AsRemote()) { return static_cast(proxy->TableColumnSelected(aColIdx)); } @@ -301,11 +342,14 @@ } static gboolean isRowSelectedCB(AtkTable* aTable, gint aRowIdx) { - AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable)); - if (accWrap) { - return static_cast(accWrap->AsTable()->IsRowSelected(aRowIdx)); + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return FALSE; } - if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) { + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { + return static_cast(acc->AsTableBase()->IsRowSelected(aRowIdx)); + } + if (RemoteAccessible* proxy = acc->AsRemote()) { return static_cast(proxy->TableRowSelected(aRowIdx)); } @@ -313,12 +357,15 @@ } static gboolean isCellSelectedCB(AtkTable* aTable, gint aRowIdx, gint aColIdx) { - AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable)); - if (accWrap) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return FALSE; + } + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || acc->IsLocal()) { return static_cast( - accWrap->AsTable()->IsCellSelected(aRowIdx, aColIdx)); + acc->AsTableBase()->IsCellSelected(aRowIdx, aColIdx)); } - if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) { + if (RemoteAccessible* proxy = acc->AsRemote()) { return static_cast(proxy->TableCellSelected(aRowIdx, aColIdx)); } diff -Nru firefox-99.0.1+build1/accessible/base/AccAttributes.cpp firefox-100.0+build1/accessible/base/AccAttributes.cpp --- firefox-99.0.1+build1/accessible/base/AccAttributes.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/base/AccAttributes.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -69,6 +69,13 @@ }, [&aValueString](const UniquePtr& val) { aValueString.AppendPrintf("Matrix4x4=%s", ToString(*val).c_str()); + }, + [&aValueString](const nsTArray& val) { + for (size_t i = 0; i < val.Length() - 1; i++) { + aValueString.AppendInt(val[i]); + aValueString.Append(u", "); + } + aValueString.AppendInt(val[val.Length() - 1]); }); } @@ -165,6 +172,11 @@ [](const UniquePtr& val) { MOZ_ASSERT_UNREACHABLE( "Trying to copy an AccAttributes containing a matrix"); + }, + [](const nsTArray& val) { + // We don't copy arrays. + MOZ_ASSERT_UNREACHABLE( + "Trying to copy an AccAttributes containing an array"); }); } } diff -Nru firefox-99.0.1+build1/accessible/base/AccAttributes.h firefox-100.0+build1/accessible/base/AccAttributes.h --- firefox-99.0.1+build1/accessible/base/AccAttributes.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/base/AccAttributes.h 2022-04-26 05:44:41.000000000 +0000 @@ -70,7 +70,7 @@ Variant, nsTArray, CSSCoord, FontSize, Color, DeleteEntry, UniquePtr, RefPtr, uint64_t, UniquePtr, - UniquePtr>; + UniquePtr, nsTArray>; static_assert(sizeof(AttrValueType) <= 16); using AtomVariantMap = nsTHashMap, AttrValueType>; @@ -104,6 +104,7 @@ void SetAttribute(nsAtom* aAttrName, AccGroupInfo* aAttrValue) { UniquePtr value(aAttrValue); + mData.InsertOrUpdate(aAttrName, AsVariant(std::move(value))); } void SetAttribute(nsAtom* aAttrName, gfx::Matrix4x4&& aAttrValue) { diff -Nru firefox-99.0.1+build1/accessible/base/AccEvent.cpp firefox-100.0+build1/accessible/base/AccEvent.cpp --- firefox-99.0.1+build1/accessible/base/AccEvent.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/base/AccEvent.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -127,11 +127,13 @@ AccTextSelChangeEvent::AccTextSelChangeEvent(HyperTextAccessible* aTarget, dom::Selection* aSelection, - int32_t aReason) + int32_t aReason, + int32_t aGranularity) : AccEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED, aTarget, eAutoDetect, eCoalesceTextSelChange), mSel(aSelection), - mReason(aReason) {} + mReason(aReason), + mGranularity(aGranularity) {} AccTextSelChangeEvent::~AccTextSelChangeEvent() {} @@ -246,7 +248,8 @@ AccCaretMoveEvent* cm = downcast_accEvent(aEvent); xpEvent = new xpcAccCaretMoveEvent( type, ToXPC(acc), ToXPCDocument(doc), node, fromUser, - cm->GetCaretOffset(), cm->IsSelectionCollapsed(), cm->IsAtEndOfLine()); + cm->GetCaretOffset(), cm->IsSelectionCollapsed(), cm->IsAtEndOfLine(), + cm->GetGranularity()); return xpEvent.forget(); } diff -Nru firefox-99.0.1+build1/accessible/base/AccEvent.h firefox-100.0+build1/accessible/base/AccEvent.h --- firefox-99.0.1+build1/accessible/base/AccEvent.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/base/AccEvent.h 2022-04-26 05:44:41.000000000 +0000 @@ -348,12 +348,14 @@ public: AccCaretMoveEvent(LocalAccessible* aAccessible, int32_t aCaretOffset, bool aIsSelectionCollapsed, bool aIsAtEndOfLine, + int32_t aGranularity, EIsFromUserInput aIsFromUserInput = eAutoDetect) : AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aAccessible, aIsFromUserInput), mCaretOffset(aCaretOffset), mIsSelectionCollapsed(aIsSelectionCollapsed), - mIsAtEndOfLine(aIsAtEndOfLine) {} + mIsAtEndOfLine(aIsAtEndOfLine), + mGranularity(aGranularity) {} virtual ~AccCaretMoveEvent() {} // AccEvent @@ -368,11 +370,14 @@ bool IsSelectionCollapsed() const { return mIsSelectionCollapsed; } bool IsAtEndOfLine() { return mIsAtEndOfLine; } + int32_t GetGranularity() const { return mGranularity; } + private: int32_t mCaretOffset; bool mIsSelectionCollapsed; bool mIsAtEndOfLine; + int32_t mGranularity; }; /** @@ -381,7 +386,8 @@ class AccTextSelChangeEvent : public AccEvent { public: AccTextSelChangeEvent(HyperTextAccessible* aTarget, - dom::Selection* aSelection, int32_t aReason); + dom::Selection* aSelection, int32_t aReason, + int32_t aGranularity); virtual ~AccTextSelChangeEvent(); // AccEvent @@ -397,6 +403,8 @@ */ bool IsCaretMoveOnly() const; + int32_t GetGranularity() const { return mGranularity; } + /** * Return selection ranges in document/control. */ @@ -405,6 +413,7 @@ private: RefPtr mSel; int32_t mReason; + int32_t mGranularity; friend class EventQueue; friend class SelectionManager; diff -Nru firefox-99.0.1+build1/accessible/base/AccIterator.h firefox-100.0+build1/accessible/base/AccIterator.h --- firefox-99.0.1+build1/accessible/base/AccIterator.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/base/AccIterator.h 2022-04-26 05:44:41.000000000 +0000 @@ -23,7 +23,7 @@ class AccIterable { public: virtual ~AccIterable() {} - virtual LocalAccessible* Next() = 0; + virtual Accessible* Next() = 0; private: friend class Relation; diff -Nru firefox-99.0.1+build1/accessible/base/CacheConstants.h firefox-100.0+build1/accessible/base/CacheConstants.h --- firefox-99.0.1+build1/accessible/base/CacheConstants.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/base/CacheConstants.h 2022-04-26 05:44:41.000000000 +0000 @@ -23,6 +23,8 @@ static constexpr uint64_t Actions = ((uint64_t)0x1) << 8; static constexpr uint64_t Style = ((uint64_t)0x1) << 9; static constexpr uint64_t TransformMatrix = ((uint64_t)0x1) << 10; + static constexpr uint64_t ScrollPosition = ((uint64_t)0x1) << 11; + static constexpr uint64_t Table = ((uint64_t)0x1) << 11; static constexpr uint64_t All = ~((uint64_t)0x0); }; diff -Nru firefox-99.0.1+build1/accessible/base/CachedTableAccessible.cpp firefox-100.0+build1/accessible/base/CachedTableAccessible.cpp --- firefox-99.0.1+build1/accessible/base/CachedTableAccessible.cpp 1970-01-01 00:00:00.000000000 +0000 +++ firefox-100.0+build1/accessible/base/CachedTableAccessible.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -0,0 +1,474 @@ +/* -*- 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 "CachedTableAccessible.h" + +#include "AccIterator.h" +#include "DocAccessibleParent.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/UniquePtr.h" +#include "nsAccUtils.h" +#include "nsIAccessiblePivot.h" +#include "Pivot.h" +#include "RemoteAccessible.h" +#include "TableAccessible.h" +#include "TableCellAccessible.h" + +namespace mozilla::a11y { + +// Used to search for table descendants relevant to table structure. +class TablePartRule : public PivotRule { + public: + virtual uint16_t Match(Accessible* aAcc) override { + role accRole = aAcc->Role(); + if (accRole == roles::CAPTION || aAcc->IsTableCell()) { + return nsIAccessibleTraversalRule::FILTER_MATCH | + nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE; + } + if (aAcc->IsTableRow()) { + return nsIAccessibleTraversalRule::FILTER_MATCH; + } + if (aAcc->IsTable() || + // Generic containers. + accRole == roles::TEXT || accRole == roles::TEXT_CONTAINER || + accRole == roles::SECTION || + // Row groups. + accRole == roles::GROUPING) { + // Walk inside these, but don't match them. + return nsIAccessibleTraversalRule::FILTER_IGNORE; + } + return nsIAccessibleTraversalRule::FILTER_IGNORE | + nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE; + } +}; + +// Iterates through headers explicitly associated with a remote table cell via +// the headers DOM attribute. These are cached as Accessible ids. +class RemoteExplicitHeadersIterator : public AccIterable { + public: + RemoteExplicitHeadersIterator(const nsTArray& aHeaders, + Accessible* aDoc) + : mHeaders(aHeaders), mDoc(aDoc), mIndex(0) {} + + virtual Accessible* Next() override { + while (mIndex < mHeaders.Length()) { + uint64_t id = mHeaders[mIndex++]; + Accessible* acc = nsAccUtils::GetAccessibleByID(mDoc, id); + if (acc) { + return acc; + } + } + return nullptr; + } + + private: + const nsTArray& mHeaders; + Accessible* mDoc; + uint32_t mIndex; +}; + +// The Accessible* keys should only be used for lookup. They should not be +// dereferenced. +using CachedTablesMap = nsTHashMap; +// We use a global map rather than a map in each document for three reasons: +// 1. We don't have a common base class for local and remote documents. +// 2. It avoids wasting memory in a document that doesn't have any tables. +// 3. It allows the cache management to be encapsulated here in +// CachedTableAccessible. +static StaticAutoPtr sCachedTables; + +/* static */ +CachedTableAccessible* CachedTableAccessible::GetFrom(Accessible* aAcc) { + if (!sCachedTables) { + sCachedTables = new CachedTablesMap(); + ClearOnShutdown(&sCachedTables); + } + return &sCachedTables->LookupOrInsertWith( + aAcc, [&] { return CachedTableAccessible(aAcc); }); +} + +/* static */ +void CachedTableAccessible::Invalidate(Accessible* aAcc) { + if (!sCachedTables) { + return; + } + Accessible* table = nullptr; + if (aAcc->IsTable()) { + table = aAcc; + } else if (aAcc->IsTableCell()) { + for (table = aAcc->Parent(); table; table = table->Parent()) { + if (table->IsTable()) { + break; + } + } + } else { + MOZ_ASSERT_UNREACHABLE("Should only be called on a table or a cell"); + } + if (table) { + // Destroy the instance (if any). We'll create a new one the next time it + // is requested. + sCachedTables->Remove(table); + } +} + +CachedTableAccessible::CachedTableAccessible(Accessible* aAcc) : mAcc(aAcc) { + MOZ_ASSERT(mAcc); + // Build the cache. The cache can only be built once per instance. When it's + // invalidated, we just throw away the instance and create a new one when + // the cache is next needed. + int32_t rowIdx = -1; + uint32_t colIdx = 0; + // Maps a column index to the cell index of its previous implicit column + // header. + nsTHashMap prevColHeaders; + Pivot pivot(mAcc); + TablePartRule rule; + for (Accessible* part = pivot.Next(mAcc, rule); part; + part = pivot.Next(part, rule)) { + role partRole = part->Role(); + if (partRole == roles::CAPTION) { + // If there are multiple captions, use the first. + if (!mCaptionAccID) { + mCaptionAccID = part->ID(); + } + continue; + } + if (part->IsTableRow()) { + ++rowIdx; + colIdx = 0; + // This might be an empty row, so ensure a row here, as our row count is + // based on the length of mRowColToCellIdx. + EnsureRow(rowIdx); + continue; + } + MOZ_ASSERT(part->IsTableCell()); + if (rowIdx == -1) { + // We haven't created a row yet, so this cell must be outside a row. + continue; + } + // Check for a cell spanning multiple rows which already occupies this + // position. Keep incrementing until we find a vacant position. + for (;;) { + EnsureRowCol(rowIdx, colIdx); + if (mRowColToCellIdx[rowIdx][colIdx] == kNoCellIdx) { + // This position is not occupied. + break; + } + // This position is occupied. + ++colIdx; + } + // Create the cell. + uint32_t cellIdx = mCells.Length(); + auto prevColHeader = prevColHeaders.MaybeGet(colIdx); + auto cell = mCells.AppendElement( + CachedTableCellAccessible(part->ID(), part, rowIdx, colIdx, + prevColHeader ? *prevColHeader : kNoCellIdx)); + mAccToCellIdx.InsertOrUpdate(part, cellIdx); + // Update our row/col map. + // This cell might span multiple rows and/or columns. In that case, we need + // to occupy multiple coordinates in the row/col map. + uint32_t lastRowForCell = + static_cast(rowIdx) + cell->RowExtent() - 1; + MOZ_ASSERT(lastRowForCell >= static_cast(rowIdx)); + uint32_t lastColForCell = colIdx + cell->ColExtent() - 1; + MOZ_ASSERT(lastColForCell >= colIdx); + for (uint32_t spannedRow = static_cast(rowIdx); + spannedRow <= lastRowForCell; ++spannedRow) { + for (uint32_t spannedCol = colIdx; spannedCol <= lastColForCell; + ++spannedCol) { + EnsureRowCol(spannedRow, spannedCol); + MOZ_ASSERT(mRowColToCellIdx[spannedRow][spannedCol] == kNoCellIdx); + auto& rowCol = mRowColToCellIdx[spannedRow][spannedCol]; + // If a cell already occupies this position, it overlaps with this one; + // e.g. r1..2c2 and r2c1..2. In that case, we want to prefer the first + // cell. + if (rowCol == kNoCellIdx) { + rowCol = cellIdx; + } + } + } + if (partRole == roles::COLUMNHEADER) { + for (uint32_t spannedCol = colIdx; spannedCol <= lastColForCell; + ++spannedCol) { + prevColHeaders.InsertOrUpdate(spannedCol, cellIdx); + } + } + // Increment for the next cell. + colIdx = lastColForCell + 1; + } +} + +void CachedTableAccessible::EnsureRow(uint32_t aRowIdx) { + for (uint32_t newRow = mRowColToCellIdx.Length(); newRow <= aRowIdx; + ++newRow) { + // The next row doesn't exist yet. Create it. + mRowColToCellIdx.AppendElement(); + } + MOZ_ASSERT(mRowColToCellIdx.Length() > aRowIdx); +} + +void CachedTableAccessible::EnsureRowCol(uint32_t aRowIdx, uint32_t aColIdx) { + EnsureRow(aRowIdx); + auto& row = mRowColToCellIdx[aRowIdx]; + for (uint32_t newCol = row.Length(); newCol <= aColIdx; ++newCol) { + // An entry doesn't yet exist for this column in this row. + row.AppendElement(kNoCellIdx); + } + MOZ_ASSERT(row.Length() > aColIdx); + if (mColCount <= aColIdx) { + ++mColCount; + } +} + +Accessible* CachedTableAccessible::Caption() const { + if (mCaptionAccID) { + Accessible* caption = nsAccUtils::GetAccessibleByID( + nsAccUtils::DocumentFor(mAcc), mCaptionAccID); + MOZ_ASSERT(caption, "Dead caption Accessible!"); + MOZ_ASSERT(caption->Role() != roles::CAPTION, "Caption has wrong role"); + return caption; + } + return nullptr; +} + +void CachedTableAccessible::Summary(nsString& aSummary) { + if (Caption()) { + // If there's a caption, we map caption to Name and summary to Description. + mAcc->Description(aSummary); + } else { + // If there's no caption, we map summary to Name. + mAcc->Name(aSummary); + } +} + +Accessible* CachedTableAccessible::CellAt(uint32_t aRowIdx, uint32_t aColIdx) { + int32_t cellIdx = CellIndexAt(aRowIdx, aColIdx); + if (cellIdx == -1) { + return nullptr; + } + 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(); +} + +/* static */ +CachedTableCellAccessible* CachedTableCellAccessible::GetFrom( + Accessible* aAcc) { + MOZ_ASSERT(aAcc->IsTableCell()); + for (Accessible* parent = aAcc; parent; parent = parent->Parent()) { + if (auto* table = + static_cast(parent->AsTableBase())) { + if (auto cellIdx = table->mAccToCellIdx.Lookup(aAcc)) { + return &table->mCells[*cellIdx]; + } + } + } + return nullptr; +} + +Accessible* CachedTableCellAccessible::Acc(Accessible* aTableAcc) const { + Accessible* acc = + nsAccUtils::GetAccessibleByID(nsAccUtils::DocumentFor(aTableAcc), mAccID); + MOZ_DIAGNOSTIC_ASSERT(acc == mAcc, "Cell's cached mAcc is dead!"); + return acc; +} + +TableAccessibleBase* 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()) { + return table; + } + } + return nullptr; +} + +uint32_t CachedTableCellAccessible::ColExtent() const { + if (RemoteAccessible* remoteAcc = mAcc->AsRemote()) { + if (remoteAcc->mCachedFields) { + if (auto colSpan = remoteAcc->mCachedFields->GetAttribute( + nsGkAtoms::colspan)) { + return *colSpan; + } + } + } else if (LocalAccessible* localAcc = 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); + return cell->ColExtent(); + } + return 1; +} + +uint32_t CachedTableCellAccessible::RowExtent() const { + if (RemoteAccessible* remoteAcc = mAcc->AsRemote()) { + if (remoteAcc->mCachedFields) { + if (auto rowSpan = remoteAcc->mCachedFields->GetAttribute( + nsGkAtoms::rowspan)) { + return *rowSpan; + } + } + } else if (LocalAccessible* localAcc = 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); + return cell->RowExtent(); + } + return 1; +} + +UniquePtr CachedTableCellAccessible::GetExplicitHeadersIterator() { + if (RemoteAccessible* remoteAcc = mAcc->AsRemote()) { + if (remoteAcc->mCachedFields) { + if (auto headers = + remoteAcc->mCachedFields->GetAttribute>( + nsGkAtoms::headers)) { + return MakeUnique(*headers, + remoteAcc->Document()); + } + } + } else if (LocalAccessible* localAcc = mAcc->AsLocal()) { + return MakeUnique( + localAcc->Document(), localAcc->GetContent(), nsGkAtoms::headers); + } + return nullptr; +} + +void CachedTableCellAccessible::ColHeaderCells(nsTArray* aCells) { + auto* table = static_cast(Table()); + if (!table) { + return; + } + if (auto iter = GetExplicitHeadersIterator()) { + while (Accessible* header = iter->Next()) { + role headerRole = header->Role(); + if (headerRole == roles::COLUMNHEADER) { + aCells->AppendElement(header); + } else if (headerRole != roles::ROWHEADER) { + // Treat this cell as a column header only if it's in the same column. + if (auto cellIdx = table->mAccToCellIdx.Lookup(header)) { + CachedTableCellAccessible& cell = table->mCells[*cellIdx]; + if (cell.ColIdx() == ColIdx()) { + aCells->AppendElement(header); + } + } + } + } + if (!aCells->IsEmpty()) { + return; + } + } + Accessible* doc = nsAccUtils::DocumentFor(table->AsAccessible()); + // Each cell stores its previous implicit column header, effectively forming a + // linked list. We traverse that to get all the headers. + CachedTableCellAccessible* cell = this; + for (;;) { + if (cell->mPrevColHeaderCellIdx == kNoCellIdx) { + break; // No more headers. + } + cell = &table->mCells[cell->mPrevColHeaderCellIdx]; + Accessible* cellAcc = nsAccUtils::GetAccessibleByID(doc, cell->mAccID); + aCells->AppendElement(cellAcc); + } +} + +void CachedTableCellAccessible::RowHeaderCells(nsTArray* aCells) { + auto* table = static_cast(Table()); + if (!table) { + return; + } + if (auto iter = GetExplicitHeadersIterator()) { + while (Accessible* header = iter->Next()) { + role headerRole = header->Role(); + if (headerRole == roles::ROWHEADER) { + aCells->AppendElement(header); + } else if (headerRole != roles::COLUMNHEADER) { + // Treat this cell as a row header only if it's in the same row. + if (auto cellIdx = table->mAccToCellIdx.Lookup(header)) { + CachedTableCellAccessible& cell = table->mCells[*cellIdx]; + if (cell.RowIdx() == RowIdx()) { + aCells->AppendElement(header); + } + } + } + } + if (!aCells->IsEmpty()) { + return; + } + } + // We don't cache implicit row headers because there are usually not that many + // cells per row. Get all the row headers on the row before this cell. + uint32_t row = RowIdx(); + uint32_t thisCol = ColIdx(); + for (uint32_t col = thisCol - 1; col < thisCol; --col) { + Accessible* cellAcc = table->CellAt(row, col); + if (!cellAcc) { + continue; + } + TableCellAccessibleBase* cell = cellAcc->AsTableCellBase(); + MOZ_ASSERT(cell); + // cell might span multiple columns. We don't want to visit it multiple + // times, so ensure col is set to cell's starting column. + col = cell->ColIdx(); + if (cellAcc->Role() != roles::ROWHEADER) { + continue; + } + aCells->AppendElement(cellAcc); + } +} + +bool CachedTableCellAccessible::Selected() { + return mAcc->State() & states::SELECTED; +} + +} // namespace mozilla::a11y diff -Nru firefox-99.0.1+build1/accessible/base/CachedTableAccessible.h firefox-100.0+build1/accessible/base/CachedTableAccessible.h --- firefox-99.0.1+build1/accessible/base/CachedTableAccessible.h 1970-01-01 00:00:00.000000000 +0000 +++ firefox-100.0+build1/accessible/base/CachedTableAccessible.h 2022-04-26 05:44:41.000000000 +0000 @@ -0,0 +1,299 @@ +/* -*- 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 CACHED_TABLE_ACCESSIBLE_H +#define CACHED_TABLE_ACCESSIBLE_H + +#include "mozilla/a11y/TableAccessibleBase.h" +#include "mozilla/a11y/TableCellAccessibleBase.h" +#include "mozilla/UniquePtr.h" +#include "nsTHashMap.h" + +namespace mozilla::a11y { + +const uint32_t kNoCellIdx = UINT32_MAX; + +class AccIterable; + +class CachedTableAccessible; + +class CachedTableCellAccessible final : public TableCellAccessibleBase { + public: + static CachedTableCellAccessible* GetFrom(Accessible* aAcc); + + virtual TableAccessibleBase* Table() const override; + + virtual uint32_t ColIdx() const override { + return static_cast(mColIdx); + } + + virtual uint32_t RowIdx() const override { + return static_cast(mRowIdx); + } + + 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; + + private: + CachedTableCellAccessible(uint64_t aAccID, Accessible* aAcc, uint32_t aRowIdx, + uint32_t aColIdx, uint32_t aPrevColHeaderCellIdx) + : mAccID(aAccID), + mAcc(aAcc), + mRowIdx(aRowIdx), + mColIdx(aColIdx), + mPrevColHeaderCellIdx(aPrevColHeaderCellIdx) {} + + // Get the Accessible for this table cell given its ancestor table Accessible, + // verifying that the Accessible is valid. + Accessible* Acc(Accessible* aTableAcc) const; + + UniquePtr GetExplicitHeadersIterator(); + + uint64_t mAccID; + // CachedTableAccessible methods which fetch a cell should retrieve the + // Accessible using Acc() rather than using mAcc. We need mAcc for some + // methods because we can't fetch a document by id. It's okay to use mAcc in + // these methods because the caller has to hold the Accessible in order to + // call them. + Accessible* mAcc; + uint32_t mRowIdx; + uint32_t mColIdx; + // The cell index of the previous implicit column header. + uint32_t mPrevColHeaderCellIdx; + friend class CachedTableAccessible; +}; + +/** + * TableAccessible implementation which builds and queries a cache. + */ +class CachedTableAccessible final : public TableAccessibleBase { + public: + static CachedTableAccessible* GetFrom(Accessible* aAcc); + + /** + * This must be called whenever a table is destroyed or the structure of a + * table changes; e.g. cells wer added or removed. It can be called with + * either a table or a cell. + */ + static void Invalidate(Accessible* aAcc); + + virtual Accessible* Caption() const override; + virtual void Summary(nsString& aSummary) override; + + virtual uint32_t ColCount() const override { return mColCount; } + + virtual uint32_t RowCount() override { return mRowColToCellIdx.Length(); } + + virtual int32_t ColIndexAt(uint32_t aCellIdx) override { + if (aCellIdx < mCells.Length()) { + return static_cast(mCells[aCellIdx].mColIdx); + } + return -1; + } + + virtual int32_t RowIndexAt(uint32_t aCellIdx) override { + if (aCellIdx < mCells.Length()) { + return static_cast(mCells[aCellIdx].mRowIdx); + } + return -1; + } + + virtual void RowAndColIndicesAt(uint32_t aCellIdx, int32_t* aRowIdx, + int32_t* aColIdx) override { + if (aCellIdx < mCells.Length()) { + CachedTableCellAccessible& cell = mCells[aCellIdx]; + *aRowIdx = static_cast(cell.mRowIdx); + *aColIdx = static_cast(cell.mColIdx); + return; + } + *aRowIdx = -1; + *aColIdx = -1; + } + + virtual uint32_t ColExtentAt(uint32_t aRowIdx, uint32_t aColIdx) override { + int32_t cellIdx = CellIndexAt(aRowIdx, aColIdx); + if (cellIdx == -1) { + return 0; + } + // Verify that the cell's Accessible is valid. + mCells[cellIdx].Acc(mAcc); + return mCells[cellIdx].ColExtent(); + } + + virtual uint32_t RowExtentAt(uint32_t aRowIdx, uint32_t aColIdx) override { + int32_t cellIdx = CellIndexAt(aRowIdx, aColIdx); + if (cellIdx == -1) { + return 0; + } + // Verify that the cell's Accessible is valid. + mCells[cellIdx].Acc(mAcc); + return mCells[cellIdx].RowExtent(); + } + + virtual int32_t CellIndexAt(uint32_t aRowIdx, uint32_t aColIdx) override { + if (aRowIdx < mRowColToCellIdx.Length()) { + auto& row = mRowColToCellIdx[aRowIdx]; + if (aColIdx < row.Length()) { + uint32_t cellIdx = row[aColIdx]; + if (cellIdx != kNoCellIdx) { + return static_cast(cellIdx); + } + } + } + return -1; + } + + virtual Accessible* CellAt(uint32_t aRowIdx, uint32_t aColIdx) override; + + virtual bool IsColSelected(uint32_t aColIdx) override { + bool selected = false; + for (uint32_t row = 0; row < RowCount(); ++row) { + selected = IsCellSelected(row, aColIdx); + if (!selected) { + break; + } + } + return selected; + } + + virtual bool IsRowSelected(uint32_t aRowIdx) override { + bool selected = false; + for (uint32_t col = 0; col < mColCount; ++col) { + selected = IsCellSelected(aRowIdx, col); + if (!selected) { + break; + } + } + return selected; + } + + virtual bool IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx) override { + int32_t cellIdx = CellIndexAt(aRowIdx, aColIdx); + if (cellIdx == -1) { + return false; + } + // Verify that the cell's Accessible is valid. + mCells[cellIdx].Acc(mAcc); + return mCells[cellIdx].Selected(); + } + + virtual uint32_t SelectedCellCount() override { + uint32_t count = 0; + for (auto& cell : mCells) { + // Verify that the cell's Accessible is valid. + cell.Acc(mAcc); + if (cell.Selected()) { + ++count; + } + } + return count; + } + + virtual uint32_t SelectedColCount() override { + uint32_t count = 0; + for (uint32_t col = 0; col < mColCount; ++col) { + if (IsColSelected(col)) { + ++count; + } + } + return count; + } + + virtual uint32_t SelectedRowCount() override { + uint32_t count = 0; + for (uint32_t row = 0; row < RowCount(); ++row) { + if (IsRowSelected(row)) { + ++count; + } + } + return count; + } + + virtual void SelectedCells(nsTArray* aCells) override { + for (auto& cell : mCells) { + // Verify that the cell's Accessible is valid. + Accessible* acc = cell.Acc(mAcc); + if (cell.Selected()) { + aCells->AppendElement(acc); + } + } + } + + virtual void SelectedCellIndices(nsTArray* aCells) override { + for (uint32_t idx = 0; idx < mCells.Length(); ++idx) { + CachedTableCellAccessible& cell = mCells[idx]; + // Verify that the cell's Accessible is valid. + cell.Acc(mAcc); + if (cell.Selected()) { + aCells->AppendElement(idx); + } + } + } + + virtual void SelectedColIndices(nsTArray* aCols) override { + for (uint32_t col = 0; col < mColCount; ++col) { + if (IsColSelected(col)) { + aCols->AppendElement(col); + } + } + } + + virtual void SelectedRowIndices(nsTArray* aRows) override { + for (uint32_t row = 0; row < RowCount(); ++row) { + if (IsRowSelected(row)) { + aRows->AppendElement(row); + } + } + } + + 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; + + private: + explicit CachedTableAccessible(Accessible* aAcc); + + // Ensure that the given row exists in our data structure, creating array + // elements as needed. + void EnsureRow(uint32_t aRowIdx); + + // Ensure that the given row and column coordinate exists in our data + // structure, creating array elements as needed. A newly created coordinate + // will be set to kNoCellIdx. + void EnsureRowCol(uint32_t aRowIdx, uint32_t aColIdx); + + Accessible* mAcc; // The table Accessible. + // We track the column count because it might not be uniform across rows in + // malformed tables. + uint32_t mColCount = 0; + // An array of cell instances. A cell index is an index into this array. + nsTArray mCells; + // Maps row and column coordinates to cell indices. + nsTArray> mRowColToCellIdx; + // Maps Accessibles to cell indexes to facilitate retrieval of a cell + // instance from a cell Accessible. The Accessible* keys should only be used + // for lookup. They should not be dereferenced. + nsTHashMap mAccToCellIdx; + uint64_t mCaptionAccID = 0; + + friend class CachedTableCellAccessible; +}; + +} // namespace mozilla::a11y + +#endif diff -Nru firefox-99.0.1+build1/accessible/base/moz.build firefox-100.0+build1/accessible/base/moz.build --- firefox-99.0.1+build1/accessible/base/moz.build 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/base/moz.build 2022-04-26 05:44:41.000000000 +0000 @@ -34,6 +34,7 @@ "ARIAMap.cpp", "ARIAStateMap.cpp", "Asserts.cpp", + "CachedTableAccessible.cpp", "DocManager.cpp", "EmbeddedObjCollector.cpp", "EventQueue.cpp", diff -Nru firefox-99.0.1+build1/accessible/base/nsAccessiblePivot.cpp firefox-100.0+build1/accessible/base/nsAccessiblePivot.cpp --- firefox-99.0.1+build1/accessible/base/nsAccessiblePivot.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/base/nsAccessiblePivot.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -472,12 +472,7 @@ } if (mPreFilter) { - uint64_t state; - if (aAcc->IsLocal()) { - state = aAcc->AsLocal()->State(); - } else { - state = aAcc->AsRemote()->State(); - } + uint64_t state = aAcc->State(); if ((nsIAccessibleTraversalRule::PREFILTER_PLATFORM_PRUNED & mPreFilter) && nsAccUtils::MustPrune(aAcc)) { @@ -499,11 +494,9 @@ return result; } - if (aAcc->IsLocal() && - (nsIAccessibleTraversalRule::PREFILTER_TRANSPARENT & mPreFilter) && - !(state & states::OPAQUE1)) { - nsIFrame* frame = aAcc->AsLocal()->GetFrame(); - if (frame->StyleEffects()->mOpacity == 0.0f) { + if (nsIAccessibleTraversalRule::PREFILTER_TRANSPARENT & mPreFilter) { + Maybe opacity = aAcc->Opacity(); + if (opacity && *opacity == 0.0f) { return result | nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE; } } @@ -524,9 +517,6 @@ uint16_t matchResult = nsIAccessibleTraversalRule::FILTER_IGNORE; - // XXX: ToXPC takes an Accessible. This can go away when pivot - // removes AoP too. - DebugOnly rv = mRule->Match(ToXPC(aAcc), &matchResult); MOZ_ASSERT(NS_SUCCEEDED(rv)); diff -Nru firefox-99.0.1+build1/accessible/base/nsAccUtils.cpp firefox-100.0+build1/accessible/base/nsAccUtils.cpp --- firefox-99.0.1+build1/accessible/base/nsAccUtils.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/base/nsAccUtils.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -11,6 +11,7 @@ #include "nsAccessibilityService.h" #include "nsCoreUtils.h" #include "DocAccessible.h" +#include "DocAccessibleParent.h" #include "HyperTextAccessible.h" #include "nsIAccessibleTypes.h" #include "Role.h" @@ -452,3 +453,28 @@ return false; } + +Accessible* nsAccUtils::DocumentFor(Accessible* aAcc) { + if (!aAcc) { + return nullptr; + } + if (LocalAccessible* localAcc = aAcc->AsLocal()) { + return localAcc->Document(); + } + return aAcc->AsRemote()->Document(); +} + +Accessible* nsAccUtils::GetAccessibleByID(Accessible* aDoc, uint64_t aID) { + if (!aDoc) { + return nullptr; + } + if (LocalAccessible* localAcc = aDoc->AsLocal()) { + if (DocAccessible* doc = localAcc->AsDoc()) { + return doc->GetAccessibleByUniqueID( + reinterpret_cast(static_cast(aID))); + } + } else if (DocAccessibleParent* doc = aDoc->AsRemote()->AsDoc()) { + return doc->GetAccessible(aID); + } + return nullptr; +} diff -Nru firefox-99.0.1+build1/accessible/base/nsAccUtils.h firefox-100.0+build1/accessible/base/nsAccUtils.h --- firefox-99.0.1+build1/accessible/base/nsAccUtils.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/base/nsAccUtils.h 2022-04-26 05:44:41.000000000 +0000 @@ -225,6 +225,23 @@ * the container-live attribute would be something other than "off" or empty. */ static bool IsARIALive(const LocalAccessible* aAccessible); + + /** + * Get the document Accessible which owns a given Accessible. + * This function is needed because there is no unified base class for local + * and remote documents. + * If aAcc is null, null will be returned. + */ + static Accessible* DocumentFor(Accessible* aAcc); + + /** + * Get an Accessible in a given document by its unique id. + * An Accessible's id can be obtained using Accessible::ID. + * This function is needed because there is no unified base class for local + * and remote documents. + * If aDoc is nul, null will be returned. + */ + static Accessible* GetAccessibleByID(Accessible* aDoc, uint64_t aID); }; } // namespace a11y diff -Nru firefox-99.0.1+build1/accessible/base/nsTextEquivUtils.cpp firefox-100.0+build1/accessible/base/nsTextEquivUtils.cpp --- firefox-99.0.1+build1/accessible/base/nsTextEquivUtils.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/base/nsTextEquivUtils.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -105,26 +105,6 @@ nsresult nsTextEquivUtils::AppendTextEquivFromTextContent(nsIContent* aContent, nsAString* aString) { if (aContent->IsText()) { - bool isHTMLBlock = false; - - nsIContent* parentContent = aContent->GetFlattenedTreeParent(); - if (parentContent) { - nsIFrame* frame = parentContent->GetPrimaryFrame(); - if (frame) { - // If this text is inside a block level frame (as opposed to span - // level), we need to add spaces around that block's text, so we don't - // get words jammed together in final name. - const nsStyleDisplay* display = frame->StyleDisplay(); - if (display->IsBlockOutsideStyle() || - display->mDisplay == StyleDisplay::TableCell) { - isHTMLBlock = true; - if (!aString->IsEmpty()) { - aString->Append(char16_t(' ')); - } - } - } - } - if (aContent->TextLength() > 0) { nsIFrame* frame = aContent->GetPrimaryFrame(); if (frame) { @@ -136,9 +116,6 @@ // If aContent is an object that is display: none, we have no a frame. aContent->GetAsText()->AppendTextTo(*aString); } - if (isHTMLBlock && !aString->IsEmpty()) { - aString->Append(char16_t(' ')); - } } return NS_OK; @@ -184,10 +161,27 @@ nsresult nsTextEquivUtils::AppendFromAccessible(Accessible* aAccessible, nsAString* aString) { // XXX: is it necessary to care the accessible is not a document? + bool isHTMLBlock = false; if (aAccessible->IsLocal() && aAccessible->AsLocal()->IsContent()) { - nsresult rv = AppendTextEquivFromTextContent( - aAccessible->AsLocal()->GetContent(), aString); + nsIContent* content = aAccessible->AsLocal()->GetContent(); + nsresult rv = AppendTextEquivFromTextContent(content, aString); if (rv != NS_OK_NO_NAME_CLAUSE_HANDLED) return rv; + if (!content->IsText()) { + nsIFrame* frame = content->GetPrimaryFrame(); + if (frame) { + // If this is a block level frame (as opposed to span level), we need to + // add spaces around that block's text, so we don't get words jammed + // together in final name. + const nsStyleDisplay* display = frame->StyleDisplay(); + if (display->IsBlockOutsideStyle() || + display->mDisplay == StyleDisplay::TableCell) { + isHTMLBlock = true; + if (!aString->IsEmpty()) { + aString->Append(char16_t(' ')); + } + } + } + } } bool isEmptyTextEquiv = true; @@ -220,9 +214,15 @@ // Implementation of h. step if (isEmptyTextEquiv && !text.IsEmpty()) { AppendString(aString, text); + if (isHTMLBlock) { + aString->Append(char16_t(' ')); + } return NS_OK; } + if (!isEmptyTextEquiv && isHTMLBlock) { + aString->Append(char16_t(' ')); + } return rv; } diff -Nru firefox-99.0.1+build1/accessible/base/Platform.h firefox-100.0+build1/accessible/base/Platform.h --- firefox-99.0.1+build1/accessible/base/Platform.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/base/Platform.h 2022-04-26 05:44:41.000000000 +0000 @@ -104,10 +104,11 @@ void ProxyFocusEvent(RemoteAccessible* aTarget, const LayoutDeviceIntRect& aCaretRect); void ProxyCaretMoveEvent(RemoteAccessible* aTarget, - const LayoutDeviceIntRect& aCaretRect); + const LayoutDeviceIntRect& aCaretRect, + int32_t aGranularity); #else void ProxyCaretMoveEvent(RemoteAccessible* aTarget, int32_t aOffset, - bool aIsSelectionCollapsed); + bool aIsSelectionCollapsed, int32_t aGranularity); #endif void ProxyTextChangeEvent(RemoteAccessible* aTarget, const nsString& aStr, int32_t aStart, uint32_t aLen, bool aIsInsert, diff -Nru firefox-99.0.1+build1/accessible/base/Relation.h firefox-100.0+build1/accessible/base/Relation.h --- firefox-99.0.1+build1/accessible/base/Relation.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/base/Relation.h 2022-04-26 05:44:41.000000000 +0000 @@ -75,7 +75,7 @@ * compute and return the next related accessible. */ inline LocalAccessible* Next() { - LocalAccessible* target = nullptr; + Accessible* target = nullptr; while (mFirstIter && !(target = mFirstIter->Next())) { mFirstIter = std::move(mFirstIter->mNextIter); @@ -83,7 +83,7 @@ if (!mFirstIter) mLastIter = nullptr; - return target; + return target ? target->AsLocal() : nullptr; } private: diff -Nru firefox-99.0.1+build1/accessible/base/SelectionManager.cpp firefox-100.0+build1/accessible/base/SelectionManager.cpp --- firefox-99.0.1+build1/accessible/base/SelectionManager.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/base/SelectionManager.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -24,10 +24,12 @@ using mozilla::dom::Selection; struct mozilla::a11y::SelData final { - SelData(Selection* aSel, int32_t aReason) : mSel(aSel), mReason(aReason) {} + SelData(Selection* aSel, int32_t aReason, int32_t aGranularity) + : mSel(aSel), mReason(aReason), mGranularity(aGranularity) {} RefPtr mSel; int16_t mReason; + int32_t mGranularity; NS_INLINE_DECL_REFCOUNTING(SelData) @@ -148,17 +150,18 @@ selection->FocusOffset()); mAccWithCaret = caretCntr; if (mCaretOffset != -1) { - RefPtr caretMoveEvent = new AccCaretMoveEvent( - caretCntr, mCaretOffset, selection->IsCollapsed(), - caretCntr->IsCaretAtEndOfLine(), aEvent->FromUserInput()); + RefPtr caretMoveEvent = + new AccCaretMoveEvent(caretCntr, mCaretOffset, selection->IsCollapsed(), + caretCntr->IsCaretAtEndOfLine(), + event->GetGranularity(), aEvent->FromUserInput()); nsEventShell::FireEvent(caretMoveEvent); } } NS_IMETHODIMP SelectionManager::NotifySelectionChanged(dom::Document* aDocument, - Selection* aSelection, - int16_t aReason) { + Selection* aSelection, int16_t aReason, + int32_t aAmount) { if (NS_WARN_IF(!aDocument) || NS_WARN_IF(!aSelection)) { return NS_ERROR_INVALID_ARG; } @@ -175,7 +178,7 @@ // Selection manager has longer lifetime than any document accessible, // so that we are guaranteed that the notification is processed before // the selection manager is destroyed. - RefPtr selData = new SelData(aSelection, aReason); + RefPtr selData = new SelData(aSelection, aReason, aAmount); document->HandleNotification( this, &SelectionManager::ProcessSelectionChanged, selData); } @@ -211,8 +214,8 @@ } if (selection->GetType() == SelectionType::eNormal) { - RefPtr event = - new AccTextSelChangeEvent(text, selection, aSelData->mReason); + RefPtr event = new AccTextSelChangeEvent( + text, selection, aSelData->mReason, aSelData->mGranularity); text->Document()->FireDelayedEvent(event); } else if (selection->GetType() == SelectionType::eSpellCheck) { diff -Nru firefox-99.0.1+build1/accessible/base/TextLeafRange.cpp firefox-100.0+build1/accessible/base/TextLeafRange.cpp --- firefox-99.0.1+build1/accessible/base/TextLeafRange.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/base/TextLeafRange.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -90,19 +90,6 @@ } }; -/** - * Get the document Accessible which owns a given Accessible. - * This function is needed because there is no unified base class for local and - * remote documents and thus there is no unified way to retrieve the document - * from an Accessible. - */ -static Accessible* DocumentFor(Accessible* aAcc) { - if (LocalAccessible* localAcc = aAcc->AsLocal()) { - return localAcc->Document(); - } - return aAcc->AsRemote()->Document(); -} - static HyperTextAccessible* HyperTextFor(LocalAccessible* aAcc) { for (LocalAccessible* acc = aAcc; acc; acc = acc->LocalParent()) { if (HyperTextAccessible* ht = acc->AsHyperText()) { @@ -113,14 +100,16 @@ } static Accessible* NextLeaf(Accessible* aOrigin) { - Accessible* doc = DocumentFor(aOrigin); + MOZ_ASSERT(aOrigin); + Accessible* doc = nsAccUtils::DocumentFor(aOrigin); Pivot pivot(doc); auto rule = LeafRule(); return pivot.Next(aOrigin, rule); } static Accessible* PrevLeaf(Accessible* aOrigin) { - Accessible* doc = DocumentFor(aOrigin); + MOZ_ASSERT(aOrigin); + Accessible* doc = nsAccUtils::DocumentFor(aOrigin); Pivot pivot(doc); auto rule = LeafRule(); return pivot.Prev(aOrigin, rule); @@ -957,7 +946,7 @@ } Accessible* prevLeaf = PrevLeaf(mAcc); BlockRule blockRule; - Pivot pivot(DocumentFor(mAcc)); + Pivot pivot(nsAccUtils::DocumentFor(mAcc)); Accessible* prevBlock = pivot.Prev(mAcc, blockRule); // Check if we're the first leaf after a block element. if (prevBlock && @@ -999,9 +988,11 @@ HyperTextAccessible* hyperAcc = parent->AsHyperText(); MOZ_ASSERT(hyperAcc); RefPtr attributes = new AccAttributes(); - TextAttrsMgr mgr(hyperAcc, aIncludeDefaults, acc, - acc ? acc->IndexInParent() : -1); - mgr.GetAttributes(attributes, nullptr, nullptr); + if (hyperAcc) { + TextAttrsMgr mgr(hyperAcc, aIncludeDefaults, acc, + acc ? acc->IndexInParent() : -1); + mgr.GetAttributes(attributes, nullptr, nullptr); + } return attributes.forget(); } diff -Nru firefox-99.0.1+build1/accessible/basetypes/Accessible.h firefox-100.0+build1/accessible/basetypes/Accessible.h --- firefox-99.0.1+build1/accessible/basetypes/Accessible.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/basetypes/Accessible.h 2022-04-26 05:44:41.000000000 +0000 @@ -24,6 +24,8 @@ class HyperTextAccessibleBase; class LocalAccessible; class RemoteAccessible; +class TableAccessibleBase; +class TableCellAccessibleBase; /** * Name type flags. @@ -74,6 +76,13 @@ uint8_t aRoleMapEntryIndex); public: + /** + * Return an id for this Accessible which is unique within the document. + * Use nsAccUtils::GetAccessibleByID to retrieve an Accessible given an id + * returned from this method. + */ + virtual uint64_t ID() const = 0; + virtual Accessible* Parent() const = 0; virtual role Role() const = 0; @@ -220,6 +229,8 @@ virtual already_AddRefed DisplayStyle() const = 0; + virtual Maybe Opacity() const = 0; + // Methods that interact with content. virtual void TakeFocus() const = 0; @@ -423,6 +434,9 @@ virtual HyperTextAccessibleBase* AsHyperTextBase() { return nullptr; } + virtual TableAccessibleBase* AsTableBase() { return nullptr; } + virtual TableCellAccessibleBase* AsTableCellBase() { return nullptr; } + /** * Return the localized string for the given key. */ diff -Nru firefox-99.0.1+build1/accessible/basetypes/moz.build firefox-100.0+build1/accessible/basetypes/moz.build --- firefox-99.0.1+build1/accessible/basetypes/moz.build 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/basetypes/moz.build 2022-04-26 05:44:41.000000000 +0000 @@ -7,6 +7,8 @@ EXPORTS.mozilla.a11y += [ "Accessible.h", "HyperTextAccessibleBase.h", + "TableAccessibleBase.h", + "TableCellAccessibleBase.h", ] UNIFIED_SOURCES += [ diff -Nru firefox-99.0.1+build1/accessible/basetypes/TableAccessibleBase.h firefox-100.0+build1/accessible/basetypes/TableAccessibleBase.h --- firefox-99.0.1+build1/accessible/basetypes/TableAccessibleBase.h 1970-01-01 00:00:00.000000000 +0000 +++ firefox-100.0+build1/accessible/basetypes/TableAccessibleBase.h 2022-04-26 05:44:41.000000000 +0000 @@ -0,0 +1,192 @@ +/* -*- 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-99.0.1+build1/accessible/basetypes/TableCellAccessibleBase.h firefox-100.0+build1/accessible/basetypes/TableCellAccessibleBase.h --- firefox-99.0.1+build1/accessible/basetypes/TableCellAccessibleBase.h 1970-01-01 00:00:00.000000000 +0000 +++ firefox-100.0+build1/accessible/basetypes/TableCellAccessibleBase.h 2022-04-26 05:44:41.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_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-99.0.1+build1/accessible/generic/ARIAGridAccessible.cpp firefox-100.0+build1/accessible/generic/ARIAGridAccessible.cpp --- firefox-99.0.1+build1/accessible/generic/ARIAGridAccessible.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/generic/ARIAGridAccessible.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -219,7 +219,7 @@ return count; } -void ARIAGridAccessible::SelectedCells(nsTArray* aCells) { +void ARIAGridAccessible::SelectedCells(nsTArray* aCells) { if (IsARIARole(nsGkAtoms::table)) return; AccIterator rowIter(this, filters::GetRow); diff -Nru firefox-99.0.1+build1/accessible/generic/ARIAGridAccessible.h firefox-100.0+build1/accessible/generic/ARIAGridAccessible.h --- firefox-99.0.1+build1/accessible/generic/ARIAGridAccessible.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/generic/ARIAGridAccessible.h 2022-04-26 05:44:41.000000000 +0000 @@ -40,7 +40,7 @@ virtual uint32_t SelectedCellCount() override; virtual uint32_t SelectedColCount() override; virtual uint32_t SelectedRowCount() override; - virtual void SelectedCells(nsTArray* aCells) 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; diff -Nru firefox-99.0.1+build1/accessible/generic/DocAccessible.cpp firefox-100.0+build1/accessible/generic/DocAccessible.cpp --- firefox-99.0.1+build1/accessible/generic/DocAccessible.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/generic/DocAccessible.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -7,6 +7,7 @@ #include "LocalAccessible-inl.h" #include "AccIterator.h" #include "AccAttributes.h" +#include "CachedTableAccessible.h" #include "DocAccessible-inl.h" #include "DocAccessibleChild.h" #include "HTMLImageMapAccessible.h" @@ -449,6 +450,10 @@ } mChildDocuments.Clear(); + // mQueuedCacheUpdates can contain a reference to this document (ex. if the + // doc is scrollable and we're sending a scroll position update). Clear the + // map here to avoid creating ref cycles. + mQueuedCacheUpdates.Clear(); // XXX thinking about ordering? if (mIPCDoc) { @@ -616,6 +621,13 @@ } void DocAccessible::HandleScroll(nsINode* aTarget) { + // Regardless of our scroll timer, we need to send a cache update + // to ensure the next Bounds() query accurately reflects our position + // after scrolling. + if (LocalAccessible* scrollTarget = GetAccessible(aTarget)) { + QueueCacheUpdate(scrollTarget, CacheDomain::ScrollPosition); + } + const uint32_t kScrollEventInterval = 100; // If we haven't dispatched a scrolling event for a target in at least // kScrollEventInterval milliseconds, dispatch one now. @@ -648,6 +660,30 @@ } } +std::pair DocAccessible::ComputeScrollData( + LocalAccessible* aAcc) { + nsPoint scrollPoint; + nsRect scrollRange; + + if (nsIFrame* frame = aAcc->GetFrame()) { + nsIScrollableFrame* sf = aAcc == this + ? mPresShell->GetRootScrollFrameAsScrollable() + : frame->GetScrollTargetFrame(); + + // If there is no scrollable frame, it's likely a scroll in a popup, like + // . Just send an event with no scroll info. The scroll info - // is currently only used on Android, and popups are rendered natively - // there. - if (sf) { - int32_t appUnitsPerDevPixel = - mPresShell->GetPresContext()->AppUnitsPerDevPixel(); - scrollPoint = LayoutDevicePoint::FromAppUnits(sf->GetScrollPosition(), - appUnitsPerDevPixel) * - mPresShell->GetResolution(); - - scrollRange = LayoutDeviceRect::FromAppUnits(sf->GetScrollRange(), - appUnitsPerDevPixel); - scrollRange.ScaleRoundOut(mPresShell->GetResolution()); - } + auto [scrollPoint, scrollRange] = ComputeScrollData(acc); + + int32_t appUnitsPerDevPixel = + mPresShell->GetPresContext()->AppUnitsPerDevPixel(); + + LayoutDeviceIntPoint scrollPointDP = LayoutDevicePoint::FromAppUnitsToNearest( + scrollPoint, appUnitsPerDevPixel); + LayoutDeviceIntRect scrollRangeDP = + LayoutDeviceRect::FromAppUnitsToNearest(scrollRange, appUnitsPerDevPixel); RefPtr event = - new AccScrollingEvent(aEventType, acc, scrollPoint.x, scrollPoint.y, - scrollRange.width, scrollRange.height); + new AccScrollingEvent(aEventType, acc, scrollPointDP.x, scrollPointDP.y, + scrollRangeDP.width, scrollRangeDP.height); nsEventShell::FireEvent(event); } diff -Nru firefox-99.0.1+build1/accessible/generic/DocAccessible.h firefox-100.0+build1/accessible/generic/DocAccessible.h --- firefox-99.0.1+build1/accessible/generic/DocAccessible.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/generic/DocAccessible.h 2022-04-26 05:44:41.000000000 +0000 @@ -387,10 +387,19 @@ /** * Notify the document that a DOM node has been scrolled. document will * dispatch throttled accessibility events for scrolling, and a scroll-end - * event. + * event. This function also queues a cache update for ScrollPosition. */ void HandleScroll(nsINode* aTarget); + /** + * Retrieves the scroll frame (if it exists) for the given accessible + * and returns its scroll position and scroll range. If the given + * accessible is `this`, return the scroll position and range of + * the root scroll frame. Return values have been scaled by the + * PresShell's resolution. + */ + std::pair ComputeScrollData(LocalAccessible* aAcc); + protected: virtual ~DocAccessible(); @@ -761,11 +770,11 @@ // Exclusively owned by IPDL so don't manually delete it! DocAccessibleChild* mIPCDoc; - nsTHashSet> mMaybeBoundsChanged; - // A hash map between LocalAccessibles and CacheDomains, tracking // cache updates that have been queued during the current tick - // but not yet sent. + // but not yet sent. It is possible for this map to contain a reference + // to the document it lives on. We clear the list in Shutdown() to + // avoid cyclical references. nsTHashMap, uint64_t> mQueuedCacheUpdates; // A set of Accessibles moved during this tick. Only used in content diff -Nru firefox-99.0.1+build1/accessible/generic/LocalAccessible.cpp firefox-100.0+build1/accessible/generic/LocalAccessible.cpp --- firefox-99.0.1+build1/accessible/generic/LocalAccessible.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/generic/LocalAccessible.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -10,6 +10,7 @@ #include "AccGroupInfo.h" #include "AccIterator.h" #include "CacheConstants.h" +#include "CachedTableAccessible.h" #include "DocAccessible-inl.h" #include "nsAccUtils.h" #include "nsAccessibilityService.h" @@ -934,9 +935,9 @@ } case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED: { AccCaretMoveEvent* event = downcast_accEvent(aEvent); - ipcDoc->SendCaretMoveEvent(id, event->GetCaretOffset(), - event->IsSelectionCollapsed(), - event->IsAtEndOfLine()); + ipcDoc->SendCaretMoveEvent( + id, event->GetCaretOffset(), event->IsSelectionCollapsed(), + event->IsAtEndOfLine(), event->GetGranularity()); break; } case nsIAccessibleEvent::EVENT_TEXT_INSERTED: @@ -1544,7 +1545,8 @@ nsIFrame* frame = GetFrame(); if (!frame) return state; - if (frame->StyleEffects()->mOpacity == 1.0f && !(state & states::INVISIBLE)) { + Maybe opacity = Opacity(); + if (opacity && *opacity == 1.0f && !(state & states::INVISIBLE)) { state |= states::OPAQUE1; } @@ -2497,18 +2499,28 @@ static_cast((mParent->IsAlert() || mParent->IsInsideAlert())) & eInsideAlert; - // if a new column header is being added, invalidate the table's header cache. - TableCellAccessible* cell = AsTableCell(); - if (cell && Role() == roles::COLUMNHEADER) { - TableAccessible* table = cell->Table(); - if (table) { - table->GetHeaderCache().Clear(); + if (TableCellAccessible* cell = AsTableCell()) { + if (StaticPrefs::accessibility_cache_enabled_AtStartup()) { + CachedTableAccessible::Invalidate(this); + } else if (Role() == roles::COLUMNHEADER) { + // A new column header is being added. Invalidate the table's header + // cache. + TableAccessible* table = cell->Table(); + if (table) { + table->GetHeaderCache().Clear(); + } } } } // LocalAccessible protected void LocalAccessible::UnbindFromParent() { + // Usually, when a subtree is removed, we do this in + // DocAccessible::UncacheChildrenInSubtree. However, that won't get called + // when the document is shut down, so we handle that here. + if (StaticPrefs::accessibility_cache_enabled_AtStartup() && IsTable()) { + CachedTableAccessible::Invalidate(this); + } mParent = nullptr; mIndexInParent = -1; mIndexOfEmbeddedChild = -1; @@ -3234,10 +3246,8 @@ fields->SetAttribute(nsGkAtoms::style, std::move(attrs)); } } - + nsIFrame* frame = GetFrame(); if (aCacheDomain & CacheDomain::TransformMatrix) { - nsIFrame* frame = GetFrame(); - if (frame && frame->IsTransformed()) { // We need to find a frame to make our transform relative to. // It's important this frame have a corresponding accessible, @@ -3263,6 +3273,16 @@ } } + if (aCacheDomain & CacheDomain::ScrollPosition) { + nsPoint scrollPosition; + std::tie(scrollPosition, std::ignore) = mDoc->ComputeScrollData(this); + + nsTArray positionArr(2); + positionArr.AppendElement(scrollPosition.x); + positionArr.AppendElement(scrollPosition.y); + fields->SetAttribute(nsGkAtoms::scrollPosition, std::move(positionArr)); + } + if (aCacheDomain & CacheDomain::DOMNodeID && mContent) { nsAtom* id = mContent->GetID(); if (id) { @@ -3319,6 +3339,54 @@ if (RefPtr display = DisplayStyle()) { fields->SetAttribute(nsGkAtoms::display, display); } + + Maybe opacity = Opacity(); + if (opacity && !(NativeState() & states::INVISIBLE)) { + fields->SetAttribute(nsGkAtoms::opacity, *opacity); + } else { + fields->SetAttribute(nsGkAtoms::opacity, DeleteEntry()); + } + } + + if (aCacheDomain & CacheDomain::Table) { + if (IsTable()) { + TableAccessible* table = AsTable(); + 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()) { + // 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 + // about thead, tbody, etc., which is info we don't have in the a11y tree. + int32_t value = static_cast(cell->RowExtent()); + if (value != 1) { + fields->SetAttribute(nsGkAtoms::rowspan, value); + } else if (aUpdateType == CacheUpdateType::Update) { + fields->SetAttribute(nsGkAtoms::rowspan, DeleteEntry()); + } + value = static_cast(cell->ColExtent()); + if (value != 1) { + fields->SetAttribute(nsGkAtoms::colspan, value); + } else if (aUpdateType == CacheUpdateType::Update) { + fields->SetAttribute(nsGkAtoms::colspan, DeleteEntry()); + } + if (mContent->AsElement()->HasAttr(kNameSpaceID_None, + nsGkAtoms::headers)) { + nsTArray headers; + IDRefsIterator iter(mDoc, mContent, nsGkAtoms::headers); + while (LocalAccessible* cell = iter.Next()) { + if (cell->IsTableCell()) { + headers.AppendElement(cell->ID()); + } + } + fields->SetAttribute(nsGkAtoms::headers, std::move(headers)); + } else { + fields->SetAttribute(nsGkAtoms::headers, DeleteEntry()); + } + } } if (aUpdateType == CacheUpdateType::Initial) { @@ -3350,6 +3418,15 @@ mStateFlags &= ~eOldFrameHasValidTransformStyle; } } + + if (IsDoc()) { + if (PresShell* presShell = AsDoc()->PresShellPtr()) { + // Send the initial resolution of the document. When this changes, we + // will ne notified via nsAS::NotifyOfResolutionChange + float resolution = presShell->GetResolution(); + fields->SetAttribute(nsGkAtoms::resolution, resolution); + } + } } return fields.forget(); @@ -3366,10 +3443,19 @@ if (nsIFrame* frame = GetFrame()) { const ComputedStyle* newStyle = frame->Style(); - nsAutoCString oldVal, newVal; - mOldComputedStyle->GetComputedPropertyValue(eCSSProperty_display, oldVal); - newStyle->GetComputedPropertyValue(eCSSProperty_display, newVal); - if (oldVal != newVal) { + nsAutoCString oldDisplay, newDisplay; + mOldComputedStyle->GetComputedPropertyValue(eCSSProperty_display, + oldDisplay); + newStyle->GetComputedPropertyValue(eCSSProperty_display, newDisplay); + + nsAutoCString oldOpacity, newOpacity; + mOldComputedStyle->GetComputedPropertyValue(eCSSProperty_opacity, + oldOpacity); + newStyle->GetComputedPropertyValue(eCSSProperty_opacity, newOpacity); + + if (oldDisplay != newDisplay || oldOpacity != newOpacity) { + // CacheDomain::Style covers both display and opacity, so if + // either property has changed, send an update for the entire domain. mDoc->QueueCacheUpdate(this, CacheDomain::Style); } @@ -3422,6 +3508,14 @@ return nullptr; } +Maybe LocalAccessible::Opacity() const { + if (nsIFrame* frame = GetFrame()) { + return Some(frame->StyleEffects()->mOpacity); + } + + return Nothing(); +} + void LocalAccessible::StaticAsserts() const { static_assert( eLastStateFlag <= (1 << kStateFlagsBits) - 1, @@ -3510,3 +3604,25 @@ aValue.Append(mKey); } + +TableAccessibleBase* LocalAccessible::AsTableBase() { + if (StaticPrefs::accessibility_cache_enabled_AtStartup() && IsTable() && + !mContent->IsXULElement()) { + // This isn't strictly related to caching, but this new table implementation + // is being developed to make caching feasible. We put it behind this pref + // to make it easy to test while it's still under development. + return CachedTableAccessible::GetFrom(this); + } + return AsTable(); +} + +TableCellAccessibleBase* LocalAccessible::AsTableCellBase() { + if (StaticPrefs::accessibility_cache_enabled_AtStartup() && IsTableCell() && + !mContent->IsXULElement()) { + // This isn't strictly related to caching, but this new table implementation + // is being developed to make caching feasible. We put it behind this pref + // to make it easy to test while it's still under development. + return CachedTableCellAccessible::GetFrom(this); + } + return AsTableCell(); +} diff -Nru firefox-99.0.1+build1/accessible/generic/LocalAccessible.h firefox-100.0+build1/accessible/generic/LocalAccessible.h --- firefox-99.0.1+build1/accessible/generic/LocalAccessible.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/generic/LocalAccessible.h 2022-04-26 05:44:41.000000000 +0000 @@ -53,7 +53,9 @@ class Relation; class RootAccessible; class TableAccessible; +class TableAccessibleBase; class TableCellAccessible; +class TableCellAccessibleBase; class TextLeafAccessible; class XULLabelAccessible; class XULTreeAccessible; @@ -124,9 +126,15 @@ /** * Return the unique identifier of the accessible. + * ID() should be preferred, but this method still exists because many + * LocalAccessible callers expect a void*. */ void* UniqueID() { return static_cast(this); } + virtual uint64_t ID() const override { + return reinterpret_cast(this); + } + /** * Return language associated with the accessible. */ @@ -494,6 +502,9 @@ return const_cast(this)->AsTableCell(); } + virtual TableAccessibleBase* AsTableBase() override; + virtual TableCellAccessibleBase* AsTableCellBase() override; + TextLeafAccessible* AsTextLeaf(); XULLabelAccessible* AsXULLabel(); @@ -784,6 +795,8 @@ virtual already_AddRefed DisplayStyle() const override; + virtual Maybe Opacity() const override; + protected: virtual ~LocalAccessible(); diff -Nru firefox-99.0.1+build1/accessible/generic/TableAccessible.h firefox-100.0+build1/accessible/generic/TableAccessible.h --- firefox-99.0.1+build1/accessible/generic/TableAccessible.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/generic/TableAccessible.h 2022-04-26 05:44:41.000000000 +0000 @@ -7,189 +7,40 @@ #ifndef TABLE_ACCESSIBLE_H #define TABLE_ACCESSIBLE_H -#include "TableCellAccessible.h" +#include "LocalAccessible.h" +#include "mozilla/a11y/TableAccessibleBase.h" +#include "mozilla/a11y/TableCellAccessibleBase.h" #include "nsPointerHashKeys.h" #include "nsRefPtrHashtable.h" -#include "nsString.h" -#include "nsTArray.h" namespace mozilla { namespace a11y { -class LocalAccessible; - /** - * Accessible table interface. + * Base class for LocalAccessible table implementations. */ -class TableAccessible { +class TableAccessible : public TableAccessibleBase { public: - /** - * Return the caption accessible if any for this table. - */ - virtual LocalAccessible* 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; } + virtual LocalAccessible* Caption() const override { return nullptr; } - /** - * Return the accessible for the cell at the given row and column indices. - */ - virtual LocalAccessible* CellAt(uint32_t aRowIdx, uint32_t aColIdx) { + virtual LocalAccessible* CellAt(uint32_t aRowIdx, uint32_t aColIdx) override { return nullptr; } - /** - * Return the index of the cell at the given row and column. - */ - virtual int32_t CellIndexAt(uint32_t aRowIdx, uint32_t aColIdx) { + virtual int32_t CellIndexAt(uint32_t aRowIdx, uint32_t aColIdx) override { return ColCount() * aRowIdx + aColIdx; } - /** - * 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 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); - - /** - * 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 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); - - /** - * 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) = 0; - - /** - * Get the set of selected cell indices. - */ - virtual void SelectedCellIndices(nsTArray* aCells) = 0; - - /** - * Get the set of selected column indices. - */ - virtual void SelectedColIndices(nsTArray* aCols) = 0; - - /** - * Get the set of selected row indices. - */ - virtual void SelectedRowIndices(nsTArray* aRows) = 0; - - /** - * 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(); - - /** - * Convert the table to an Accessible*. - */ - virtual LocalAccessible* AsAccessible() = 0; - - typedef nsRefPtrHashtable, - LocalAccessible> - HeaderCache; + 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 diff -Nru firefox-99.0.1+build1/accessible/generic/TableCellAccessible.cpp firefox-100.0+build1/accessible/generic/TableCellAccessible.cpp --- firefox-99.0.1+build1/accessible/generic/TableCellAccessible.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/generic/TableCellAccessible.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -12,7 +12,7 @@ using namespace mozilla; using namespace mozilla::a11y; -void TableCellAccessible::RowHeaderCells(nsTArray* aCells) { +void TableCellAccessible::RowHeaderCells(nsTArray* aCells) { uint32_t rowIdx = RowIdx(), colIdx = ColIdx(); TableAccessible* table = Table(); if (!table) return; @@ -106,7 +106,7 @@ return nullptr; } -void TableCellAccessible::ColHeaderCells(nsTArray* aCells) { +void TableCellAccessible::ColHeaderCells(nsTArray* aCells) { for (LocalAccessible* cell = PrevColHeader(); cell; cell = cell->AsTableCell()->PrevColHeader()) { aCells->AppendElement(cell); diff -Nru firefox-99.0.1+build1/accessible/generic/TableCellAccessible.h firefox-100.0+build1/accessible/generic/TableCellAccessible.h --- firefox-99.0.1+build1/accessible/generic/TableCellAccessible.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/generic/TableCellAccessible.h 2022-04-26 05:44:41.000000000 +0000 @@ -7,59 +7,22 @@ #ifndef mozilla_a11y_TableCellAccessible_h__ #define mozilla_a11y_TableCellAccessible_h__ -#include "nsTArray.h" -#include +#include "mozilla/a11y/TableCellAccessibleBase.h" +#include "TableAccessible.h" namespace mozilla { namespace a11y { class LocalAccessible; -class TableAccessible; /** - * Abstract interface implemented by table cell accessibles. + * Base class for LocalAccessible table cell implementations. */ -class TableCellAccessible { +class TableCellAccessible : public TableCellAccessibleBase { 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); - - /** - * Return the row header cells for this cell. - */ - virtual void RowHeaderCells(nsTArray* aCells); - - /** - * Returns true if this cell is selected. - */ - virtual bool Selected() = 0; + virtual TableAccessible* Table() const override = 0; + virtual void ColHeaderCells(nsTArray* aCells) override; + virtual void RowHeaderCells(nsTArray* aCells) override; private: LocalAccessible* PrevColHeader(); diff -Nru firefox-99.0.1+build1/accessible/html/HTMLImageMapAccessible.cpp firefox-100.0+build1/accessible/html/HTMLImageMapAccessible.cpp --- firefox-99.0.1+build1/accessible/html/HTMLImageMapAccessible.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/html/HTMLImageMapAccessible.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -51,7 +51,9 @@ if (!area) return nullptr; nsIContent* linkContent = area->GetContent(); - return linkContent ? linkContent->GetHrefURI() : nullptr; + return linkContent && linkContent->IsElement() + ? linkContent->AsElement()->GetHrefURI() + : nullptr; } //////////////////////////////////////////////////////////////////////////////// diff -Nru firefox-99.0.1+build1/accessible/html/HTMLLinkAccessible.cpp firefox-100.0+build1/accessible/html/HTMLLinkAccessible.cpp --- firefox-99.0.1+build1/accessible/html/HTMLLinkAccessible.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/html/HTMLLinkAccessible.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -121,7 +121,7 @@ already_AddRefed HTMLLinkAccessible::AnchorURIAt( uint32_t aAnchorIndex) const { - return aAnchorIndex == 0 ? mContent->GetHrefURI() : nullptr; + return aAnchorIndex == 0 ? mContent->AsElement()->GetHrefURI() : nullptr; } //////////////////////////////////////////////////////////////////////////////// diff -Nru firefox-99.0.1+build1/accessible/html/HTMLTableAccessible.cpp firefox-100.0+build1/accessible/html/HTMLTableAccessible.cpp --- firefox-99.0.1+build1/accessible/html/HTMLTableAccessible.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/html/HTMLTableAccessible.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -10,6 +10,7 @@ #include "nsAccessibilityService.h" #include "nsAccUtils.h" #include "AccAttributes.h" +#include "CacheConstants.h" #include "DocAccessible.h" #include "LocalAccessible-inl.h" #include "nsTextEquivUtils.h" @@ -150,6 +151,10 @@ aAttribute == nsGkAtoms::scope) { mDoc->FireDelayedEvent(nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED, this); + mDoc->QueueCacheUpdate(this, CacheDomain::Table); + } else if (aAttribute == nsGkAtoms::rowspan || + aAttribute == nsGkAtoms::colspan) { + mDoc->QueueCacheUpdate(this, CacheDomain::Table); } } @@ -199,8 +204,7 @@ return table->RowExtentAt(rowIdx, colIdx); } -void HTMLTableCellAccessible::ColHeaderCells( - nsTArray* aCells) { +void HTMLTableCellAccessible::ColHeaderCells(nsTArray* aCells) { IDRefsIterator itr(mDoc, mContent, nsGkAtoms::headers); while (LocalAccessible* cell = itr.Next()) { a11y::role cellRole = cell->Role(); @@ -219,8 +223,7 @@ if (aCells->IsEmpty()) TableCellAccessible::ColHeaderCells(aCells); } -void HTMLTableCellAccessible::RowHeaderCells( - nsTArray* aCells) { +void HTMLTableCellAccessible::RowHeaderCells(nsTArray* aCells) { IDRefsIterator itr(mDoc, mContent, nsGkAtoms::headers); while (LocalAccessible* cell = itr.Next()) { a11y::role cellRole = cell->Role(); @@ -525,7 +528,7 @@ return count; } -void HTMLTableAccessible::SelectedCells(nsTArray* aCells) { +void HTMLTableAccessible::SelectedCells(nsTArray* aCells) { nsTableWrapperFrame* tableFrame = GetTableWrapperFrame(); if (!tableFrame) return; diff -Nru firefox-99.0.1+build1/accessible/html/HTMLTableAccessible.h firefox-100.0+build1/accessible/html/HTMLTableAccessible.h --- firefox-99.0.1+build1/accessible/html/HTMLTableAccessible.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/html/HTMLTableAccessible.h 2022-04-26 05:44:41.000000000 +0000 @@ -52,8 +52,8 @@ 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 void ColHeaderCells(nsTArray* aCells) override; + virtual void RowHeaderCells(nsTArray* aCells) override; virtual bool Selected() override; protected: @@ -152,7 +152,7 @@ virtual uint32_t SelectedCellCount() override; virtual uint32_t SelectedColCount() override; virtual uint32_t SelectedRowCount() override; - virtual void SelectedCells(nsTArray* aCells) 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; diff -Nru firefox-99.0.1+build1/accessible/interfaces/nsIAccessibleCaretMoveEvent.idl firefox-100.0+build1/accessible/interfaces/nsIAccessibleCaretMoveEvent.idl --- firefox-99.0.1+build1/accessible/interfaces/nsIAccessibleCaretMoveEvent.idl 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/interfaces/nsIAccessibleCaretMoveEvent.idl 2022-04-26 05:44:41.000000000 +0000 @@ -25,4 +25,9 @@ * Return true if the caret is at the end of a line. */ readonly attribute bool isAtEndOfLine; + + /** + * Return caret move granularity. + */ + readonly attribute long granularity; }; diff -Nru firefox-99.0.1+build1/accessible/interfaces/nsIAccessible.idl firefox-100.0+build1/accessible/interfaces/nsIAccessible.idl --- firefox-99.0.1+build1/accessible/interfaces/nsIAccessible.idl 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/interfaces/nsIAccessible.idl 2022-04-26 05:44:41.000000000 +0000 @@ -182,6 +182,11 @@ readonly attribute nsIPersistentProperties attributes; /** + * Cached fields from a remote accessible + */ + readonly attribute nsIPersistentProperties cache; + + /** * Platform specific interface for accessible */ readonly attribute nsISupports nativeInterface; diff -Nru firefox-99.0.1+build1/accessible/ipc/DocAccessibleParent.cpp firefox-100.0+build1/accessible/ipc/DocAccessibleParent.cpp --- firefox-99.0.1+build1/accessible/ipc/DocAccessibleParent.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/ipc/DocAccessibleParent.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -4,6 +4,7 @@ * 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 "CachedTableAccessible.h" #include "DocAccessibleParent.h" #include "mozilla/a11y/Platform.h" #include "mozilla/dom/BrowserBridgeParent.h" @@ -162,6 +163,10 @@ }); } + if (newProxy->IsTableCell()) { + CachedTableAccessible::Invalidate(newProxy); + } + DebugOnly isOuterDoc = newProxy->ChildCount() == 1; uint32_t accessibles = 1; @@ -188,6 +193,9 @@ // This is a move. Moves are sent as a hide and then a show, but for a move, // we want to keep the Accessible alive for reuse later. aAcc->SetParent(nullptr); + if (aAcc->IsTable() || aAcc->IsTableCell()) { + CachedTableAccessible::Invalidate(aAcc); + } mMovingIDs.EnsureRemoved(id); if (aAcc->IsOuterDoc()) { // Leave child documents alone. They are added and removed differently to @@ -341,7 +349,7 @@ const LayoutDeviceIntRect& aCaretRect, #endif // defined (XP_WIN) const int32_t& aOffset, const bool& aIsSelectionCollapsed, - const bool& aIsAtEndOfLine) { + const bool& aIsAtEndOfLine, const int32_t& aGranularity) { if (mShutdown) { return IPC_OK(); } @@ -364,9 +372,9 @@ } #if defined(XP_WIN) - ProxyCaretMoveEvent(proxy, aCaretRect); + ProxyCaretMoveEvent(proxy, aCaretRect, aGranularity); #else - ProxyCaretMoveEvent(proxy, aOffset, aIsSelectionCollapsed); + ProxyCaretMoveEvent(proxy, aOffset, aIsSelectionCollapsed, aGranularity); #endif if (!nsCoreUtils::AccEventObserversExist()) { @@ -378,9 +386,9 @@ nsINode* node = nullptr; bool fromUser = true; // XXX fix me uint32_t type = nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED; - RefPtr event = - new xpcAccCaretMoveEvent(type, xpcAcc, doc, node, fromUser, aOffset, - aIsSelectionCollapsed, aIsAtEndOfLine); + RefPtr event = new xpcAccCaretMoveEvent( + type, xpcAcc, doc, node, fromUser, aOffset, aIsSelectionCollapsed, + aIsAtEndOfLine, aGranularity); nsCoreUtils::DispatchAccEvent(std::move(event)); return IPC_OK(); diff -Nru firefox-99.0.1+build1/accessible/ipc/DocAccessibleParent.h firefox-100.0+build1/accessible/ipc/DocAccessibleParent.h --- firefox-99.0.1+build1/accessible/ipc/DocAccessibleParent.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/ipc/DocAccessibleParent.h 2022-04-26 05:44:41.000000000 +0000 @@ -108,7 +108,7 @@ const LayoutDeviceIntRect& aCaretRect, #endif const int32_t& aOffset, const bool& aIsSelectionCollapsed, - const bool& aIsAtEndOfLine) final; + const bool& aIsAtEndOfLine, const int32_t& aGranularity) final; virtual mozilla::ipc::IPCResult RecvTextChangeEvent( const uint64_t& aID, const nsString& aStr, const int32_t& aStart, diff -Nru firefox-99.0.1+build1/accessible/ipc/other/DocAccessibleChild.cpp firefox-100.0+build1/accessible/ipc/other/DocAccessibleChild.cpp --- firefox-99.0.1+build1/accessible/ipc/other/DocAccessibleChild.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/ipc/other/DocAccessibleChild.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -844,12 +844,11 @@ const uint64_t& aID, nsTArray* aCells) { TableCellAccessible* acc = IdToTableCellAccessible(aID); if (acc) { - AutoTArray headerCells; + AutoTArray headerCells; acc->ColHeaderCells(&headerCells); aCells->SetCapacity(headerCells.Length()); - for (uint32_t i = 0; i < headerCells.Length(); ++i) { - aCells->AppendElement( - reinterpret_cast(headerCells[i]->UniqueID())); + for (Accessible* header : headerCells) { + aCells->AppendElement(header->ID()); } } @@ -860,12 +859,11 @@ const uint64_t& aID, nsTArray* aCells) { TableCellAccessible* acc = IdToTableCellAccessible(aID); if (acc) { - AutoTArray headerCells; + AutoTArray headerCells; acc->RowHeaderCells(&headerCells); aCells->SetCapacity(headerCells.Length()); - for (uint32_t i = 0; i < headerCells.Length(); ++i) { - aCells->AppendElement( - reinterpret_cast(headerCells[i]->UniqueID())); + for (Accessible* header : headerCells) { + aCells->AppendElement(header->ID()); } } @@ -1106,11 +1104,11 @@ const uint64_t& aID, nsTArray* aCellIDs) { TableAccessible* acc = IdToTableAccessible(aID); if (acc) { - AutoTArray cells; + AutoTArray cells; acc->SelectedCells(&cells); aCellIDs->SetCapacity(cells.Length()); - for (uint32_t i = 0; i < cells.Length(); ++i) { - aCellIDs->AppendElement(reinterpret_cast(cells[i]->UniqueID())); + for (Accessible* cell : cells) { + aCellIDs->AppendElement(cell->ID()); } } @@ -1206,9 +1204,9 @@ #ifdef MOZ_ACCESSIBILITY_ATK TableAccessible* acc = IdToTableAccessible(aID); if (acc) { - LocalAccessible* header = AccessibleWrap::GetColumnHeader(acc, aCol); + Accessible* header = AccessibleWrap::GetColumnHeader(acc, aCol); if (header) { - *aHeader = reinterpret_cast(header->UniqueID()); + *aHeader = header->ID(); *aOk = true; } } @@ -1225,9 +1223,9 @@ #ifdef MOZ_ACCESSIBILITY_ATK TableAccessible* acc = IdToTableAccessible(aID); if (acc) { - LocalAccessible* header = AccessibleWrap::GetRowHeader(acc, aRow); + Accessible* header = AccessibleWrap::GetRowHeader(acc, aRow); if (header) { - *aHeader = reinterpret_cast(header->UniqueID()); + *aHeader = header->ID(); *aOk = true; } } diff -Nru firefox-99.0.1+build1/accessible/ipc/other/PDocAccessible.ipdl firefox-100.0+build1/accessible/ipc/other/PDocAccessible.ipdl --- firefox-99.0.1+build1/accessible/ipc/other/PDocAccessible.ipdl 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/ipc/other/PDocAccessible.ipdl 2022-04-26 05:44:41.000000000 +0000 @@ -99,7 +99,8 @@ async HideEvent(uint64_t aRootID, bool aFromUser); async StateChangeEvent(uint64_t aID, uint64_t aState, bool aEnabled); async CaretMoveEvent(uint64_t aID, int32_t aOffset, - bool aIsSelectionCollapsed, bool aIsAtEndOfLine); + bool aIsSelectionCollapsed, bool aIsAtEndOfLine, + int32_t aGranularity); async TextChangeEvent(uint64_t aID, nsString aStr, int32_t aStart, uint32_t aLen, bool aIsInsert, bool aFromUser); async SelectionEvent(uint64_t aID, uint64_t aWidgetID, uint32_t aType); diff -Nru firefox-99.0.1+build1/accessible/ipc/other/RemoteAccessible.h firefox-100.0+build1/accessible/ipc/other/RemoteAccessible.h --- firefox-99.0.1+build1/accessible/ipc/other/RemoteAccessible.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/ipc/other/RemoteAccessible.h 2022-04-26 05:44:41.000000000 +0000 @@ -67,6 +67,8 @@ bool SelectionBoundsAt(int32_t aSelectionNum, nsString& aData, int32_t* aStartOffset, int32_t* aEndOffset); + virtual bool TableIsProbablyForLayout() override; + protected: explicit RemoteAccessible(DocAccessibleParent* aThisAsDoc) : RemoteAccessibleBase(aThisAsDoc) { diff -Nru firefox-99.0.1+build1/accessible/ipc/RemoteAccessibleBase.cpp firefox-100.0+build1/accessible/ipc/RemoteAccessibleBase.cpp --- firefox-99.0.1+build1/accessible/ipc/RemoteAccessibleBase.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/ipc/RemoteAccessibleBase.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ARIAMap.h" +#include "CachedTableAccessible.h" #include "DocAccessible.h" #include "mozilla/a11y/DocAccessibleParent.h" #include "mozilla/a11y/DocManager.h" @@ -17,6 +18,7 @@ #include "mozilla/dom/CanonicalBrowsingContext.h" #include "mozilla/dom/DocumentInlines.h" #include "mozilla/gfx/Matrix.h" +#include "mozilla/StaticPrefs_accessibility.h" #include "mozilla/Unused.h" #include "nsAccUtils.h" #include "nsTextEquivUtils.h" @@ -49,6 +51,10 @@ xpcDoc->NotifyOfShutdown(static_cast(this)); } + if (IsTable() || IsTableCell()) { + CachedTableAccessible::Invalidate(this); + } + // XXX Ideally this wouldn't be necessary, but it seems OuterDoc accessibles // can be destroyed before the doc they own. uint32_t childCount = mChildren.Length(); @@ -342,6 +348,26 @@ } template +void RemoteAccessibleBase::ApplyScrollOffset(nsRect& aBounds) const { + Maybe&> maybeScrollPosition = + mCachedFields->GetAttribute>(nsGkAtoms::scrollPosition); + + if (!maybeScrollPosition || maybeScrollPosition->Length() != 2) { + return; + } + // Our retrieved value is in app units, so we don't need to do any + // unit conversion here. + const nsTArray& scrollPosition = *maybeScrollPosition; + + // Scroll position is an inverse representation of scroll offset (since the + // further the scroll bar moves down the page, the further the page content + // moves up/closer to the origin). + nsPoint scrollOffset(-scrollPosition[0], -scrollPosition[1]); + + aBounds.MoveBy(scrollOffset.x, scrollOffset.y); +} + +template LayoutDeviceIntRect RemoteAccessibleBase::Bounds() const { if (mCachedFields) { Maybe maybeBounds = RetrieveCachedBounds(); @@ -358,7 +384,7 @@ Unused << ApplyTransform(bounds); LayoutDeviceIntRect devPxBounds; - const Accessible* acc = this; + const Accessible* acc = Parent(); while (acc) { if (LocalAccessible* localAcc = @@ -384,12 +410,9 @@ } RemoteAccessible* remoteAcc = const_cast(acc)->AsRemote(); - // Verify that remoteAcc is not `this`, since `bounds` was - // initialised to include this->RetrieveCachedBounds() - Maybe maybeRemoteBounds = - (remoteAcc == this) ? Nothing() : remoteAcc->RetrieveCachedBounds(); - if (maybeRemoteBounds) { + if (Maybe maybeRemoteBounds = + remoteAcc->RetrieveCachedBounds()) { nsRect remoteBounds = *maybeRemoteBounds; // We need to take into account a non-1 resolution set on the // presshell. This happens with async pinch zooming, among other @@ -406,11 +429,19 @@ // be scaled relative to its parent doc. res = remoteAcc->AsDoc()->mCachedFields->GetAttribute( nsGkAtoms::resolution); + MOZ_ASSERT(res, "No cached document resolution found."); bounds.ScaleRoundOut(res.valueOr(1.0f)); } - // We should offset `bounds` by the bounds retrieved above. - // This is how we build screen coordinates from relative coordinates. + // Apply scroll offset, if applicable. Only the contents of an + // element are affected by its scroll offset, which is why this call + // happens in this loop instead of both inside and outside of + // the loop (like ApplyTransform). + remoteAcc->ApplyScrollOffset(remoteBounds); + + // Regardless of whether this is a doc, we should offset `bounds` + // by the bounds retrieved here. This is how we build screen + // coordinates from relative coordinates. bounds.MoveBy(remoteBounds.X(), remoteBounds.Y()); Unused << remoteAcc->ApplyTransform(bounds); } @@ -538,6 +569,18 @@ state |= states::COLLAPSED; } } + + // Fetch our current opacity value from the cache. + auto opacity = Opacity(); + if (opacity && *opacity == 1.0f) { + state |= states::OPAQUE1; + } else { + // If we can't retrieve an opacity value, or if the value we retrieve + // is less than one, ensure the OPAQUE1 bit is cleared. + // It's possible this bit was set in the cached `rawState` vector, but + // we've since been notified of a style change invalidating that state. + state &= ~states::OPAQUE1; + } } auto* browser = static_cast(Document()->Manager()); if (browser == dom::BrowserParent::GetFocused()) { @@ -582,6 +625,20 @@ if (RefPtr display = DisplayStyle()) { attributes->SetAttribute(nsGkAtoms::display, display); } + + if (TableCellAccessibleBase* cell = AsTableCellBase()) { + TableAccessibleBase* table = cell->Table(); + uint32_t row = cell->RowIdx(); + uint32_t col = cell->ColIdx(); + int32_t cellIdx = table->CellIndexAt(row, col); + if (cellIdx != -1) { + attributes->SetAttribute(nsGkAtoms::tableCellIndex, cellIdx); + } + } + + if (bool layoutGuess = TableIsProbablyForLayout()) { + attributes->SetAttribute(nsGkAtoms::layout_guess, layoutGuess); + } } return attributes.forget(); @@ -612,6 +669,17 @@ } template +Maybe RemoteAccessibleBase::Opacity() const { + if (mCachedFields) { + // GetAttribute already returns a Maybe, so we don't + // need to do any additional manipulation. + return mCachedFields->GetAttribute(nsGkAtoms::opacity); + } + + return Nothing(); +} + +template nsAtom* RemoteAccessibleBase::GetPrimaryAction() const { if (mCachedFields) { if (auto action = @@ -902,6 +970,36 @@ Unused << mDoc->SendSetSelected(mID, aSelect); } +template +TableAccessibleBase* RemoteAccessibleBase::AsTableBase() { + MOZ_ASSERT(StaticPrefs::accessibility_cache_enabled_AtStartup()); + if (IsTable()) { + return CachedTableAccessible::GetFrom(this); + } + return nullptr; +} + +template +TableCellAccessibleBase* RemoteAccessibleBase::AsTableCellBase() { + MOZ_ASSERT(StaticPrefs::accessibility_cache_enabled_AtStartup()); + if (IsTableCell()) { + return CachedTableCellAccessible::GetFrom(this); + } + return nullptr; +} + +template +bool RemoteAccessibleBase::TableIsProbablyForLayout() { + MOZ_ASSERT(StaticPrefs::accessibility_cache_enabled_AtStartup()); + if (mCachedFields) { + if (auto layoutGuess = + mCachedFields->GetAttribute(nsGkAtoms::layout_guess)) { + return *layoutGuess; + } + } + return false; +} + template class RemoteAccessibleBase; } // namespace a11y diff -Nru firefox-99.0.1+build1/accessible/ipc/RemoteAccessibleBase.h firefox-100.0+build1/accessible/ipc/RemoteAccessibleBase.h --- firefox-99.0.1+build1/accessible/ipc/RemoteAccessibleBase.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/ipc/RemoteAccessibleBase.h 2022-04-26 05:44:41.000000000 +0000 @@ -177,6 +177,8 @@ virtual already_AddRefed DisplayStyle() const override; + virtual Maybe Opacity() const override; + virtual uint8_t ActionCount() const override; virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override; @@ -218,10 +220,7 @@ uintptr_t GetWrapper() const { return mWrapper; } void SetWrapper(uintptr_t aWrapper) { mWrapper = aWrapper; } - /* - * Return the ID of the accessible being proxied. - */ - uint64_t ID() const { return mID; } + virtual uint64_t ID() const override { return mID; } /** * Return the document containing this proxy, or the proxy itself if it is a @@ -272,6 +271,8 @@ virtual void AppendTextTo(nsAString& aText, uint32_t aStartOffset = 0, uint32_t aLength = UINT32_MAX) override; + virtual bool TableIsProbablyForLayout(); + uint32_t GetCachedTextLength(); Maybe&> GetCachedTextLines(); RefPtr GetCachedTextAttributes(); @@ -281,6 +282,9 @@ : nullptr; } + virtual TableAccessibleBase* AsTableBase() override; + virtual TableCellAccessibleBase* AsTableCellBase() override; + /** * Return the id of the dom node this accessible represents. Note this * should probably only be used for testing. @@ -317,6 +321,7 @@ void SetParent(Derived* aParent); Maybe RetrieveCachedBounds() const; bool ApplyTransform(nsRect& aBounds) const; + void ApplyScrollOffset(nsRect& aBounds) const; virtual void ARIAGroupPosition(int32_t* aLevel, int32_t* aSetSize, int32_t* aPosInSet) const override; @@ -335,6 +340,8 @@ friend Derived; friend DocAccessibleParent; + friend class xpcAccessible; + friend class CachedTableCellAccessible; nsTArray mChildren; DocAccessibleParent* mDoc; diff -Nru firefox-99.0.1+build1/accessible/ipc/RemoteAccessibleShared.h firefox-100.0+build1/accessible/ipc/RemoteAccessibleShared.h --- firefox-99.0.1+build1/accessible/ipc/RemoteAccessibleShared.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/ipc/RemoteAccessibleShared.h 2022-04-26 05:44:41.000000000 +0000 @@ -193,7 +193,6 @@ void TableSelectRow(uint32_t aRow); void TableUnselectColumn(uint32_t aCol); void TableUnselectRow(uint32_t aRow); -bool TableIsProbablyForLayout(); RemoteAccessible* AtkTableColumnHeader(int32_t aCol); RemoteAccessible* AtkTableRowHeader(int32_t aRow); diff -Nru firefox-99.0.1+build1/accessible/ipc/win/DocAccessibleChild.cpp firefox-100.0+build1/accessible/ipc/win/DocAccessibleChild.cpp --- firefox-99.0.1+build1/accessible/ipc/win/DocAccessibleChild.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/ipc/win/DocAccessibleChild.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -200,22 +200,26 @@ bool DocAccessibleChild::SendCaretMoveEvent(const uint64_t& aID, const int32_t& aOffset, const bool& aIsSelectionCollapsed, - const bool& aIsAtEndOfLine) { + const bool& aIsAtEndOfLine, + const int32_t& aGranularity) { return SendCaretMoveEvent(aID, GetCaretRectFor(aID), aOffset, - aIsSelectionCollapsed, aIsAtEndOfLine); + aIsSelectionCollapsed, aIsAtEndOfLine, + aGranularity); } bool DocAccessibleChild::SendCaretMoveEvent( const uint64_t& aID, const LayoutDeviceIntRect& aCaretRect, const int32_t& aOffset, const bool& aIsSelectionCollapsed, - const bool& aIsAtEndOfLine) { + const bool& aIsAtEndOfLine, const int32_t& aGranularity) { if (IsConstructedInParentProcess()) { return PDocAccessibleChild::SendCaretMoveEvent( - aID, aCaretRect, aOffset, aIsSelectionCollapsed, aIsAtEndOfLine); + aID, aCaretRect, aOffset, aIsSelectionCollapsed, aIsAtEndOfLine, + aGranularity); } PushDeferredEvent(MakeUnique( - this, aID, aCaretRect, aOffset, aIsSelectionCollapsed, aIsAtEndOfLine)); + this, aID, aCaretRect, aOffset, aIsSelectionCollapsed, aIsAtEndOfLine, + aGranularity)); return true; } diff -Nru firefox-99.0.1+build1/accessible/ipc/win/DocAccessibleChild.h firefox-100.0+build1/accessible/ipc/win/DocAccessibleChild.h --- firefox-99.0.1+build1/accessible/ipc/win/DocAccessibleChild.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/ipc/win/DocAccessibleChild.h 2022-04-26 05:44:41.000000000 +0000 @@ -52,12 +52,14 @@ const bool& aEnabled); bool SendCaretMoveEvent(const uint64_t& aID, const int32_t& aOffset, const bool& aIsSelectionCollapsed, - const bool& aIsAtEndOfLine); + const bool& aIsAtEndOfLine, + const int32_t& aGranularity); bool SendCaretMoveEvent(const uint64_t& aID, const LayoutDeviceIntRect& aCaretRect, const int32_t& aOffset, const bool& aIsSelectionCollapsed, - const bool& aIsAtEndOfLine); + const bool& aIsAtEndOfLine, + const int32_t& aGranularity); bool SendFocusEvent(const uint64_t& aID); bool SendFocusEvent(const uint64_t& aID, const LayoutDeviceIntRect& aCaretRect); @@ -175,17 +177,20 @@ struct SerializedCaretMove final : public DeferredEvent { SerializedCaretMove(DocAccessibleChild* aTarget, uint64_t aID, const LayoutDeviceIntRect& aCaretRect, int32_t aOffset, - bool aIsSelectionCollapsed, bool aIsAtEndOfLine) + bool aIsSelectionCollapsed, bool aIsAtEndOfLine, + int32_t aGranularity) : DeferredEvent(aTarget), mID(aID), mCaretRect(aCaretRect), mOffset(aOffset), mIsSelectionCollapsed(aIsSelectionCollapsed), - mIsAtEndOfLine(aIsAtEndOfLine) {} + mIsAtEndOfLine(aIsAtEndOfLine), + mGranularity(aGranularity) {} void Dispatch(DocAccessibleChild* aIPCDoc) override { - Unused << aIPCDoc->SendCaretMoveEvent( - mID, mCaretRect, mOffset, mIsSelectionCollapsed, mIsAtEndOfLine); + Unused << aIPCDoc->SendCaretMoveEvent(mID, mCaretRect, mOffset, + mIsSelectionCollapsed, + mIsAtEndOfLine, mGranularity); } uint64_t mID; @@ -193,6 +198,7 @@ int32_t mOffset; bool mIsSelectionCollapsed; bool mIsAtEndOfLine; + int32_t mGranularity; }; struct SerializedFocus final : public DeferredEvent { diff -Nru firefox-99.0.1+build1/accessible/ipc/win/HandlerProvider.h firefox-100.0+build1/accessible/ipc/win/HandlerProvider.h --- firefox-99.0.1+build1/accessible/ipc/win/HandlerProvider.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/ipc/win/HandlerProvider.h 2022-04-26 05:44:41.000000000 +0000 @@ -115,7 +115,7 @@ HRESULT* result); Atomic mRefCnt; - Mutex mMutex; // Protects mSerializer + Mutex mMutex MOZ_UNANNOTATED; // Protects mSerializer const IID mTargetUnkIid; mscom::InterceptorTargetPtr mTargetUnk; // Constant, main thread only @@ -136,7 +136,7 @@ // Used when the payload is built prior to marshaling the object by a bulk // fetch operation. See prebuildPayload(). IA2PayloadPtr mPayload; - Mutex mPayloadMutex; // Protects mPayload + Mutex mPayloadMutex MOZ_UNANNOTATED; // Protects mPayload }; } // namespace a11y diff -Nru firefox-99.0.1+build1/accessible/ipc/win/PDocAccessible.ipdl firefox-100.0+build1/accessible/ipc/win/PDocAccessible.ipdl --- firefox-99.0.1+build1/accessible/ipc/win/PDocAccessible.ipdl 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/ipc/win/PDocAccessible.ipdl 2022-04-26 05:44:41.000000000 +0000 @@ -70,7 +70,7 @@ async StateChangeEvent(uint64_t aID, uint64_t aState, bool aEnabled); async CaretMoveEvent(uint64_t aID, LayoutDeviceIntRect aCaretRect, int32_t aOffset, bool aIsAtEndOfLine, - bool aIsSelectionCollapsed); + bool aIsSelectionCollapsed, int32_t aGranularity); async TextChangeEvent(uint64_t aID, nsString aStr, int32_t aStart, uint32_t aLen, bool aIsInsert, bool aFromUser); sync SyncTextChangeEvent(uint64_t aID, nsString aStr, int32_t aStart, diff -Nru firefox-99.0.1+build1/accessible/mac/AccessibleWrap.mm firefox-100.0+build1/accessible/mac/AccessibleWrap.mm --- firefox-99.0.1+build1/accessible/mac/AccessibleWrap.mm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/mac/AccessibleWrap.mm 2022-04-26 05:44:41.000000000 +0000 @@ -226,7 +226,9 @@ int32_t caretOffset = event->GetCaretOffset(); MOXTextMarkerDelegate* delegate = [MOXTextMarkerDelegate getOrCreateForDoc:aEvent->Document()]; - [delegate setCaretOffset:eventTarget at:caretOffset]; + [delegate setCaretOffset:eventTarget + at:caretOffset + moveGranularity:event->GetGranularity()]; if (event->IsSelectionCollapsed()) { // If the selection is collapsed, invalidate our text selection cache. [delegate setSelectionFrom:eventTarget diff -Nru firefox-99.0.1+build1/accessible/mac/MacUtils.h firefox-100.0+build1/accessible/mac/MacUtils.h --- firefox-99.0.1+build1/accessible/mac/MacUtils.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/mac/MacUtils.h 2022-04-26 05:44:41.000000000 +0000 @@ -18,11 +18,19 @@ namespace utils { // convert an array of Gecko accessibles to an NSArray of native accessibles -NSArray* ConvertToNSArray(nsTArray& aArray); +template +NSArray* ConvertToNSArray(AccArray& aArray) { + NSMutableArray* nativeArray = [[[NSMutableArray alloc] init] autorelease]; -// convert an array of Gecko proxy accessibles to an NSArray of native -// accessibles -NSArray* ConvertToNSArray(nsTArray& aArray); + // iterate through the list, and get each native accessible. + for (Accessible* curAccessible : aArray) { + mozAccessible* curNative = GetNativeFromGeckoAccessible(curAccessible); + if (curNative) + [nativeArray addObject:GetObjectOrRepresentedView(curNative)]; + } + + return nativeArray; +} /** * Get a localized string from the string bundle. diff -Nru firefox-99.0.1+build1/accessible/mac/MacUtils.mm firefox-100.0+build1/accessible/mac/MacUtils.mm --- firefox-99.0.1+build1/accessible/mac/MacUtils.mm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/mac/MacUtils.mm 2022-04-26 05:44:41.000000000 +0000 @@ -15,39 +15,6 @@ namespace a11y { namespace utils { -// convert an array of Gecko accessibles to an NSArray of native accessibles -NSArray* ConvertToNSArray(nsTArray& aArray) { - NSMutableArray* nativeArray = [[[NSMutableArray alloc] init] autorelease]; - - // iterate through the list, and get each native accessible. - size_t totalCount = aArray.Length(); - for (size_t i = 0; i < totalCount; i++) { - LocalAccessible* curAccessible = aArray.ElementAt(i); - mozAccessible* curNative = GetNativeFromGeckoAccessible(curAccessible); - if (curNative) - [nativeArray addObject:GetObjectOrRepresentedView(curNative)]; - } - - return nativeArray; -} - -// convert an array of Gecko proxy accessibles to an NSArray of native -// accessibles -NSArray* ConvertToNSArray(nsTArray& aArray) { - NSMutableArray* nativeArray = [[[NSMutableArray alloc] init] autorelease]; - - // iterate through the list, and get each native accessible. - size_t totalCount = aArray.Length(); - for (size_t i = 0; i < totalCount; i++) { - RemoteAccessible* curAccessible = aArray.ElementAt(i); - mozAccessible* curNative = GetNativeFromGeckoAccessible(curAccessible); - if (curNative) - [nativeArray addObject:GetObjectOrRepresentedView(curNative)]; - } - - return nativeArray; -} - /** * Get a localized string from the a11y string bundle. * Return nil if not found. diff -Nru firefox-99.0.1+build1/accessible/mac/MOXTextMarkerDelegate.h firefox-100.0+build1/accessible/mac/MOXTextMarkerDelegate.h --- firefox-99.0.1+build1/accessible/mac/MOXTextMarkerDelegate.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/mac/MOXTextMarkerDelegate.h 2022-04-26 05:44:41.000000000 +0000 @@ -15,6 +15,7 @@ AXTextMarkerRangeRef mSelection; AXTextMarkerRef mCaret; AXTextMarkerRef mPrevCaret; + int32_t mCaretMoveGranularity; } + (id)getOrCreateForDoc:(mozilla::a11y::Accessible*)aDoc; @@ -30,7 +31,9 @@ to:(mozilla::a11y::Accessible*)endContainer at:(int32_t)endOffset; -- (void)setCaretOffset:(mozilla::a11y::Accessible*)container at:(int32_t)offset; +- (void)setCaretOffset:(mozilla::a11y::Accessible*)container + at:(int32_t)offset + moveGranularity:(int32_t)granularity; - (NSDictionary*)selectionChangeInfo; diff -Nru firefox-99.0.1+build1/accessible/mac/MOXTextMarkerDelegate.mm firefox-100.0+build1/accessible/mac/MOXTextMarkerDelegate.mm --- firefox-99.0.1+build1/accessible/mac/MOXTextMarkerDelegate.mm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/mac/MOXTextMarkerDelegate.mm 2022-04-26 05:44:41.000000000 +0000 @@ -12,6 +12,7 @@ #include "mozAccessible.h" #include "mozilla/Preferences.h" +#include "nsISelectionListener.h" using namespace mozilla::a11y; @@ -52,6 +53,8 @@ mGeckoDocAccessible = aDoc; } + mCaretMoveGranularity = nsISelectionListener::NO_AMOUNT; + return self; } @@ -76,11 +79,14 @@ } - (void)setCaretOffset:(mozilla::a11y::Accessible*)container - at:(int32_t)offset { + at:(int32_t)offset + moveGranularity:(int32_t)granularity { GeckoTextMarker caretMarker(container, offset); mPrevCaret = mCaret; mCaret = caretMarker.CreateAXTextMarker(); + mCaretMoveGranularity = granularity; + CFRetain(mCaret); } @@ -146,21 +152,43 @@ } bool isForward = prevCaretMarker < caretMarker; - uint32_t deltaLength = - GeckoTextMarkerRange(isForward ? prevCaretMarker : caretMarker, - isForward ? caretMarker : prevCaretMarker) - .Length(); + int direction = isForward ? AXTextSelectionDirectionNext + : AXTextSelectionDirectionPrevious; + + int32_t granularity = AXTextSelectionGranularityUnknown; + switch (mCaretMoveGranularity) { + case nsISelectionListener::CHARACTER_AMOUNT: + case nsISelectionListener::CLUSTER_AMOUNT: + granularity = AXTextSelectionGranularityCharacter; + break; + case nsISelectionListener::WORD_AMOUNT: + case nsISelectionListener::WORDNOSPACE_AMOUNT: + granularity = AXTextSelectionGranularityWord; + break; + case nsISelectionListener::LINE_AMOUNT: + granularity = AXTextSelectionGranularityLine; + break; + case nsISelectionListener::BEGINLINE_AMOUNT: + direction = AXTextSelectionDirectionBeginning; + granularity = AXTextSelectionGranularityLine; + break; + case nsISelectionListener::ENDLINE_AMOUNT: + direction = AXTextSelectionDirectionEnd; + granularity = AXTextSelectionGranularityLine; + break; + case nsISelectionListener::PARAGRAPH_AMOUNT: + granularity = AXTextSelectionGranularityParagraph; + break; + default: + break; + } // Determine selection direction with marker comparison. // If the delta between the two markers is more than one, consider it // a word. Not accurate, but good enough for VO. [info addEntriesFromDictionary:@{ - @"AXTextSelectionDirection" : isForward - ? @(AXTextSelectionDirectionNext) - : @(AXTextSelectionDirectionPrevious), - @"AXTextSelectionGranularity" : deltaLength == 1 - ? @(AXTextSelectionGranularityCharacter) - : @(AXTextSelectionGranularityWord) + @"AXTextSelectionDirection" : @(direction), + @"AXTextSelectionGranularity" : @(granularity) }]; return info; diff -Nru firefox-99.0.1+build1/accessible/mac/mozTableAccessible.mm firefox-100.0+build1/accessible/mac/mozTableAccessible.mm --- firefox-99.0.1+build1/accessible/mac/mozTableAccessible.mm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/mac/mozTableAccessible.mm 2022-04-26 05:44:41.000000000 +0000 @@ -12,8 +12,9 @@ #include "AccIterator.h" #include "LocalAccessible.h" -#include "TableAccessible.h" -#include "TableCellAccessible.h" +#include "mozilla/a11y/TableAccessibleBase.h" +#include "mozilla/a11y/TableCellAccessibleBase.h" +#include "mozilla/StaticPrefs_accessibility.h" #include "XULTreeAccessible.h" #include "Pivot.h" #include "Relation.h" @@ -49,13 +50,14 @@ mChildren = [[NSMutableArray alloc] init]; - if (LocalAccessible* acc = [mParent geckoAccessible]->AsLocal()) { - TableAccessible* table = acc->AsTable(); + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || + [mParent geckoAccessible]->IsLocal()) { + TableAccessibleBase* table = [mParent geckoAccessible]->AsTableBase(); MOZ_ASSERT(table, "Got null table when fetching column children!"); uint32_t numRows = table->RowCount(); for (uint32_t j = 0; j < numRows; j++) { - LocalAccessible* cell = table->CellAt(j, mIndex); + Accessible* cell = table->CellAt(j, mIndex); mozAccessible* nativeCell = cell ? GetNativeFromGeckoAccessible(cell) : nil; if ([nativeCell isAccessibilityElement]) { @@ -196,6 +198,11 @@ } bool tableGuess; + // For LocalAccessible and cached RemoteAccessible, We could use + // AsTableBase()->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. if (LocalAccessible* acc = mGeckoAccessible->AsLocal()) { tableGuess = acc->AsTable()->IsProbablyLayoutTable(); } else { @@ -235,16 +242,18 @@ - (NSNumber*)moxRowCount { MOZ_ASSERT(mGeckoAccessible); - return mGeckoAccessible->IsLocal() - ? @(mGeckoAccessible->AsLocal()->AsTable()->RowCount()) + return (StaticPrefs::accessibility_cache_enabled_AtStartup() || + mGeckoAccessible->IsLocal()) + ? @(mGeckoAccessible->AsTableBase()->RowCount()) : @(mGeckoAccessible->AsRemote()->TableRowCount()); } - (NSNumber*)moxColumnCount { MOZ_ASSERT(mGeckoAccessible); - return mGeckoAccessible->IsLocal() - ? @(mGeckoAccessible->AsLocal()->AsTable()->ColCount()) + return (StaticPrefs::accessibility_cache_enabled_AtStartup() || + mGeckoAccessible->IsLocal()) + ? @(mGeckoAccessible->AsTableBase()->ColCount()) : @(mGeckoAccessible->AsRemote()->TableColumnCount()); } @@ -286,8 +295,9 @@ mColContainers = [[NSMutableArray alloc] init]; uint32_t numCols = 0; - if (LocalAccessible* acc = mGeckoAccessible->AsLocal()) { - numCols = acc->AsTable()->ColCount(); + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || + mGeckoAccessible->IsLocal()) { + numCols = mGeckoAccessible->AsTableBase()->ColCount(); } else { numCols = mGeckoAccessible->AsRemote()->TableColumnCount(); } @@ -314,10 +324,11 @@ MOZ_ASSERT(mGeckoAccessible); uint32_t numCols = 0; - TableAccessible* table = nullptr; + TableAccessibleBase* table = nullptr; - if (LocalAccessible* acc = mGeckoAccessible->AsLocal()) { - table = mGeckoAccessible->AsLocal()->AsTable(); + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || + mGeckoAccessible->IsLocal()) { + table = mGeckoAccessible->AsTableBase(); numCols = table->ColCount(); } else { numCols = mGeckoAccessible->AsRemote()->TableColumnCount(); @@ -354,8 +365,9 @@ MOZ_ASSERT(mGeckoAccessible); Accessible* cell; - if (mGeckoAccessible->IsLocal()) { - cell = mGeckoAccessible->AsLocal()->AsTable()->CellAt(row, col); + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || + mGeckoAccessible->IsLocal()) { + cell = mGeckoAccessible->AsTableBase()->CellAt(row, col); } else { cell = mGeckoAccessible->AsRemote()->TableCellAt(row, col); } @@ -416,8 +428,9 @@ - (NSValue*)moxRowIndexRange { MOZ_ASSERT(mGeckoAccessible); - if (mGeckoAccessible->IsLocal()) { - TableCellAccessible* cell = mGeckoAccessible->AsLocal()->AsTableCell(); + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || + mGeckoAccessible->IsLocal()) { + TableCellAccessibleBase* cell = mGeckoAccessible->AsTableCellBase(); return [NSValue valueWithRange:NSMakeRange(cell->RowIdx(), cell->RowExtent())]; } else { @@ -430,8 +443,9 @@ - (NSValue*)moxColumnIndexRange { MOZ_ASSERT(mGeckoAccessible); - if (mGeckoAccessible->IsLocal()) { - TableCellAccessible* cell = mGeckoAccessible->AsLocal()->AsTableCell(); + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || + mGeckoAccessible->IsLocal()) { + TableCellAccessibleBase* cell = mGeckoAccessible->AsTableCellBase(); return [NSValue valueWithRange:NSMakeRange(cell->ColIdx(), cell->ColExtent())]; } else { @@ -444,9 +458,10 @@ - (NSArray*)moxRowHeaderUIElements { MOZ_ASSERT(mGeckoAccessible); - if (mGeckoAccessible->IsLocal()) { - TableCellAccessible* cell = mGeckoAccessible->AsLocal()->AsTableCell(); - AutoTArray headerCells; + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || + mGeckoAccessible->IsLocal()) { + TableCellAccessibleBase* cell = mGeckoAccessible->AsTableCellBase(); + AutoTArray headerCells; cell->RowHeaderCells(&headerCells); return utils::ConvertToNSArray(headerCells); } else { @@ -460,9 +475,10 @@ - (NSArray*)moxColumnHeaderUIElements { MOZ_ASSERT(mGeckoAccessible); - if (mGeckoAccessible->IsLocal()) { - TableCellAccessible* cell = mGeckoAccessible->AsLocal()->AsTableCell(); - AutoTArray headerCells; + if (StaticPrefs::accessibility_cache_enabled_AtStartup() || + mGeckoAccessible->IsLocal()) { + TableCellAccessibleBase* cell = mGeckoAccessible->AsTableCellBase(); + AutoTArray headerCells; cell->ColHeaderCells(&headerCells); return utils::ConvertToNSArray(headerCells); } else { diff -Nru firefox-99.0.1+build1/accessible/mac/Platform.mm firefox-100.0+build1/accessible/mac/Platform.mm --- firefox-99.0.1+build1/accessible/mac/Platform.mm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/mac/Platform.mm 2022-04-26 05:44:41.000000000 +0000 @@ -111,11 +111,11 @@ } void ProxyCaretMoveEvent(RemoteAccessible* aTarget, int32_t aOffset, - bool aIsSelectionCollapsed) { + bool aIsSelectionCollapsed, int32_t aGranularity) { mozAccessible* wrapper = GetNativeFromGeckoAccessible(aTarget); MOXTextMarkerDelegate* delegate = [MOXTextMarkerDelegate getOrCreateForDoc:aTarget->Document()]; - [delegate setCaretOffset:aTarget at:aOffset]; + [delegate setCaretOffset:aTarget at:aOffset moveGranularity:aGranularity]; if (aIsSelectionCollapsed) { // If selection is collapsed, invalidate selection. [delegate setSelectionFrom:aTarget at:aOffset to:aTarget at:aOffset]; diff -Nru firefox-99.0.1+build1/accessible/other/Platform.cpp firefox-100.0+build1/accessible/other/Platform.cpp --- firefox-99.0.1+build1/accessible/other/Platform.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/other/Platform.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -22,7 +22,8 @@ void a11y::ProxyStateChangeEvent(RemoteAccessible*, uint64_t, bool) {} void a11y::ProxyCaretMoveEvent(RemoteAccessible* aTarget, int32_t aOffset, - bool aIsSelectionCollapsed) {} + bool aIsSelectionCollapsed, + int32_t aGranularity) {} void a11y::ProxyTextChangeEvent(RemoteAccessible*, const nsString&, int32_t, uint32_t, bool, bool) {} diff -Nru firefox-99.0.1+build1/accessible/tests/browser/bounds/browser_test_simple_transform.js firefox-100.0+build1/accessible/tests/browser/bounds/browser_test_simple_transform.js --- firefox-99.0.1+build1/accessible/tests/browser/bounds/browser_test_simple_transform.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/tests/browser/bounds/browser_test_simple_transform.js 2022-04-26 05:44:41.000000000 +0000 @@ -4,71 +4,20 @@ "use strict"; -async function finishContentPaint(browser) { - await SpecialPowers.spawn(browser, [], () => { - return new Promise(function(r) { - content.requestAnimationFrame(() => content.setTimeout(r)); - }); - }); -} - -async function testContentBoundsInIframe(iframeDocAcc, id, browser) { - const acc = findAccessibleChildByID(iframeDocAcc, id); - const x = {}; - const y = {}; - const width = {}; - const height = {}; - acc.getBounds(x, y, width, height); - - await invokeContentTask( - browser, - [id, x.value, y.value, width.value, height.value], - (_id, _x, _y, _width, _height) => { - const { Layout: LayoutUtils } = ChromeUtils.import( - "chrome://mochitests/content/browser/accessible/tests/browser/Layout.jsm" - ); - - let [ - expectedX, - expectedY, - expectedWidth, - expectedHeight, - ] = LayoutUtils.getBoundsForDOMElm(_id, content.document); - - ok( - _x >= expectedX - 5 || _x <= expectedX + 5, - "Got " + _x + ", accurate x for " + _id - ); - ok( - _y >= expectedY - 5 || _y <= expectedY + 5, - "Got " + _y + ", accurate y for " + _id - ); - ok( - _width >= expectedWidth - 5 || _width <= expectedWidth + 5, - "Got " + _width + ", accurate width for " + _id - ); - ok( - _height >= expectedHeight - 5 || _height <= expectedHeight + 5, - "Got " + _height + ", accurate height for " + _id - ); - } - ); -} - // test basic translation addAccessibleTask( `

hello world

`, async function(browser, iframeDocAcc, contentDocAcc) { ok(iframeDocAcc, "IFRAME document accessible is present"); - await testContentBoundsInIframe(iframeDocAcc, "translate", browser); + await testBoundsInContent(iframeDocAcc, "translate", browser); await invokeContentTask(browser, [], () => { let p = content.document.getElementById("translate"); p.style = "transform: translate(100px, 100px);"; }); - await finishContentPaint(browser); - await testContentBoundsInIframe(iframeDocAcc, "translate", browser); + await waitForContentPaint(browser); + await testBoundsInContent(iframeDocAcc, "translate", browser); }, { topLevel: true, iframe: true, remoteIframe: true } ); @@ -78,15 +27,15 @@ `

hello world

`, async function(browser, iframeDocAcc, contentDocAcc) { ok(iframeDocAcc, "IFRAME document accessible is present"); - await testContentBoundsInIframe(iframeDocAcc, "rotate", browser); + await testBoundsInContent(iframeDocAcc, "rotate", browser); await invokeContentTask(browser, [], () => { let p = content.document.getElementById("rotate"); p.style = "transform: rotate(-40deg);"; }); - await finishContentPaint(browser); - await testContentBoundsInIframe(iframeDocAcc, "rotate", browser); + await waitForContentPaint(browser); + await testBoundsInContent(iframeDocAcc, "rotate", browser); }, { topLevel: true, iframe: true, remoteIframe: true } ); @@ -96,15 +45,15 @@ `

hello world

`, async function(browser, iframeDocAcc, contentDocAcc) { ok(iframeDocAcc, "IFRAME document accessible is present"); - await testContentBoundsInIframe(iframeDocAcc, "scale", browser); + await testBoundsInContent(iframeDocAcc, "scale", browser); await invokeContentTask(browser, [], () => { let p = content.document.getElementById("scale"); p.style = "transform: scale(2);"; }); - await finishContentPaint(browser); - await testContentBoundsInIframe(iframeDocAcc, "scale", browser); + await waitForContentPaint(browser); + await testBoundsInContent(iframeDocAcc, "scale", browser); }, { topLevel: true, iframe: true, remoteIframe: true } ); diff -Nru firefox-99.0.1+build1/accessible/tests/browser/e10s/browser_caching_actions.js firefox-100.0+build1/accessible/tests/browser/e10s/browser_caching_actions.js --- firefox-99.0.1+build1/accessible/tests/browser/e10s/browser_caching_actions.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/tests/browser/e10s/browser_caching_actions.js 2022-04-26 05:44:41.000000000 +0000 @@ -22,14 +22,6 @@ cycle: "Cycle", }; -const isCacheEnabled = Services.prefs.getBoolPref( - "accessibility.cache.enabled", - false -); -// Some RemoteAccessible methods aren't supported on Windows when the cache is -// disabled. -const isWinNoCache = !isCacheEnabled && AppConstants.platform == "win"; - async function testActions(browser, docAcc, id, expectedActions, domEvents) { const acc = findAccessibleChildByID(docAcc, id); is(acc.actionCount, expectedActions.length, "Correct action count"); diff -Nru firefox-99.0.1+build1/accessible/tests/browser/e10s/browser_caching_attributes.js firefox-100.0+build1/accessible/tests/browser/e10s/browser_caching_attributes.js --- firefox-99.0.1+build1/accessible/tests/browser/e10s/browser_caching_attributes.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/tests/browser/e10s/browser_caching_attributes.js 2022-04-26 05:44:41.000000000 +0000 @@ -7,11 +7,6 @@ /* import-globals-from ../../mochitest/attributes.js */ loadScripts({ name: "attributes.js", dir: MOCHITESTS_DIR }); -const isCacheEnabled = Services.prefs.getBoolPref( - "accessibility.cache.enabled", - false -); - /** * Default textbox accessible attributes. */ diff -Nru firefox-99.0.1+build1/accessible/tests/browser/e10s/browser_caching_states.js firefox-100.0+build1/accessible/tests/browser/e10s/browser_caching_states.js --- firefox-99.0.1+build1/accessible/tests/browser/e10s/browser_caching_states.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/tests/browser/e10s/browser_caching_states.js 2022-04-26 05:44:41.000000000 +0000 @@ -211,3 +211,41 @@ testStates(docAcc, STATE_FOCUSED); } ); + +function checkOpacity(acc, present) { + // eslint-disable-next-line no-unused-vars + let [_, extraState] = getStates(acc); + let currOpacity = extraState & EXT_STATE_OPAQUE; + return present ? currOpacity : !currOpacity; +} + +/** + * Test caching of the OPAQUE1 state. + */ +addAccessibleTask( + ` +
hello world
+ `, + async function(browser, docAcc) { + const div = findAccessibleChildByID(docAcc, "div"); + await untilCacheOk(() => checkOpacity(div, true), "Found opaque state"); + + await invokeContentTask(browser, [], () => { + let elm = content.document.getElementById("div"); + elm.style = "opacity: 0.4;"; + }); + + await untilCacheOk( + () => checkOpacity(div, false), + "Did not find opaque state" + ); + + await invokeContentTask(browser, [], () => { + let elm = content.document.getElementById("div"); + elm.style = "opacity: 1;"; + }); + + await untilCacheOk(() => checkOpacity(div, true), "Found opaque state"); + }, + { iframe: true, remoteIframe: true, chrome: true } +); diff -Nru firefox-99.0.1+build1/accessible/tests/browser/e10s/browser_caching_table.js firefox-100.0+build1/accessible/tests/browser/e10s/browser_caching_table.js --- firefox-99.0.1+build1/accessible/tests/browser/e10s/browser_caching_table.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-100.0+build1/accessible/tests/browser/e10s/browser_caching_table.js 2022-04-26 05:44:41.000000000 +0000 @@ -0,0 +1,330 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Test tables for both local and remote Accessibles. There is more extensive + * coverage in ../../mochitest/table. These tests are primarily to ensure that + * the cache works as expected and that there is consistency between local and + * remote. + */ + +"use strict"; + +/* import-globals-from ../../mochitest/table.js */ +/* import-globals-from ../../mochitest/attributes.js */ +loadScripts( + { name: "table.js", dir: MOCHITESTS_DIR }, + { name: "attributes.js", dir: MOCHITESTS_DIR } +); + +/** + * Test table counts, indexes, extents and implicit headers. + */ +addAccessibleTask( + ` + + + + + + + + +
abcd
eifjgh
k
+ `, + async function(browser, docAcc) { + const table = findAccessibleChildByID(docAcc, "table", [ + nsIAccessibleTable, + ]); + is(table.rowCount, 3, "table rowCount correct"); + is(table.columnCount, 4, "table columnCount correct"); + testTableIndexes(table, [ + [0, 1, 1, 2], + [3, 4, 5, 6], + [3, 4, 7, -1], + ]); + const cells = {}; + for (const id of ["a", "bc", "d", "ei", "fj", "g", "h", "k"]) { + cells[id] = findAccessibleChildByID(docAcc, id, [nsIAccessibleTableCell]); + } + is(cells.a.rowExtent, 1, "a rowExtent correct"); + is(cells.a.columnExtent, 1, "a columnExtent correct"); + is(cells.bc.rowExtent, 1, "bc rowExtent correct"); + is(cells.bc.columnExtent, 2, "bc columnExtent correct"); + is(cells.ei.rowExtent, 2, "ei rowExtent correct"); + is(cells.fj.rowExtent, 2, "fj rowExtent correct"); + testHeaderCells([ + { + cell: cells.ei, + rowHeaderCells: [], + columnHeaderCells: [cells.a], + }, + { + cell: cells.g, + rowHeaderCells: [cells.ei], + columnHeaderCells: [cells.bc], + }, + { + cell: cells.k, + rowHeaderCells: [cells.ei], + columnHeaderCells: [cells.bc], + }, + ]); + }, + { + chrome: true, + topLevel: isCacheEnabled, + iframe: isCacheEnabled, + remoteIframe: isCacheEnabled, + } +); + +/** + * Test table explicit headers. + */ +addAccessibleTask( + ` + + + + +
ab
cd
ef
+ `, + async function(browser, docAcc) { + const cells = {}; + for (const id of ["a", "b", "c", "d", "e", "f"]) { + cells[id] = findAccessibleChildByID(docAcc, id, [nsIAccessibleTableCell]); + } + testHeaderCells([ + { + cell: cells.c, + rowHeaderCells: [cells.d], + columnHeaderCells: [cells.b], + }, + { + cell: cells.e, + rowHeaderCells: [cells.f], + columnHeaderCells: [cells.c], + }, + ]); + }, + { + chrome: true, + topLevel: isCacheEnabled, + iframe: isCacheEnabled, + remoteIframe: isCacheEnabled, + } +); + +/** + * Test that an inner table doesn't impact an outer table. + */ +addAccessibleTask( + ` + +
outerCell +
a
+
+ + `, + async function(browser, docAcc) { + const outerTable = findAccessibleChildByID(docAcc, "outerTable", [ + nsIAccessibleTable, + ]); + is(outerTable.rowCount, 1, "outerTable rowCount correct"); + is(outerTable.columnCount, 1, "outerTable columnCount correct"); + const outerCell = findAccessibleChildByID(docAcc, "outerCell"); + is( + outerTable.getCellAt(0, 0), + outerCell, + "outerTable returns correct cell" + ); + const innerTable = findAccessibleChildByID(docAcc, "innerTable", [ + nsIAccessibleTable, + ]); + is(innerTable.rowCount, 1, "innerTable rowCount correct"); + is(innerTable.columnCount, 1, "innerTable columnCount correct"); + const innerCell = findAccessibleChildByID(docAcc, "innerCell"); + is( + innerTable.getCellAt(0, 0), + innerCell, + "innerTable returns correct cell" + ); + }, + { + chrome: true, + topLevel: isCacheEnabled, + iframe: isCacheEnabled, + remoteIframe: isCacheEnabled, + } +); + +/** + * Test table caption and summary. + */ +addAccessibleTask( + ` + + + +
c1
a
+ + +
a
+ + + +
c3
a
+ `, + async function(browser, docAcc) { + const t1 = findAccessibleChildByID(docAcc, "t1", [nsIAccessibleTable]); + const c1 = findAccessibleChildByID(docAcc, "c1"); + is(t1.caption, c1, "t1 caption correct"); + ok(!t1.summary, "t1 no summary"); + const t2 = findAccessibleChildByID(docAcc, "t2", [nsIAccessibleTable]); + ok(!t2.caption, "t2 caption is null"); + is(t2.summary, "s2", "t2 summary correct"); + const t3 = findAccessibleChildByID(docAcc, "t3", [nsIAccessibleTable]); + const c3 = findAccessibleChildByID(docAcc, "c3"); + is(t3.caption, c3, "t3 caption correct"); + is(t3.summary, "s3", "t3 summary correct"); + }, + { + chrome: true, + topLevel: isCacheEnabled, + iframe: isCacheEnabled, + remoteIframe: isCacheEnabled, + } +); + +/** + * Test table layout guess. + */ +addAccessibleTask( + ` +
a
+
a
+ `, + async function(browser, docAcc) { + const layout = findAccessibleChildByID(docAcc, "layout", [ + nsIAccessibleTable, + ]); + testAttrs(layout, { "layout-guess": "true" }, true); + const data = findAccessibleChildByID(docAcc, "data", [nsIAccessibleTable]); + testAbsentAttrs(data, { "layout-guess": "true" }); + }, + { + chrome: true, + topLevel: isCacheEnabled, + iframe: isCacheEnabled, + remoteIframe: isCacheEnabled, + } +); + +/** + * Test ARIA grid. + */ +addAccessibleTask( + ` +
+
+
a
b
+
+
+
c
d
+
+
+ `, + async function(browser, docAcc) { + const grid = findAccessibleChildByID(docAcc, "grid", [nsIAccessibleTable]); + is(grid.rowCount, 2, "grid rowCount correct"); + is(grid.columnCount, 2, "grid columnCount correct"); + testTableIndexes(grid, [ + [0, 1], + [2, 3], + ]); + const cells = {}; + for (const id of ["a", "b", "c", "d"]) { + cells[id] = findAccessibleChildByID(docAcc, id, [nsIAccessibleTableCell]); + } + is(cells.a.rowExtent, 1, "a rowExtent correct"); + is(cells.a.columnExtent, 1, "a columnExtent correct"); + testHeaderCells([ + { + cell: cells.c, + rowHeaderCells: [], + columnHeaderCells: [cells.a], + }, + { + cell: cells.d, + rowHeaderCells: [cells.c], + columnHeaderCells: [cells.b], + }, + ]); + }, + { + chrome: true, + topLevel: isCacheEnabled, + iframe: isCacheEnabled, + remoteIframe: isCacheEnabled, + } +); + +function setNodeHidden(browser, id, hidden) { + return invokeContentTask(browser, [id, hidden], (cId, cHidden) => { + content.document.getElementById(cId).hidden = cHidden; + }); +} + +/** + * Test that the table is updated correctly when it is mutated. + */ +addAccessibleTask( + ` + + + +
ab
+ `, + async function(browser, docAcc) { + const table = findAccessibleChildByID(docAcc, "table", [ + nsIAccessibleTable, + ]); + is(table.rowCount, 1, "table rowCount correct"); + is(table.columnCount, 2, "table columnCount correct"); + testTableIndexes(table, [[0, 1]]); + info("Showing r2"); + let reordered = waitForEvent(EVENT_REORDER, table); + setNodeHidden(browser, "r2", false); + await reordered; + is(table.rowCount, 2, "table rowCount correct"); + testTableIndexes(table, [ + [0, 1], + [2, 3], + ]); + info("Hiding r2"); + reordered = waitForEvent(EVENT_REORDER, table); + setNodeHidden(browser, "r2", true); + await reordered; + is(table.rowCount, 1, "table rowCount correct"); + testTableIndexes(table, [[0, 1]]); + info("Hiding b"); + reordered = waitForEvent(EVENT_REORDER, "r1"); + setNodeHidden(browser, "b", true); + await reordered; + is(table.columnCount, 1, "table columnCount correct"); + testTableIndexes(table, [[0]]); + info("Showing b"); + reordered = waitForEvent(EVENT_REORDER, "r1"); + setNodeHidden(browser, "b", false); + await reordered; + is(table.columnCount, 2, "table columnCount correct"); + }, + { + chrome: true, + topLevel: isCacheEnabled, + iframe: isCacheEnabled, + remoteIframe: isCacheEnabled, + } +); diff -Nru firefox-99.0.1+build1/accessible/tests/browser/e10s/browser_caching_text.js firefox-100.0+build1/accessible/tests/browser/e10s/browser_caching_text.js --- firefox-99.0.1+build1/accessible/tests/browser/e10s/browser_caching_text.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/tests/browser/e10s/browser_caching_text.js 2022-04-26 05:44:41.000000000 +0000 @@ -11,14 +11,6 @@ { name: "attributes.js", dir: MOCHITESTS_DIR } ); -const isCacheEnabled = Services.prefs.getBoolPref( - "accessibility.cache.enabled", - false -); -// Some RemoteAccessible methods aren't supported on Windows when the cache is -// disabled. -const isWinNoCache = !isCacheEnabled && AppConstants.platform == "win"; - /** * Test line and word offsets for various cases for both local and remote * Accessibles. There is more extensive coverage in ../../mochitest/text. These diff -Nru firefox-99.0.1+build1/accessible/tests/browser/e10s/browser.ini firefox-100.0+build1/accessible/tests/browser/e10s/browser.ini --- firefox-99.0.1+build1/accessible/tests/browser/e10s/browser.ini 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/tests/browser/e10s/browser.ini 2022-04-26 05:44:41.000000000 +0000 @@ -22,6 +22,7 @@ skip-if = (os == "linux" && bits == 64) || (debug && os == "mac") || (debug && os == "win") #Bug 1388256 [browser_caching_relations.js] [browser_caching_states.js] +[browser_caching_table.js] [browser_caching_text.js] [browser_caching_value.js] [browser_caching_uniqueid.js] diff -Nru firefox-99.0.1+build1/accessible/tests/browser/e10s/browser_obj_group.js firefox-100.0+build1/accessible/tests/browser/e10s/browser_obj_group.js --- firefox-99.0.1+build1/accessible/tests/browser/e10s/browser_obj_group.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/tests/browser/e10s/browser_obj_group.js 2022-04-26 05:44:41.000000000 +0000 @@ -7,11 +7,6 @@ /* import-globals-from ../../mochitest/attributes.js */ loadScripts({ name: "attributes.js", dir: MOCHITESTS_DIR }); -const isCacheEnabled = Services.prefs.getBoolPref( - "accessibility.cache.enabled", - false -); - /** * select elements */ diff -Nru firefox-99.0.1+build1/accessible/tests/browser/events/browser.ini firefox-100.0+build1/accessible/tests/browser/events/browser.ini --- firefox-99.0.1+build1/accessible/tests/browser/events/browser.ini 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/tests/browser/events/browser.ini 2022-04-26 05:44:41.000000000 +0000 @@ -8,6 +8,7 @@ environment = A11YLOG=doclifecycle,events,notifications +[browser_test_caret_move_granularity.js] [browser_test_docload.js] skip-if = e10s [browser_test_scrolling.js] diff -Nru firefox-99.0.1+build1/accessible/tests/browser/events/browser_test_caret_move_granularity.js firefox-100.0+build1/accessible/tests/browser/events/browser_test_caret_move_granularity.js --- firefox-99.0.1+build1/accessible/tests/browser/events/browser_test_caret_move_granularity.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-100.0+build1/accessible/tests/browser/events/browser_test_caret_move_granularity.js 2022-04-26 05:44:41.000000000 +0000 @@ -0,0 +1,102 @@ +/* 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/. */ + +"use strict"; + +const CLUSTER_AMOUNT = Ci.nsISelectionListener.CLUSTER_AMOUNT; +const WORD_AMOUNT = Ci.nsISelectionListener.WORD_AMOUNT; +const LINE_AMOUNT = Ci.nsISelectionListener.LINE_AMOUNT; +const BEGINLINE_AMOUNT = Ci.nsISelectionListener.BEGINLINE_AMOUNT; +const ENDLINE_AMOUNT = Ci.nsISelectionListener.ENDLINE_AMOUNT; + +const isMac = AppConstants.platform == "macosx"; + +function matchCaretMoveEvent(id, caretOffset) { + return evt => { + evt.QueryInterface(nsIAccessibleCaretMoveEvent); + return ( + getAccessibleDOMNodeID(evt.accessible) == id && + evt.caretOffset == caretOffset + ); + }; +} + +addAccessibleTask( + ``, + async function(browser, accDoc) { + const textarea = findAccessibleChildByID(accDoc, "textarea"); + let caretMoved = waitForEvent( + EVENT_TEXT_CARET_MOVED, + matchCaretMoveEvent("textarea", 0) + ); + textarea.takeFocus(); + let evt = await caretMoved; + evt.QueryInterface(nsIAccessibleCaretMoveEvent); + ok(!evt.isAtEndOfLine, "Caret is not at end of line"); + + caretMoved = waitForEvent( + EVENT_TEXT_CARET_MOVED, + matchCaretMoveEvent("textarea", 1) + ); + EventUtils.synthesizeKey("KEY_ArrowRight"); + evt = await caretMoved; + evt.QueryInterface(nsIAccessibleCaretMoveEvent); + ok(!evt.isAtEndOfLine, "Caret is not at end of line"); + is(evt.granularity, CLUSTER_AMOUNT, "Caret moved by cluster"); + + caretMoved = waitForEvent( + EVENT_TEXT_CARET_MOVED, + matchCaretMoveEvent("textarea", 15) + ); + EventUtils.synthesizeKey("KEY_ArrowDown"); + evt = await caretMoved; + evt.QueryInterface(nsIAccessibleCaretMoveEvent); + todo(!evt.isAtEndOfLine, "Caret is not at end of line"); + is(evt.granularity, LINE_AMOUNT, "Caret moved by line"); + + caretMoved = waitForEvent( + EVENT_TEXT_CARET_MOVED, + matchCaretMoveEvent("textarea", 14) + ); + if (isMac) { + EventUtils.synthesizeKey("KEY_ArrowLeft", { metaKey: true }); + } else { + EventUtils.synthesizeKey("KEY_Home"); + } + evt = await caretMoved; + evt.QueryInterface(nsIAccessibleCaretMoveEvent); + ok(!evt.isAtEndOfLine, "Caret is not at end of line"); + is(evt.granularity, BEGINLINE_AMOUNT, "Caret moved to line start"); + + caretMoved = waitForEvent( + EVENT_TEXT_CARET_MOVED, + matchCaretMoveEvent("textarea", 28) + ); + if (isMac) { + EventUtils.synthesizeKey("KEY_ArrowRight", { metaKey: true }); + } else { + EventUtils.synthesizeKey("KEY_End"); + } + evt = await caretMoved; + evt.QueryInterface(nsIAccessibleCaretMoveEvent); + ok(evt.isAtEndOfLine, "Caret is at end of line"); + is(evt.granularity, ENDLINE_AMOUNT, "Caret moved to line end"); + + caretMoved = waitForEvent( + EVENT_TEXT_CARET_MOVED, + matchCaretMoveEvent("textarea", 24) + ); + if (isMac) { + EventUtils.synthesizeKey("KEY_ArrowLeft", { altKey: true }); + } else { + EventUtils.synthesizeKey("KEY_ArrowLeft", { ctrlKey: true }); + } + evt = await caretMoved; + evt.QueryInterface(nsIAccessibleCaretMoveEvent); + ok(!evt.isAtEndOfLine, "Caret is not at end of line"); + is(evt.granularity, WORD_AMOUNT, "Caret moved by word"); + } +); diff -Nru firefox-99.0.1+build1/accessible/tests/browser/mac/browser_text_input.js firefox-100.0+build1/accessible/tests/browser/mac/browser_text_input.js --- firefox-99.0.1+build1/accessible/tests/browser/mac/browser_text_input.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/tests/browser/mac/browser_text_input.js 2022-04-26 05:44:41.000000000 +0000 @@ -224,7 +224,7 @@ ); } -async function focusIntoInputAndType(accDoc, inputId, innerContainerId) { +async function focusIntoInput(accDoc, inputId, innerContainerId) { let selectionId = innerContainerId ? innerContainerId : inputId; let input = getNativeInterface(accDoc, inputId); ok(!input.getAttributeValue("AXFocused"), "input is not focused"); @@ -249,6 +249,11 @@ ]); input.setAttributeValue("AXFocused", true); await events; +} + +async function focusIntoInputAndType(accDoc, inputId, innerContainerId) { + let selectionId = innerContainerId ? innerContainerId : inputId; + await focusIntoInput(accDoc, inputId, innerContainerId); async function testTextInput( synthKey, @@ -337,14 +342,14 @@ { AXTextStateChangeType: AXTextStateChangeTypeSelectionMove } ); await synthKeyAndTestSelectionChanged( - "KEY_Home", - { shiftKey: true }, + "KEY_ArrowLeft", + { shiftKey: true, metaKey: true }, selectionId, "hello ", { AXTextStateChangeType: AXTextStateChangeTypeSelectionExtend, - AXTextSelectionDirection: AXTextSelectionDirectionPrevious, - AXTextSelectionGranularity: AXTextSelectionGranularityWord, + AXTextSelectionDirection: AXTextSelectionDirectionBeginning, + AXTextSelectionGranularity: AXTextSelectionGranularityLine, } ); await synthKeyAndTestSelectionChanged( @@ -372,7 +377,8 @@ `link `, async (browser, accDoc) => { await focusIntoInputAndType(accDoc, "input"); - } + }, + { topLevel: true, iframe: true, remoteIframe: true } ); // Test content editable @@ -390,15 +396,6 @@ } ); -// Test text input in iframe -addAccessibleTask( - `link `, - async (browser, accDoc) => { - await focusIntoInputAndType(accDoc, "input"); - }, - { iframe: true } -); - // Test input that gets role::EDITCOMBOBOX addAccessibleTask(``, async (browser, accDoc) => { const box = getNativeInterface(accDoc, "box"); @@ -410,3 +407,48 @@ ); await focusIntoInputAndType(accDoc, "box"); }); + +// Test multiline caret control in a text area +addAccessibleTask( + ``, + async (browser, accDoc) => { + await focusIntoInput(accDoc, "input"); + + await synthKeyAndTestSelectionChanged("KEY_ArrowRight", null, "input", "", { + AXTextStateChangeType: AXTextStateChangeTypeSelectionMove, + AXTextSelectionDirection: AXTextSelectionDirectionNext, + AXTextSelectionGranularity: AXTextSelectionGranularityCharacter, + }); + + await synthKeyAndTestSelectionChanged("KEY_ArrowDown", null, "input", "", { + AXTextStateChangeType: AXTextStateChangeTypeSelectionMove, + AXTextSelectionDirection: AXTextSelectionDirectionNext, + AXTextSelectionGranularity: AXTextSelectionGranularityLine, + }); + + await synthKeyAndTestSelectionChanged( + "KEY_ArrowLeft", + { metaKey: true }, + "input", + "", + { + AXTextStateChangeType: AXTextStateChangeTypeSelectionMove, + AXTextSelectionDirection: AXTextSelectionDirectionBeginning, + AXTextSelectionGranularity: AXTextSelectionGranularityLine, + } + ); + + await synthKeyAndTestSelectionChanged( + "KEY_ArrowRight", + { metaKey: true }, + "input", + "", + { + AXTextStateChangeType: AXTextStateChangeTypeSelectionMove, + AXTextSelectionDirection: AXTextSelectionDirectionEnd, + AXTextSelectionGranularity: AXTextSelectionGranularityLine, + } + ); + }, + { topLevel: true, iframe: true, remoteIframe: true } +); diff -Nru firefox-99.0.1+build1/accessible/tests/browser/mac/head.js firefox-100.0+build1/accessible/tests/browser/mac/head.js --- firefox-99.0.1+build1/accessible/tests/browser/mac/head.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/tests/browser/mac/head.js 2022-04-26 05:44:41.000000000 +0000 @@ -10,7 +10,9 @@ AXTextStateChangeTypeSelectionExtend, AXTextSelectionDirectionUnknown, AXTextSelectionDirectionPrevious, AXTextSelectionDirectionNext, AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown, - AXTextSelectionGranularityCharacter, AXTextSelectionGranularityWord */ + AXTextSelectionDirectionBeginning, AXTextSelectionDirectionEnd, + AXTextSelectionGranularityCharacter, AXTextSelectionGranularityWord, + AXTextSelectionGranularityLine */ // Load the shared-head file first. /* import-globals-from ../shared-head.js */ @@ -37,6 +39,8 @@ // AXTextSelectionDirection enum values const AXTextSelectionDirectionUnknown = 0; +const AXTextSelectionDirectionBeginning = 1; +const AXTextSelectionDirectionEnd = 2; const AXTextSelectionDirectionPrevious = 3; const AXTextSelectionDirectionNext = 4; const AXTextSelectionDirectionDiscontiguous = 5; @@ -45,6 +49,7 @@ const AXTextSelectionGranularityUnknown = 0; const AXTextSelectionGranularityCharacter = 1; const AXTextSelectionGranularityWord = 2; +const AXTextSelectionGranularityLine = 3; function getNativeInterface(accDoc, id) { return findAccessibleChildByID(accDoc, id).nativeInterface.QueryInterface( diff -Nru firefox-99.0.1+build1/accessible/tests/browser/scroll/browser.ini firefox-100.0+build1/accessible/tests/browser/scroll/browser.ini --- firefox-99.0.1+build1/accessible/tests/browser/scroll/browser.ini 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/tests/browser/scroll/browser.ini 2022-04-26 05:44:41.000000000 +0000 @@ -8,3 +8,4 @@ [browser_test_zoom_text.js] skip-if = e10s && os == 'win' # bug 1372296 +[browser_test_scroll_bounds.js] diff -Nru firefox-99.0.1+build1/accessible/tests/browser/scroll/browser_test_scroll_bounds.js firefox-100.0+build1/accessible/tests/browser/scroll/browser_test_scroll_bounds.js --- firefox-99.0.1+build1/accessible/tests/browser/scroll/browser_test_scroll_bounds.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-100.0+build1/accessible/tests/browser/scroll/browser_test_scroll_bounds.js 2022-04-26 05:44:41.000000000 +0000 @@ -0,0 +1,120 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/layout.js */ +loadScripts({ name: "layout.js", dir: MOCHITESTS_DIR }); + +const appUnitsPerDevPixel = 60; + +function testCachedScrollPosition(acc, expectedX, expectedY) { + let cachedPosition = ""; + try { + cachedPosition = acc.cache.getStringProperty("scroll-position"); + } catch (e) { + // `getStringProperty` will throw an exception if we ask for + // a domain it doesn't have -- catch the exception here to + // prevent it from taking down the whole test. + console.info("Unable to fetch scroll position from cache!"); + } + + // The value we retrieve from the cache is in app units, but the values + // passed in are in pixels. Since the retrieved value is a string, + // and harder to modify, adjust our expected x and y values to match its units. + return ( + cachedPosition == + `${expectedX * appUnitsPerDevPixel}, ${expectedY * appUnitsPerDevPixel}` + ); +} + +/** + * Test bounds of accessibles after scrolling + */ +addAccessibleTask( + ` +
+
+ +
+
+ `, + async function(browser, docAcc) { + ok(docAcc, "iframe document acc is present"); + await testBoundsInContent(docAcc, "square", browser); + await testBoundsInContent(docAcc, "rect", browser); + + await invokeContentTask(browser, [], () => { + content.document.getElementById("square").scrollIntoView(); + }); + + await waitForContentPaint(browser); + + await testBoundsInContent(docAcc, "square", browser); + await testBoundsInContent(docAcc, "rect", browser); + + await invokeContentTask(browser, [], () => { + content.document.getElementById("rect").scrollIntoView(); + }); + + await waitForContentPaint(browser); + await testBoundsInContent(docAcc, "square", browser); + await testBoundsInContent(docAcc, "rect", browser); + }, + { iframe: true, remoteIframe: true, chrome: true } +); + +/** + * Test scroll offset on cached accessibles + */ +addAccessibleTask( + ` +
+
+ +
+
+ `, + async function(browser, docAcc) { + // We can only access the `cache` attribute of an accessible when + // the cache is enabled and we're in a remote browser. Verify + // both these conditions hold, and return early if they don't. + if (!isCacheEnabled || !browser.isRemoteBrowser) { + return; + } + + ok(docAcc, "iframe document acc is present"); + await untilCacheOk( + () => testCachedScrollPosition(docAcc, 0, 0), + "Correct initial scroll position." + ); + + await invokeContentTask(browser, [], () => { + content.document.getElementById("square").scrollIntoView(); + }); + + await waitForContentPaint(browser); + + // The only content to scroll over is `square`'s top margin + // so our scroll offset here should be 3000px + await untilCacheOk( + () => testCachedScrollPosition(docAcc, 0, 3000), + "Correct scroll position after first scroll." + ); + + await invokeContentTask(browser, [], () => { + content.document.getElementById("rect").scrollIntoView(); + }); + + await waitForContentPaint(browser); + // We have to scroll over `square`'s top margin (3000px), + // `square` itself (100px), and `square`'s bottom margin (4000px). + // This should give us a 7100px offset. + await untilCacheOk( + () => testCachedScrollPosition(docAcc, 0, 7100), + "Correct final scroll position." + ); + }, + { iframe: true, remoteIframe: true } +); diff -Nru firefox-99.0.1+build1/accessible/tests/browser/scroll/browser_test_zoom_text.js firefox-100.0+build1/accessible/tests/browser/scroll/browser_test_zoom_text.js --- firefox-99.0.1+build1/accessible/tests/browser/scroll/browser_test_zoom_text.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/tests/browser/scroll/browser_test_zoom_text.js 2022-04-26 05:44:41.000000000 +0000 @@ -7,14 +7,6 @@ /* import-globals-from ../../mochitest/layout.js */ loadScripts({ name: "layout.js", dir: MOCHITESTS_DIR }); -async function waitForContentPaint(browser) { - await SpecialPowers.spawn(browser, [], () => { - return new Promise(function(r) { - content.requestAnimationFrame(() => content.setTimeout(r)); - }); - }); -} - async function runTests(browser, accDoc) { await loadContentScripts(browser, "Layout.jsm"); diff -Nru firefox-99.0.1+build1/accessible/tests/browser/selectable/head.js firefox-100.0+build1/accessible/tests/browser/selectable/head.js --- firefox-99.0.1+build1/accessible/tests/browser/selectable/head.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/tests/browser/selectable/head.js 2022-04-26 05:44:41.000000000 +0000 @@ -4,7 +4,7 @@ "use strict"; -/* exported testMultiSelectable, isCacheEnabled, isWinNoCache */ +/* exported testMultiSelectable */ // Load the shared-head file first. /* import-globals-from ../shared-head.js */ @@ -25,14 +25,6 @@ { name: "role.js", dir: MOCHITESTS_DIR } ); -const isCacheEnabled = Services.prefs.getBoolPref( - "accessibility.cache.enabled", - false -); -// Some RemoteAccessible methods aren't supported on Windows when the cache is -// disabled. -const isWinNoCache = !isCacheEnabled && AppConstants.platform == "win"; - // Handle case where multiple selection change events are coalesced into // a SELECTION_WITHIN event. Promise resolves to true in that case. function multipleSelectionChanged(widget, changedChildren, selected) { diff -Nru firefox-99.0.1+build1/accessible/tests/browser/shared-head.js firefox-100.0+build1/accessible/tests/browser/shared-head.js --- firefox-99.0.1+build1/accessible/tests/browser/shared-head.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/tests/browser/shared-head.js 2022-04-26 05:44:41.000000000 +0000 @@ -5,16 +5,17 @@ "use strict"; /* import-globals-from ../mochitest/common.js */ +/* import-globals-from ../mochitest/layout.js */ /* import-globals-from ../mochitest/promisified-events.js */ -/* exported Logger, MOCHITESTS_DIR, invokeSetAttribute, invokeFocus, +/* exported Logger, MOCHITESTS_DIR, isCacheEnabled, isWinNoCache, invokeSetAttribute, invokeFocus, invokeSetStyle, getAccessibleDOMNodeID, getAccessibleTagName, addAccessibleTask, findAccessibleChildByID, isDefunct, CURRENT_CONTENT_DIR, loadScripts, loadContentScripts, snippetToURL, Cc, Cu, arrayFromChildren, forceGC, contentSpawnMutation, DEFAULT_IFRAME_ID, DEFAULT_IFRAME_DOC_BODY_ID, invokeContentTask, matchContentDoc, currentContentDoc, getContentDPR, - waitForImageMap, getContentBoundsForDOMElm, untilCacheIs, untilCacheOk */ + waitForImageMap, getContentBoundsForDOMElm, untilCacheIs, untilCacheOk, testBoundsInContent, waitForContentPaint */ const CURRENT_FILE_DIR = "/browser/accessible/tests/browser/"; @@ -42,6 +43,15 @@ const HTML_MIME_TYPE = "text/html"; const XHTML_MIME_TYPE = "application/xhtml+xml"; +const isCacheEnabled = Services.prefs.getBoolPref( + "accessibility.cache.enabled", + false +); + +// Some RemoteAccessible methods aren't supported on Windows when the cache is +// disabled. +const isWinNoCache = !isCacheEnabled && AppConstants.platform == "win"; + function loadHTMLFromFile(path) { // Load the HTML to return in the response from file. // Since it's relative to the cwd of the test runner, we start there and @@ -358,7 +368,8 @@ } else { srcURL.searchParams.append( "html", - ` + ` + Accessibility Fission Test @@ -377,7 +388,8 @@ `` ); } else { - doc = `${doc}`; + doc = ` + ${doc}`; } src = `data:${mimeType};charset=utf-8,${encodeURIComponent(doc)}`; @@ -414,7 +426,8 @@ } const encodedDoc = encodeURIComponent( - ` + ` + Accessibility Test @@ -860,3 +873,54 @@ () => [retrievalFunc(), expected, message] ).then(([got, exp, msg]) => is(got, exp, msg)); } + +async function waitForContentPaint(browser) { + await SpecialPowers.spawn(browser, [], () => { + return new Promise(function(r) { + content.requestAnimationFrame(() => content.setTimeout(r)); + }); + }); +} + +async function testBoundsInContent(iframeDocAcc, id, browser) { + const acc = findAccessibleChildByID(iframeDocAcc, id); + const x = {}; + const y = {}; + const width = {}; + const height = {}; + acc.getBounds(x, y, width, height); + + await invokeContentTask( + browser, + [id, x.value, y.value, width.value, height.value], + (_id, _x, _y, _width, _height) => { + const { Layout: LayoutUtils } = ChromeUtils.import( + "chrome://mochitests/content/browser/accessible/tests/browser/Layout.jsm" + ); + + let [ + expectedX, + expectedY, + expectedWidth, + expectedHeight, + ] = LayoutUtils.getBoundsForDOMElm(_id, content.document); + + ok( + _x >= expectedX - 5 || _x <= expectedX + 5, + "Got " + _x + ", accurate x for " + _id + ); + ok( + _y >= expectedY - 5 || _y <= expectedY + 5, + "Got " + _y + ", accurate y for " + _id + ); + ok( + _width >= expectedWidth - 5 || _width <= expectedWidth + 5, + "Got " + _width + ", accurate width for " + _id + ); + ok( + _height >= expectedHeight - 5 || _height <= expectedHeight + 5, + "Got " + _height + ", accurate height for " + _id + ); + } + ); +} diff -Nru firefox-99.0.1+build1/accessible/tests/mochitest/name/test_general.html firefox-100.0+build1/accessible/tests/mochitest/name/test_general.html --- firefox-99.0.1+build1/accessible/tests/mochitest/name/test_general.html 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/tests/mochitest/name/test_general.html 2022-04-26 05:44:41.000000000 +0000 @@ -58,10 +58,9 @@ // spaces) testName("btn_labelledby_mixed_block", "text more text"); - // Gets the name from text nodes contained by html:td (every text node - // value in the name should be devided by spaces). - // XXX: this case is rather a feature than strong wanted behaviour. - testName("btn_labelledby_mixed_table", "text space text"); + // Gets the name from text nodes contained by html:td. The text nodes + // should not be divided by spaces. + testName("btn_labelledby_mixed_table", "textTEXTtext"); // Gets the name from image accessible. testName("btn_labelledby_mixed_img", "text image"); @@ -251,6 +250,17 @@ // Name from subtree of grouping labelled by an ancestor. testName("grouping_labelledby_ancestor", "label"); + // Name from subtree of a container containing text nodes and inline + // elements. There should be no spaces because everyhing is inline. + testName("container_text_inline", "abc"); + // Name from subtree of a container containing text nodes and block + // elements. There should be a space on both sides of the block. + testName("container_text_block", "a b c"); + // Name from subtree of a container containing text nodes and empty + // block elements. There should be space on either side of the blocks, but + // not a double space. + testName("container_text_emptyblock", "a b"); + SimpleTest.finish(); } @@ -387,7 +397,7 @@ - +
textspacetexttextTEXTtext
@@ -711,5 +721,12 @@ This content should not be included in the grouping's label. + + +
abc
+ +
a

b

c
+ +
a

b
diff -Nru firefox-99.0.1+build1/accessible/tests/mochitest/states.js firefox-100.0+build1/accessible/tests/mochitest/states.js --- firefox-99.0.1+build1/accessible/tests/mochitest/states.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/tests/mochitest/states.js 2022-04-26 05:44:41.000000000 +0000 @@ -56,6 +56,7 @@ nsIAccessibleStates.EXT_STATE_SUPPORTS_AUTOCOMPLETION; const EXT_STATE_VERTICAL = nsIAccessibleStates.EXT_STATE_VERTICAL; const EXT_STATE_SELECTABLE_TEXT = nsIAccessibleStates.EXT_STATE_SELECTABLE_TEXT; +const EXT_STATE_OPAQUE = nsIAccessibleStates.EXT_STATE_OPAQUE; const kOrdinalState = false; const kExtraState = 1; diff -Nru firefox-99.0.1+build1/accessible/tests/mochitest/text/test_hypertext.html firefox-100.0+build1/accessible/tests/mochitest/text/test_hypertext.html --- firefox-99.0.1+build1/accessible/tests/mochitest/text/test_hypertext.html 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/tests/mochitest/text/test_hypertext.html 2022-04-26 05:44:41.000000000 +0000 @@ -145,5 +145,6 @@
foo
+
diff -Nru firefox-99.0.1+build1/accessible/windows/ia2/ia2AccessibleTableCell.cpp firefox-100.0+build1/accessible/windows/ia2/ia2AccessibleTableCell.cpp --- firefox-99.0.1+build1/accessible/windows/ia2/ia2AccessibleTableCell.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/windows/ia2/ia2AccessibleTableCell.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -10,19 +10,18 @@ #include "AccessibleTable2_i.c" #include "AccessibleTableCell_i.c" -#include "AccessibleWrap.h" -#include "TableAccessible.h" -#include "TableCellAccessible.h" #include "IUnknownImpl.h" - +#include "mozilla/a11y/Accessible.h" +#include "mozilla/a11y/TableAccessibleBase.h" +#include "mozilla/a11y/TableCellAccessibleBase.h" #include "nsCOMPtr.h" #include "nsString.h" using namespace mozilla::a11y; -TableCellAccessible* ia2AccessibleTableCell::CellAcc() { - AccessibleWrap* acc = LocalAcc(); - return acc ? acc->AsTableCell() : nullptr; +TableCellAccessibleBase* ia2AccessibleTableCell::CellAcc() { + Accessible* acc = Acc(); + return acc ? acc->AsTableCellBase() : nullptr; } // IUnknown @@ -38,15 +37,14 @@ if (!aTable) return E_INVALIDARG; *aTable = nullptr; - TableCellAccessible* tableCell = CellAcc(); + TableCellAccessibleBase* tableCell = CellAcc(); if (!tableCell) return CO_E_OBJNOTCONNECTED; - TableAccessible* table = tableCell->Table(); + TableAccessibleBase* table = tableCell->Table(); if (!table) return E_FAIL; - AccessibleWrap* wrap = static_cast(table->AsAccessible()); - RefPtr result; - wrap->GetNativeInterface(getter_AddRefs(result)); + Accessible* tableAcc = table->AsAccessible(); + RefPtr result = MsaaAccessible::GetFrom(tableAcc); result.forget(aTable); return S_OK; } @@ -56,7 +54,7 @@ if (!aSpan) return E_INVALIDARG; *aSpan = 0; - TableCellAccessible* tableCell = CellAcc(); + TableCellAccessibleBase* tableCell = CellAcc(); if (!tableCell) return CO_E_OBJNOTCONNECTED; *aSpan = tableCell->ColExtent(); @@ -71,10 +69,10 @@ *aCellAccessibles = nullptr; *aNColumnHeaderCells = 0; - TableCellAccessible* tableCell = CellAcc(); + TableCellAccessibleBase* tableCell = CellAcc(); if (!tableCell) return CO_E_OBJNOTCONNECTED; - AutoTArray cells; + AutoTArray cells; tableCell->ColHeaderCells(&cells); *aNColumnHeaderCells = cells.Length(); @@ -84,9 +82,7 @@ if (!*aCellAccessibles) return E_OUTOFMEMORY; for (uint32_t i = 0; i < cells.Length(); i++) { - AccessibleWrap* cell = static_cast(cells[i]); - RefPtr iaCell; - cell->GetNativeInterface(getter_AddRefs(iaCell)); + RefPtr iaCell = MsaaAccessible::GetFrom(cells[i]); iaCell.forget(&(*aCellAccessibles)[i]); } @@ -98,7 +94,7 @@ if (!aColIdx) return E_INVALIDARG; *aColIdx = -1; - TableCellAccessible* tableCell = CellAcc(); + TableCellAccessibleBase* tableCell = CellAcc(); if (!tableCell) return CO_E_OBJNOTCONNECTED; *aColIdx = tableCell->ColIdx(); @@ -110,7 +106,7 @@ if (!aSpan) return E_INVALIDARG; *aSpan = 0; - TableCellAccessible* tableCell = CellAcc(); + TableCellAccessibleBase* tableCell = CellAcc(); if (!tableCell) return CO_E_OBJNOTCONNECTED; *aSpan = tableCell->RowExtent(); @@ -124,10 +120,10 @@ *aCellAccessibles = nullptr; *aNRowHeaderCells = 0; - TableCellAccessible* tableCell = CellAcc(); + TableCellAccessibleBase* tableCell = CellAcc(); if (!tableCell) return CO_E_OBJNOTCONNECTED; - AutoTArray cells; + AutoTArray cells; tableCell->RowHeaderCells(&cells); *aNRowHeaderCells = cells.Length(); @@ -136,9 +132,7 @@ if (!*aCellAccessibles) return E_OUTOFMEMORY; for (uint32_t i = 0; i < cells.Length(); i++) { - AccessibleWrap* cell = static_cast(cells[i]); - RefPtr iaCell; - cell->GetNativeInterface(getter_AddRefs(iaCell)); + RefPtr iaCell = MsaaAccessible::GetFrom(cells[i]); iaCell.forget(&(*aCellAccessibles)[i]); } @@ -150,7 +144,7 @@ if (!aRowIdx) return E_INVALIDARG; *aRowIdx = -1; - TableCellAccessible* tableCell = CellAcc(); + TableCellAccessibleBase* tableCell = CellAcc(); if (!tableCell) return CO_E_OBJNOTCONNECTED; *aRowIdx = tableCell->RowIdx(); @@ -167,7 +161,7 @@ *aRowIdx = *aColIdx = *aRowExtents = *aColExtents = 0; *aIsSelected = false; - TableCellAccessible* tableCell = CellAcc(); + TableCellAccessibleBase* tableCell = CellAcc(); if (!tableCell) return CO_E_OBJNOTCONNECTED; *aRowIdx = tableCell->RowIdx(); @@ -184,7 +178,7 @@ if (!aIsSelected) return E_INVALIDARG; *aIsSelected = false; - TableCellAccessible* tableCell = CellAcc(); + TableCellAccessibleBase* tableCell = CellAcc(); if (!tableCell) return CO_E_OBJNOTCONNECTED; *aIsSelected = tableCell->Selected(); diff -Nru firefox-99.0.1+build1/accessible/windows/ia2/ia2AccessibleTableCell.h firefox-100.0+build1/accessible/windows/ia2/ia2AccessibleTableCell.h --- firefox-99.0.1+build1/accessible/windows/ia2/ia2AccessibleTableCell.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/windows/ia2/ia2AccessibleTableCell.h 2022-04-26 05:44:41.000000000 +0000 @@ -14,7 +14,7 @@ namespace mozilla { namespace a11y { -class TableCellAccessible; +class TableCellAccessibleBase; class ia2AccessibleTableCell : public IAccessibleTableCell, public ia2AccessibleHypertext { @@ -62,7 +62,7 @@ using ia2AccessibleHypertext::ia2AccessibleHypertext; private: - TableCellAccessible* CellAcc(); + TableCellAccessibleBase* CellAcc(); }; } // namespace a11y diff -Nru firefox-99.0.1+build1/accessible/windows/ia2/ia2AccessibleTable.cpp firefox-100.0+build1/accessible/windows/ia2/ia2AccessibleTable.cpp --- firefox-99.0.1+build1/accessible/windows/ia2/ia2AccessibleTable.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/windows/ia2/ia2AccessibleTable.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -11,19 +11,18 @@ #include "AccessibleTable_i.c" #include "AccessibleTable2_i.c" -#include "AccessibleWrap.h" #include "IUnknownImpl.h" -#include "Statistics.h" -#include "TableAccessible.h" - +#include "mozilla/a11y/Accessible.h" +#include "mozilla/a11y/TableAccessibleBase.h" #include "nsCOMPtr.h" #include "nsString.h" +#include "Statistics.h" using namespace mozilla::a11y; -TableAccessible* ia2AccessibleTable::TableAcc() { - AccessibleWrap* acc = LocalAcc(); - return acc ? acc->AsTable() : nullptr; +TableAccessibleBase* ia2AccessibleTable::TableAcc() { + Accessible* acc = Acc(); + return acc ? acc->AsTableBase() : nullptr; } // IUnknown @@ -64,14 +63,13 @@ if (!aAccessible) return E_INVALIDARG; *aAccessible = nullptr; - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; - AccessibleWrap* caption = static_cast(table->Caption()); + Accessible* caption = table->Caption(); if (!caption) return S_FALSE; - RefPtr result; - caption->GetNativeInterface(getter_AddRefs(result)); + RefPtr result = MsaaAccessible::GetFrom(caption); result.forget(aAccessible); return S_OK; } @@ -82,7 +80,7 @@ if (!aChildIdx) return E_INVALIDARG; *aChildIdx = 0; - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aRowIdx < 0 || aColIdx < 0 || @@ -99,7 +97,7 @@ if (!aDescription) return E_INVALIDARG; *aDescription = nullptr; - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aColIdx < 0 || static_cast(aColIdx) >= table->ColCount()) @@ -119,7 +117,7 @@ if (!aSpan) return E_INVALIDARG; *aSpan = 0; - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aRowIdx < 0 || aColIdx < 0 || @@ -146,7 +144,7 @@ if (!aColIdx) return E_INVALIDARG; *aColIdx = 0; - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aCellIdx < 0) { @@ -167,7 +165,7 @@ if (!aColCount) return E_INVALIDARG; *aColCount = 0; - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; *aColCount = table->ColCount(); @@ -179,7 +177,7 @@ if (!aRowCount) return E_INVALIDARG; *aRowCount = 0; - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; *aRowCount = table->RowCount(); @@ -196,7 +194,7 @@ if (!aColCount) return E_INVALIDARG; *aColCount = 0; - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; *aColCount = table->SelectedColCount(); @@ -208,7 +206,7 @@ if (!aRowCount) return E_INVALIDARG; *aRowCount = 0; - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; *aRowCount = table->SelectedRowCount(); @@ -221,7 +219,7 @@ if (!aDescription) return E_INVALIDARG; *aDescription = nullptr; - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aRowIdx < 0 || static_cast(aRowIdx) >= table->RowCount()) @@ -240,7 +238,7 @@ if (!aSpan) return E_INVALIDARG; *aSpan = 0; - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aRowIdx < 0 || aColIdx < 0 || @@ -267,7 +265,7 @@ if (!aRowIdx) return E_INVALIDARG; *aRowIdx = 0; - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aCellIdx < 0) { @@ -290,7 +288,7 @@ *aChildren = nullptr; *aNChildren = 0; - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; AutoTArray cellIndices; @@ -336,7 +334,7 @@ if (!aIsSelected) return E_INVALIDARG; *aIsSelected = false; - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aColIdx < 0 || static_cast(aColIdx) >= table->ColCount()) @@ -351,7 +349,7 @@ if (!aIsSelected) return E_INVALIDARG; *aIsSelected = false; - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aRowIdx < 0 || static_cast(aRowIdx) >= table->RowCount()) @@ -367,7 +365,7 @@ if (!aIsSelected) return E_INVALIDARG; *aIsSelected = false; - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aRowIdx < 0 || aColIdx < 0 || @@ -381,7 +379,7 @@ STDMETHODIMP ia2AccessibleTable::selectRow(long aRowIdx) { - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aRowIdx < 0 || static_cast(aRowIdx) >= table->RowCount()) @@ -393,7 +391,7 @@ STDMETHODIMP ia2AccessibleTable::selectColumn(long aColIdx) { - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aColIdx < 0 || static_cast(aColIdx) >= table->ColCount()) @@ -405,7 +403,7 @@ STDMETHODIMP ia2AccessibleTable::unselectRow(long aRowIdx) { - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aRowIdx < 0 || static_cast(aRowIdx) >= table->RowCount()) @@ -417,7 +415,7 @@ STDMETHODIMP ia2AccessibleTable::unselectColumn(long aColIdx) { - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aColIdx < 0 || static_cast(aColIdx) >= table->ColCount()) @@ -441,7 +439,7 @@ *aRowExtents = 0; *aColExtents = 0; *aIsSelected = false; - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; if (aCellIdx < 0) { @@ -477,15 +475,13 @@ *aCell = nullptr; - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; - AccessibleWrap* cell = - static_cast(table->CellAt(aRowIdx, aColIdx)); + Accessible* cell = table->CellAt(aRowIdx, aColIdx); if (!cell) return E_INVALIDARG; - RefPtr result; - cell->GetNativeInterface(getter_AddRefs(result)); + RefPtr result = MsaaAccessible::GetFrom(cell); result.forget(aCell); return S_OK; } @@ -495,7 +491,7 @@ if (!aCellCount) return E_INVALIDARG; *aCellCount = 0; - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; *aCellCount = table->SelectedCellCount(); @@ -509,10 +505,10 @@ *aCells = nullptr; *aNSelectedCells = 0; - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; - AutoTArray cells; + AutoTArray cells; table->SelectedCells(&cells); if (cells.IsEmpty()) return S_FALSE; @@ -521,8 +517,7 @@ if (!*aCells) return E_OUTOFMEMORY; for (uint32_t i = 0; i < cells.Length(); i++) { - RefPtr cell; - cells[i]->GetNativeInterface(getter_AddRefs(cell)); + RefPtr cell = MsaaAccessible::GetFrom(cells[i]); cell.forget(&(*aCells)[i]); } @@ -536,7 +531,7 @@ *aColumns = nullptr; *aNColumns = 0; - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; AutoTArray colIndices; @@ -558,7 +553,7 @@ *aRows = nullptr; *aNRows = 0; - TableAccessible* table = TableAcc(); + TableAccessibleBase* table = TableAcc(); if (!table) return CO_E_OBJNOTCONNECTED; AutoTArray rowIndices; diff -Nru firefox-99.0.1+build1/accessible/windows/ia2/ia2AccessibleTable.h firefox-100.0+build1/accessible/windows/ia2/ia2AccessibleTable.h --- firefox-99.0.1+build1/accessible/windows/ia2/ia2AccessibleTable.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/windows/ia2/ia2AccessibleTable.h 2022-04-26 05:44:41.000000000 +0000 @@ -16,7 +16,7 @@ namespace mozilla { namespace a11y { -class TableAccessible; +class TableAccessibleBase; class ia2AccessibleTable : public IAccessibleTable, public IAccessibleTable2, @@ -169,7 +169,7 @@ using ia2AccessibleHypertext::ia2AccessibleHypertext; private: - TableAccessible* TableAcc(); + TableAccessibleBase* TableAcc(); }; } // namespace a11y diff -Nru firefox-99.0.1+build1/accessible/windows/msaa/MsaaAccessible.cpp firefox-100.0+build1/accessible/windows/msaa/MsaaAccessible.cpp --- firefox-99.0.1+build1/accessible/windows/msaa/MsaaAccessible.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/windows/msaa/MsaaAccessible.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -59,14 +59,14 @@ if (aAcc->IsDoc()) { return new MsaaDocAccessible(aAcc); } + if (aAcc->IsTable()) { + return new ia2AccessibleTable(aAcc); + } + if (aAcc->IsTableCell()) { + return new ia2AccessibleTableCell(aAcc); + } if (localAcc) { // XXX These classes don't support RemoteAccessible yet. - if (aAcc->IsTable()) { - return new ia2AccessibleTable(aAcc); - } - if (aAcc->IsTableCell()) { - return new ia2AccessibleTableCell(aAcc); - } if (aAcc->IsApplication()) { return new ia2AccessibleApplication(aAcc); } diff -Nru firefox-99.0.1+build1/accessible/windows/msaa/Platform.cpp firefox-100.0+build1/accessible/windows/msaa/Platform.cpp --- firefox-99.0.1+build1/accessible/windows/msaa/Platform.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/windows/msaa/Platform.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -172,7 +172,8 @@ } void a11y::ProxyCaretMoveEvent(RemoteAccessible* aTarget, - const LayoutDeviceIntRect& aCaretRect) { + const LayoutDeviceIntRect& aCaretRect, + int32_t aGranularity) { AccessibleWrap::UpdateSystemCaretFor(aTarget, aCaretRect); MsaaAccessible::FireWinEvent(aTarget, nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED); diff -Nru firefox-99.0.1+build1/accessible/xpcom/xpcAccessible.cpp firefox-100.0+build1/accessible/xpcom/xpcAccessible.cpp --- firefox-99.0.1+build1/accessible/xpcom/xpcAccessible.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/xpcom/xpcAccessible.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -374,6 +374,36 @@ } NS_IMETHODIMP +xpcAccessible::GetCache(nsIPersistentProperties** aCachedFields) { + NS_ENSURE_ARG_POINTER(aCachedFields); + *aCachedFields = nullptr; + + if (!IntlGeneric()) { + return NS_ERROR_FAILURE; + } + + RefPtr props = new nsPersistentProperties(); + if (IntlGeneric()->IsRemote()) { + RefPtr cachedFields = + IntlGeneric()->AsRemote()->mCachedFields; + + nsAutoString unused; + for (auto iter : *cachedFields) { + nsAutoString name; + iter.NameAsString(name); + + nsAutoString value; + iter.ValueAsString(value); + + props->SetStringProperty(NS_ConvertUTF16toUTF8(name), value, unused); + } + } + + props.forget(aCachedFields); + return NS_OK; +} + +NS_IMETHODIMP xpcAccessible::GetNativeInterface(nsISupports** aNativeInterface) { #ifdef MOZ_WIDGET_COCOA NS_ENSURE_ARG_POINTER(aNativeInterface); diff -Nru firefox-99.0.1+build1/accessible/xpcom/xpcAccessible.h firefox-100.0+build1/accessible/xpcom/xpcAccessible.h --- firefox-99.0.1+build1/accessible/xpcom/xpcAccessible.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/xpcom/xpcAccessible.h 2022-04-26 05:44:41.000000000 +0000 @@ -54,6 +54,8 @@ NS_IMETHOD GetAttributes(nsIPersistentProperties** aAttributes) final; + NS_IMETHOD GetCache(nsIPersistentProperties** aCachedFields) final; + NS_IMETHOD GetNativeInterface(nsISupports** aNativeInterface) final; NS_IMETHOD GetBounds(int32_t* aX, int32_t* aY, int32_t* aWidth, diff -Nru firefox-99.0.1+build1/accessible/xpcom/xpcAccessibleTableCell.cpp firefox-100.0+build1/accessible/xpcom/xpcAccessibleTableCell.cpp --- firefox-99.0.1+build1/accessible/xpcom/xpcAccessibleTableCell.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/xpcom/xpcAccessibleTableCell.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -6,10 +6,10 @@ #include "xpcAccessibleTableCell.h" -#include "LocalAccessible.h" +#include "mozilla/a11y/Accessible.h" +#include "mozilla/a11y/TableAccessibleBase.h" +#include "mozilla/a11y/TableCellAccessibleBase.h" #include "nsIAccessibleTable.h" -#include "TableAccessible.h" -#include "TableCellAccessible.h" #include "nsComponentManagerUtils.h" #include "nsIMutableArray.h" @@ -34,7 +34,7 @@ if (!Intl()) return NS_ERROR_FAILURE; - TableAccessible* table = Intl()->Table(); + TableAccessibleBase* table = Intl()->Table(); if (!table) return NS_ERROR_FAILURE; nsCOMPtr xpcTable = do_QueryInterface( @@ -94,7 +94,7 @@ if (!Intl()) return NS_ERROR_FAILURE; - AutoTArray headerCells; + AutoTArray headerCells; Intl()->ColHeaderCells(&headerCells); nsCOMPtr cells = do_CreateInstance(NS_ARRAY_CONTRACTID); @@ -115,7 +115,7 @@ if (!Intl()) return NS_ERROR_FAILURE; - AutoTArray headerCells; + AutoTArray headerCells; Intl()->RowHeaderCells(&headerCells); nsCOMPtr cells = do_CreateInstance(NS_ARRAY_CONTRACTID); diff -Nru firefox-99.0.1+build1/accessible/xpcom/xpcAccessibleTableCell.h firefox-100.0+build1/accessible/xpcom/xpcAccessibleTableCell.h --- firefox-99.0.1+build1/accessible/xpcom/xpcAccessibleTableCell.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/xpcom/xpcAccessibleTableCell.h 2022-04-26 05:44:41.000000000 +0000 @@ -13,6 +13,7 @@ namespace mozilla { namespace a11y { +class TableCellAccessibleBase; /** * XPCOM wrapper around TableAccessibleCell class. @@ -39,13 +40,7 @@ virtual ~xpcAccessibleTableCell() {} private: - TableCellAccessible* Intl() { - if (LocalAccessible* acc = mIntl->AsLocal()) { - return acc->AsTableCell(); - } - - return nullptr; - } + TableCellAccessibleBase* Intl() { return mIntl->AsTableCellBase(); } xpcAccessibleTableCell(const xpcAccessibleTableCell&) = delete; xpcAccessibleTableCell& operator=(const xpcAccessibleTableCell&) = delete; diff -Nru firefox-99.0.1+build1/accessible/xpcom/xpcAccessibleTable.cpp firefox-100.0+build1/accessible/xpcom/xpcAccessibleTable.cpp --- firefox-99.0.1+build1/accessible/xpcom/xpcAccessibleTable.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/xpcom/xpcAccessibleTable.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -6,8 +6,8 @@ #include "xpcAccessibleTable.h" -#include "LocalAccessible.h" -#include "TableAccessible.h" +#include "mozilla/a11y/Accessible.h" +#include "mozilla/a11y/TableAccessibleBase.h" #include "nsIMutableArray.h" #include "nsComponentManagerUtils.h" @@ -250,12 +250,12 @@ do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); - AutoTArray cellsArray; + AutoTArray cellsArray; Intl()->SelectedCells(&cellsArray); uint32_t totalCount = cellsArray.Length(); for (uint32_t idx = 0; idx < totalCount; idx++) { - LocalAccessible* cell = cellsArray.ElementAt(idx); + Accessible* cell = cellsArray.ElementAt(idx); selCells->AppendElement(static_cast(ToXPC(cell))); } diff -Nru firefox-99.0.1+build1/accessible/xpcom/xpcAccessibleTable.h firefox-100.0+build1/accessible/xpcom/xpcAccessibleTable.h --- firefox-99.0.1+build1/accessible/xpcom/xpcAccessibleTable.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/xpcom/xpcAccessibleTable.h 2022-04-26 05:44:41.000000000 +0000 @@ -12,6 +12,7 @@ namespace mozilla { namespace a11y { +class TableAccessibleBase; /** * XPCOM wrapper around TableAccessible class. @@ -65,9 +66,7 @@ virtual ~xpcAccessibleTable() {} private: - TableAccessible* Intl() { - return mIntl->IsLocal() ? mIntl->AsLocal()->AsTable() : nullptr; - } + TableAccessibleBase* Intl() { return mIntl->AsTableBase(); } xpcAccessibleTable(const xpcAccessibleTable&) = delete; xpcAccessibleTable& operator=(const xpcAccessibleTable&) = delete; diff -Nru firefox-99.0.1+build1/accessible/xul/XULListboxAccessible.cpp firefox-100.0+build1/accessible/xul/XULListboxAccessible.cpp --- firefox-99.0.1+build1/accessible/xul/XULListboxAccessible.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/xul/XULListboxAccessible.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -210,7 +210,7 @@ return selectedRowCount >= 0 ? selectedRowCount : 0; } -void XULListboxAccessible::SelectedCells(nsTArray* aCells) { +void XULListboxAccessible::SelectedCells(nsTArray* aCells) { nsCOMPtr control = Elm()->AsXULMultiSelectControl(); NS_ASSERTION(control, diff -Nru firefox-99.0.1+build1/accessible/xul/XULListboxAccessible.h firefox-100.0+build1/accessible/xul/XULListboxAccessible.h --- firefox-99.0.1+build1/accessible/xul/XULListboxAccessible.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/xul/XULListboxAccessible.h 2022-04-26 05:44:41.000000000 +0000 @@ -66,7 +66,7 @@ virtual uint32_t SelectedCellCount() override; virtual uint32_t SelectedColCount() override; virtual uint32_t SelectedRowCount() override; - virtual void SelectedCells(nsTArray* aCells) 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; diff -Nru firefox-99.0.1+build1/accessible/xul/XULTreeGridAccessible.cpp firefox-100.0+build1/accessible/xul/XULTreeGridAccessible.cpp --- firefox-99.0.1+build1/accessible/xul/XULTreeGridAccessible.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/xul/XULTreeGridAccessible.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -62,7 +62,7 @@ return SelectedItemCount(); } -void XULTreeGridAccessible::SelectedCells(nsTArray* aCells) { +void XULTreeGridAccessible::SelectedCells(nsTArray* aCells) { uint32_t colCount = ColCount(), rowCount = RowCount(); for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) { @@ -489,7 +489,7 @@ uint32_t XULTreeGridCellAccessible::RowIdx() const { return mRow; } void XULTreeGridCellAccessible::ColHeaderCells( - nsTArray* aHeaderCells) { + nsTArray* aHeaderCells) { dom::Element* columnElm = mColumn->Element(); LocalAccessible* headerCell = mDoc->GetAccessible(columnElm); diff -Nru firefox-99.0.1+build1/accessible/xul/XULTreeGridAccessible.h firefox-100.0+build1/accessible/xul/XULTreeGridAccessible.h --- firefox-99.0.1+build1/accessible/xul/XULTreeGridAccessible.h 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/accessible/xul/XULTreeGridAccessible.h 2022-04-26 05:44:41.000000000 +0000 @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -39,7 +40,7 @@ virtual uint32_t SelectedCellCount() override; virtual uint32_t SelectedColCount() override; virtual uint32_t SelectedRowCount() override; - virtual void SelectedCells(nsTArray* aCells) 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; @@ -140,9 +141,8 @@ virtual TableAccessible* Table() const override; virtual uint32_t ColIdx() const override; virtual uint32_t RowIdx() const override; - virtual void ColHeaderCells( - nsTArray* aHeaderCells) override; - virtual void RowHeaderCells(nsTArray* aCells) override {} + virtual void ColHeaderCells(nsTArray* aHeaderCells) override; + virtual void RowHeaderCells(nsTArray* aCells) override {} virtual bool Selected() override; /** diff -Nru firefox-99.0.1+build1/browser/actors/AboutPrivateBrowsingParent.jsm firefox-100.0+build1/browser/actors/AboutPrivateBrowsingParent.jsm --- firefox-99.0.1+build1/browser/actors/AboutPrivateBrowsingParent.jsm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/actors/AboutPrivateBrowsingParent.jsm 2022-04-26 05:44:41.000000000 +0000 @@ -31,6 +31,8 @@ XPCOMUtils.defineLazyModuleGetters(this, { UrlbarPrefs: "resource:///modules/UrlbarPrefs.jsm", + SpecialMessageActions: + "resource://messaging-system/lib/SpecialMessageActions.jsm", }); // We only show the private search banner once per browser session. @@ -162,8 +164,13 @@ Services.prefs.setIntPref(SHOWN_PREF, MAX_SEARCH_BANNER_SHOW_COUNT); break; } - case "ShouldShowVPNPromo": { - return BrowserUtils.shouldShowVPNPromo(); + case "ShouldShowPromo": { + return BrowserUtils.shouldShowPromo( + BrowserUtils.PromoType[aMessage.data.type] + ); + } + case "SpecialMessageActionDispatch": { + SpecialMessageActions.handleAction(aMessage.data, browser); } } diff -Nru firefox-99.0.1+build1/browser/actors/AboutReaderParent.jsm firefox-100.0+build1/browser/actors/AboutReaderParent.jsm --- firefox-99.0.1+build1/browser/actors/AboutReaderParent.jsm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/actors/AboutReaderParent.jsm 2022-04-26 05:44:41.000000000 +0000 @@ -201,6 +201,13 @@ break; } + case "RedirectTo": { + gCachedArticles.set(message.data.newURL, message.data.article); + // This is setup as a query so we can navigate the page after we've + // cached the relevant info in the parent. + return true; + } + default: this.callListeners(message); break; diff -Nru firefox-99.0.1+build1/browser/actors/ClickHandlerChild.jsm firefox-100.0+build1/browser/actors/ClickHandlerChild.jsm --- firefox-99.0.1+build1/browser/actors/ClickHandlerChild.jsm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/actors/ClickHandlerChild.jsm 2022-04-26 05:44:41.000000000 +0000 @@ -184,9 +184,9 @@ // actions here to avoid leaking clipboard content unexpectedly. // Note that whether the link will work actually or not does not matter // because in this case, user does not intent to paste clipboard content. - if (event.button === 1) { - event.preventMultipleActions(); - } + // We also need to do this to prevent multiple tabs opening if there are + // nested link elements. + event.preventMultipleActions(); this.sendAsyncMessage("Content:Click", json); } diff -Nru firefox-99.0.1+build1/browser/actors/ContextMenuChild.jsm firefox-100.0+build1/browser/actors/ContextMenuChild.jsm --- firefox-99.0.1+build1/browser/actors/ContextMenuChild.jsm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/actors/ContextMenuChild.jsm 2022-04-26 05:44:41.000000000 +0000 @@ -28,9 +28,8 @@ }); XPCOMUtils.defineLazyGetter(this, "PageMenuChild", () => { - let tmp = {}; - ChromeUtils.import("resource://gre/modules/PageMenu.jsm", tmp); - return new tmp.PageMenuChild(); + let pageMenu = ChromeUtils.import("resource://gre/modules/PageMenu.jsm"); + return new pageMenu.PageMenuChild(); }); let contextMenus = new WeakMap(); @@ -378,6 +377,7 @@ this.context.linkProtocol && !( this.context.linkProtocol == "mailto" || + this.context.linkProtocol == "tel" || this.context.linkProtocol == "javascript" || this.context.linkProtocol == "news" || this.context.linkProtocol == "snews" @@ -873,6 +873,7 @@ context.onLink = false; context.onLoadedImage = false; context.onMailtoLink = false; + context.onTelLink = false; context.onMozExtLink = false; context.onNumeric = false; context.onPassword = false; @@ -1158,6 +1159,7 @@ context.linkTextStr = this._getLinkText(); context.linkProtocol = this._getLinkProtocol(); context.onMailtoLink = context.linkProtocol == "mailto"; + context.onTelLink = context.linkProtocol == "tel"; context.onMozExtLink = context.linkProtocol == "moz-extension"; context.onSaveableLink = this._isLinkSaveable(context.link); diff -Nru firefox-99.0.1+build1/browser/actors/test/browser/browser.ini firefox-100.0+build1/browser/actors/test/browser/browser.ini --- firefox-99.0.1+build1/browser/actors/test/browser/browser.ini 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/actors/test/browser/browser.ini 2022-04-26 05:44:41.000000000 +0000 @@ -1,3 +1,4 @@ +[browser_nested_link_clicks.js] [browser_untrusted_click_event.js] support-files = click.html diff -Nru firefox-99.0.1+build1/browser/actors/test/browser/browser_nested_link_clicks.js firefox-100.0+build1/browser/actors/test/browser/browser_nested_link_clicks.js --- firefox-99.0.1+build1/browser/actors/test/browser/browser_nested_link_clicks.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-100.0+build1/browser/actors/test/browser/browser_nested_link_clicks.js 2022-04-26 05:44:41.000000000 +0000 @@ -0,0 +1,45 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Nested links should only open a single tab when ctrl-clicked. + */ +add_task(async function nested_link_click_opens_single_tab() { + await BrowserTestUtils.withNewTab( + "https://example.com/empty", + async browser => { + await SpecialPowers.spawn(browser, [], () => { + let doc = content.document; + let outerLink = doc.createElement("a"); + outerLink.href = "https://mozilla.org/"; + let link = doc.createElement("a"); + link.href = "https://example.org/linked"; + link.textContent = "Click me"; + link.id = "mylink"; + outerLink.append(link); + doc.body.append(outerLink); + }); + + let startingNumberOfTabs = gBrowser.tabs.length; + let newTabPromise = BrowserTestUtils.waitForNewTab( + gBrowser, + "https://example.org/linked", + true + ); + await BrowserTestUtils.synthesizeMouseAtCenter( + "#mylink", + { accelKey: true }, + browser + ); + let tab = await newTabPromise; + is( + gBrowser.tabs.length, + startingNumberOfTabs + 1, + "Should only have opened 1 tab." + ); + BrowserTestUtils.removeTab(tab); + } + ); +}); diff -Nru firefox-99.0.1+build1/browser/app/moz.build firefox-100.0+build1/browser/app/moz.build --- firefox-99.0.1+build1/browser/app/moz.build 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/app/moz.build 2022-04-26 05:44:41.000000000 +0000 @@ -161,7 +161,7 @@ "-mfpmath=387", ] -for icon in ("firefox", "document", "newwindow", "newtab", "pbmode"): +for icon in ("firefox", "document", "newwindow", "newtab", "pbmode", "document_pdf"): DEFINES[icon.upper() + "_ICO"] = '"%s/%s/%s.ico"' % ( TOPSRCDIR, CONFIG["MOZ_BRANDING_DIRECTORY"], diff -Nru firefox-99.0.1+build1/browser/app/profile/firefox.js firefox-100.0+build1/browser/app/profile/firefox.js --- firefox-99.0.1+build1/browser/app/profile/firefox.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/app/profile/firefox.js 2022-04-26 05:44:41.000000000 +0000 @@ -275,6 +275,11 @@ // When setting the default browser on Windows 10 using the UserChoice // registry keys, also try to set Firefox as the default PDF handler. pref("browser.shell.setDefaultPDFHandler", false); +// When setting Firefox as the default PDF handler (subject to conditions +// above), only set Firefox as the default PDF handler when the existing handler +// is a known browser, and not when existing handler is another PDF handler such +// as Acrobat Reader or Nitro PDF. +pref("browser.shell.setDefaultPDFHandler.onlyReplaceBrowsers", true); #endif @@ -311,7 +316,7 @@ #endif // Show an upgrade dialog on major upgrades. -pref("browser.startup.upgradeDialog.enabled", false); +pref("browser.startup.upgradeDialog.enabled", true); // Don't create the hidden window during startup on // platforms that don't always need it (Win/Linux). @@ -1078,8 +1083,6 @@ pref("browser.sessionstore.debug", false); // Forget closed windows/tabs after two weeks pref("browser.sessionstore.cleanup.forget_closed_after", 1209600000); -// Amount of failed SessionFile writes until we restart the worker. -pref("browser.sessionstore.max_write_failures", 5); // Don't quit the browser when Ctrl + Q is pressed. pref("browser.quitShortcut.disabled", false); @@ -1635,9 +1638,6 @@ // Show degraded UI for http pages in private mode. pref("security.insecure_connection_icon.pbmode.enabled", true); -// For secure connections, show gray instead of green lock icon -pref("security.secure_connection_icon_color_gray", true); - // Show "Not Secure" text for http pages; disabled for now pref("security.insecure_connection_text.enabled", false); pref("security.insecure_connection_text.pbmode.enabled", false); @@ -1820,8 +1820,6 @@ pref("browser.contentblocking.fingerprinting.preferences.ui.enabled", true); // Enable cookieBehavior = BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN as an option in the custom category ui pref("browser.contentblocking.reject-and-isolate-cookies.preferences.ui.enabled", true); -// State Partitioning MVP UI. -pref("browser.contentblocking.state-partitioning.mvp.ui.enabled", true); // Possible values for browser.contentblocking.features.strict pref: // Tracking Protection: @@ -1845,6 +1843,9 @@ // Restrict relaxing default referrer policy: // "rp": Restrict relaxing default referrer policy enabled // "-rp": Restrict relaxing default referrer policy disabled +// Restrict relaxing default referrer policy for top navigation: +// "rpTop": Restrict relaxing default referrer policy enabled +// "-rpTop": Restrict relaxing default referrer policy disabled // OCSP cache partitioning: // "ocsp": OCSP cache partitioning enabled // "-ocsp": OCSP cache partitioning disabled @@ -1863,7 +1864,7 @@ // "cookieBehaviorPBM4": cookie behaviour BEHAVIOR_REJECT_TRACKER // "cookieBehaviorPBM5": cookie behaviour BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN // One value from each section must be included in the browser.contentblocking.features.strict pref. -pref("browser.contentblocking.features.strict", "tp,tpPrivate,cookieBehavior5,cookieBehaviorPBM5,cm,fp,stp,lvl2,rp,ocsp"); +pref("browser.contentblocking.features.strict", "tp,tpPrivate,cookieBehavior5,cookieBehaviorPBM5,cm,fp,stp,lvl2,rp,rpTop,ocsp"); // Hide the "Change Block List" link for trackers/tracking content in the custom // Content Blocking/ETP panel. By default, it will not be visible. There is also @@ -1897,9 +1898,17 @@ pref("browser.vpn_promo.enabled", true); // Only show vpn card to certain regions. Comma separated string of two letter ISO 3166-1 country codes. // The most recent list of supported countries can be found at https://support.mozilla.org/en-US/kb/mozilla-vpn-countries-available-subscribe -pref("browser.contentblocking.report.vpn_regions", "at,be,ca,ch,de,fr,ie,it,my,nl,nz,sg,es,gb,us" +// The full list of supported country codes can also be found at https://github.com/mozilla/bedrock/search?q=VPN_COUNTRY_CODES +pref("browser.contentblocking.report.vpn_regions", "as,at,be,ca,ch,de,es,fi,fr,gb,gg,ie,im,io,it,je,mp,my,nl,nz,pr,se,sg,uk,um,us,vg,vi" ); +// Avoid advertising Focus in certain regions. Comma separated string of two letter +// ISO 3166-1 country codes. +pref("browser.promo.focus.disallowed_regions", "cn"); + +// Default to enabling focus promos to be shown where allowed. +pref("browser.promo.focus.enabled", true); + // Comma separated string of mozilla vpn supported platforms. pref("browser.contentblocking.report.vpn_platforms", "win,mac,linux"); pref("browser.contentblocking.report.hide_vpn_banner", false); @@ -1987,11 +1996,7 @@ pref("browser.tabs.remote.desktopbehavior", true); // Run media transport in a separate process? -#ifdef EARLY_BETA_OR_EARLIER - pref("media.peerconnection.mtransport_process", true); -#else - pref("media.peerconnection.mtransport_process", false); -#endif +pref("media.peerconnection.mtransport_process", true); // For speculatively warming up tabs to improve perceived // performance while using the async tab switcher. @@ -2160,23 +2165,31 @@ pref("app.shield.optoutstudies.enabled", false); #endif -// Multi-lingual preferences +// Multi-lingual preferences: +// *.enabled - Are langpacks available for the build of Firefox? +// *.downloadEnabled - Langpacks are allowed to be downloaded from AMO. AMO only serves +// langpacks for release and beta. Unsupported releases (like Nightly) can be +// manually tested with the following preference: +// extensions.getAddons.langpacks.url: https://mock-amo-language-tools.glitch.me/?app=firefox&type=language&appversion=%VERSION% +// *.liveReload - Switching a langpack will change the language without a restart. +// *.liveReloadBidirectional - Allows switching when moving between LTR and RTL +// languages without a full restart. +// *.aboutWelcome.languageMismatchEnabled - Enables an onboarding menu in about:welcome +// to allow a user to change their language when there is a language mismatch between +// the app and browser. #if defined(RELEASE_OR_BETA) && !defined(MOZ_DEV_EDITION) pref("intl.multilingual.enabled", true); pref("intl.multilingual.downloadEnabled", true); + pref("intl.multilingual.liveReload", true); + pref("intl.multilingual.liveReloadBidirectional", false); + pref("intl.multilingual.aboutWelcome.languageMismatchEnabled", true); #else pref("intl.multilingual.enabled", false); - // AMO only serves language packs for release and beta versions. pref("intl.multilingual.downloadEnabled", false); + pref("intl.multilingual.liveReload", false); + pref("intl.multilingual.liveReloadBidirectional", false); + pref("intl.multilingual.aboutWelcome.languageMismatchEnabled", false); #endif -// With the preference enabled below, switching the browser language will do a live -// reload rather than requiring a restart. Enable bidirectional below as well to allow -// live reloading when switching between LTR and RTL languages. -pref("intl.multilingual.liveReload", false); -pref("intl.multilingual.liveReloadBidirectional", false); -// Suggest to change the language on about:welcome when there is a mismatch with the OS. -pref("intl.multilingual.aboutWelcome.languageMismatchEnabled", false); - // Simulate conditions that will happen when the browser // is running with Fission enabled. This is meant to assist diff -Nru firefox-99.0.1+build1/browser/app/splash.rc firefox-100.0+build1/browser/app/splash.rc --- firefox-99.0.1+build1/browser/app/splash.rc 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/app/splash.rc 2022-04-26 05:44:41.000000000 +0000 @@ -12,6 +12,7 @@ IDI_NEWWINDOW ICON NEWWINDOW_ICO IDI_NEWTAB ICON NEWTAB_ICO IDI_PBMODE ICON PBMODE_ICO +IDI_DOCUMENT_PDF ICON DOCUMENT_PDF_ICO STRINGTABLE DISCARDABLE BEGIN diff -Nru firefox-99.0.1+build1/browser/base/content/browser-context.inc firefox-100.0+build1/browser/base/content/browser-context.inc --- firefox-99.0.1+build1/browser/base/content/browser-context.inc 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/browser-context.inc 2022-04-26 05:44:41.000000000 +0000 @@ -106,6 +106,9 @@ + diff -Nru firefox-99.0.1+build1/browser/base/content/browser.css firefox-100.0+build1/browser/base/content/browser.css --- firefox-99.0.1+build1/browser/base/content/browser.css 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/browser.css 2022-04-26 05:44:41.000000000 +0000 @@ -94,7 +94,10 @@ } } -%ifdef MENUBAR_CAN_AUTOHIDE +#titlebar { + -moz-window-dragging: drag; +} + #toolbar-menubar[autohide="true"] { overflow: hidden; } @@ -104,13 +107,10 @@ height: 0 !important; appearance: none !important; } -%endif -%ifdef XP_MACOSX -#toolbar-menubar { +#toolbar-menubar:not([autohide]) { visibility: collapse; } -%endif panelmultiview { -moz-box-align: start; @@ -291,48 +291,48 @@ display: none; } - -%ifdef MENUBAR_CAN_AUTOHIDE /* Hide the TabsToolbar titlebar controls if the menubar is permanently shown. * (That is, if the menu bar doesn't autohide, and we're not in a fullscreen or * popup window.) */ -:root:not([chromehidden~="menubar"], [inFullscreen]) #toolbar-menubar:not([autohide=true]) + #TabsToolbar > :is(.titlebar-buttonbox-container, .titlebar-spacer), -%endif -%ifndef MOZ_WIDGET_COCOA -%ifndef MOZ_WIDGET_GTK -:root:not([sizemode=normal]) .titlebar-spacer[type="pre-tabs"], -%endif -%endif +:root:not([chromehidden~="menubar"], [inFullscreen]) #toolbar-menubar[autohide="false"] + #TabsToolbar > :is(.titlebar-buttonbox-container, .titlebar-spacer) { + display: none; +} + :root:not([chromemargin], [inFullscreen]) .titlebar-buttonbox-container, :root[inFullscreen] .titlebar-spacer, :root:not([tabsintitlebar]) .titlebar-spacer { display: none; } -%ifdef MOZ_WIDGET_GTK -@media (-moz-gtk-csd-reversed-placement: 0) { - :root:not([sizemode=normal]) .titlebar-spacer[type="pre-tabs"], - :root[gtktiledwindow=true] .titlebar-spacer[type="pre-tabs"] { + +@media (-moz-platform: windows) { + :root:not([sizemode=normal]) .titlebar-spacer[type="pre-tabs"] { display: none; } } -@media (-moz-gtk-csd-reversed-placement) { - :root:not([sizemode=normal]) .titlebar-spacer[type="post-tabs"], - :root[gtktiledwindow=true] .titlebar-spacer[type="post-tabs"] { - display: none; + +@media (-moz-platform: linux) { + @media (-moz-gtk-csd-reversed-placement: 0) { + :root:not([sizemode=normal]) .titlebar-spacer[type="pre-tabs"], + :root[gtktiledwindow=true] .titlebar-spacer[type="pre-tabs"] { + display: none; + } + } + @media (-moz-gtk-csd-reversed-placement) { + :root:not([sizemode=normal]) .titlebar-spacer[type="post-tabs"], + :root[gtktiledwindow=true] .titlebar-spacer[type="post-tabs"] { + display: none; + } } } -%endif :root:not([sizemode=maximized], [sizemode=fullscreen]) .titlebar-restore, :root:is([sizemode=maximized], [sizemode=fullscreen]) .titlebar-max { display: none; } -%ifdef MENUBAR_CAN_AUTOHIDE -#toolbar-menubar[autohide=true]:not([inactive]) + #TabsToolbar > .titlebar-buttonbox-container { +#toolbar-menubar[autohide="true"]:not([inactive]) + #TabsToolbar > .titlebar-buttonbox-container { visibility: hidden; } -%endif :root[tabsintitlebar] .titlebar-buttonbox { position: relative; @@ -365,22 +365,20 @@ -moz-box-ordinal-group: 1000; } -%ifdef XP_MACOSX - -@media not (-moz-mac-rtl) { - .titlebar-buttonbox-container:-moz-locale-dir(ltr) { - -moz-box-ordinal-group: 0; +@media (-moz-platform: macos) { + @media not (-moz-mac-rtl) { + .titlebar-buttonbox-container:-moz-locale-dir(ltr) { + -moz-box-ordinal-group: 0; + } } -} -@media (-moz-mac-rtl) { - .titlebar-buttonbox-container:-moz-locale-dir(rtl) { - -moz-box-ordinal-group: 0; + @media (-moz-mac-rtl) { + .titlebar-buttonbox-container:-moz-locale-dir(rtl) { + -moz-box-ordinal-group: 0; + } } } -%endif - :root[inDOMFullscreen] #navigator-toolbox, :root[inDOMFullscreen] #fullscr-toggler, :root[inDOMFullscreen] #sidebar-box, @@ -470,13 +468,13 @@ opacity: 0.5; } -%ifndef XP_MACOSX -toolbarpaletteitem[place="palette"], -toolbarpaletteitem[place="menu-panel"], -toolbarpaletteitem[place="toolbar"] { - -moz-user-focus: normal; +@media not (-moz-platform: macos) { + toolbarpaletteitem[place="palette"], + toolbarpaletteitem[place="menu-panel"], + toolbarpaletteitem[place="toolbar"] { + -moz-user-focus: normal; + } } -%endif #bookmarks-toolbar-placeholder, #bookmarks-toolbar-button, @@ -528,11 +526,11 @@ -moz-box-flex: 1; } -%ifdef XP_MACOSX -:root[inFullscreen="true"] { - padding-top: 0; /* override drawintitlebar="true" */ +@media (-moz-platform: macos) { + :root[inFullscreen="true"] { + padding-top: 0; /* override drawintitlebar="true" */ + } } -%endif /* Hide menu elements intended for keyboard access support */ #main-menubar[openedwithkey=false] .show-only-for-keyboard { @@ -1046,7 +1044,7 @@ @supports -moz-bool-pref("xul.panel-animations.enabled") { @media (prefers-reduced-motion: no-preference) { -%ifdef MOZ_WIDGET_COCOA +@media (-moz-platform: macos) { /* On Mac, use the properties "-moz-window-transform" and "-moz-window-opacity" instead of "transform" and "opacity" for these animations. The -moz-window* properties apply to the whole window including the window's @@ -1089,7 +1087,8 @@ -moz-window-opacity: 0; -moz-window-transform: none; } -%else +} +@media not (-moz-platform: macos) { #BMB_bookmarksPopup:not([animate="false"]) { opacity: 0; transform: translateY(-70px); @@ -1116,7 +1115,7 @@ #BMB_bookmarksPopup[animate][animate="cancel"] { transform: none; } -%endif +} } } diff -Nru firefox-99.0.1+build1/browser/base/content/browser-data-submission-info-bar.js firefox-100.0+build1/browser/base/content/browser-data-submission-info-bar.js --- firefox-99.0.1+build1/browser/base/content/browser-data-submission-info-bar.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/browser-data-submission-info-bar.js 2022-04-26 05:44:41.000000000 +0000 @@ -43,25 +43,11 @@ return; } - let brandBundle = document.getElementById("bundle_brand"); - let appName = brandBundle.getString("brandShortName"); - let vendorName = brandBundle.getString("vendorShortName"); - - let message = gNavigatorBundle.getFormattedString( - "dataReportingNotification.message", - [appName, vendorName] - ); - this._actionTaken = false; let buttons = [ { - label: gNavigatorBundle.getString( - "dataReportingNotification.button.label" - ), - accessKey: gNavigatorBundle.getString( - "dataReportingNotification.button.accessKey" - ), + "l10n-id": "data-reporting-notification-button", popup: null, callback: () => { this._actionTaken = true; @@ -74,7 +60,9 @@ gNotificationBox.appendNotification( this._DATA_REPORTING_NOTIFICATION, { - label: message, + label: { + "l10n-id": "data-reporting-notification-message", + }, priority: gNotificationBox.PRIORITY_INFO_HIGH, eventCallback: event => { if (event == "removed") { diff -Nru firefox-99.0.1+build1/browser/base/content/browser-fullScreenAndPointerLock.js firefox-100.0+build1/browser/base/content/browser-fullScreenAndPointerLock.js --- firefox-99.0.1+build1/browser/base/content/browser-fullScreenAndPointerLock.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/browser-fullScreenAndPointerLock.js 2022-04-26 05:44:41.000000000 +0000 @@ -94,9 +94,10 @@ } else { textElem.removeAttribute("hidden"); // Document's principal's URI has a host. Display a warning including it. - let utils = {}; - ChromeUtils.import("resource://gre/modules/DownloadUtils.jsm", utils); - let displayHost = utils.DownloadUtils.getURIHost(uri.spec)[0]; + let { DownloadUtils } = ChromeUtils.import( + "resource://gre/modules/DownloadUtils.jsm" + ); + let displayHost = DownloadUtils.getURIHost(uri.spec)[0]; let l10nString = { "fullscreen-warning": "fullscreen-warning-domain", "pointerlock-warning": "pointerlock-warning-domain", @@ -885,8 +886,7 @@ XPCOMUtils.defineLazyGetter(FullScreen, "_permissionNotificationIDs", () => { let { PermissionUI } = ChromeUtils.import( - "resource:///modules/PermissionUI.jsm", - {} + "resource:///modules/PermissionUI.jsm" ); return ( Object.values(PermissionUI) diff -Nru firefox-99.0.1+build1/browser/base/content/browser.js firefox-100.0+build1/browser/base/content/browser.js --- firefox-99.0.1+build1/browser/base/content/browser.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/browser.js 2022-04-26 05:44:41.000000000 +0000 @@ -632,6 +632,7 @@ var gPageIcons = { "about:home": "chrome://branding/content/icon32.png", + "about:myfirefox": "chrome://branding/content/icon32.png", "about:newtab": "chrome://branding/content/icon32.png", "about:welcome": "chrome://branding/content/icon32.png", "about:privatebrowsing": "chrome://browser/skin/privatebrowsing/favicon.svg", @@ -639,12 +640,13 @@ var gInitialPages = [ "about:blank", - "about:newtab", "about:home", + ...(AppConstants.NIGHTLY_BUILD ? ["about:myfirefox"] : []), + "about:newtab", "about:privatebrowsing", - "about:welcomeback", "about:sessionrestore", "about:welcome", + "about:welcomeback", ]; function isInitialPage(url) { @@ -1628,6 +1630,18 @@ document.documentElement.setAttribute("sizemode", "maximized"); } } + if (AppConstants.MENUBAR_CAN_AUTOHIDE) { + const toolbarMenubar = document.getElementById("toolbar-menubar"); + // set a default value + if (!toolbarMenubar.hasAttribute("autohide")) { + toolbarMenubar.setAttribute("autohide", true); + } + toolbarMenubar.setAttribute( + "data-l10n-id", + "toolbar-context-menu-menu-bar-cmd" + ); + toolbarMenubar.setAttribute("data-l10n-attrs", "toolbarname"); + } // Run menubar initialization first, to avoid TabsInTitlebar code picking // up mutations from it and causing a reflow. @@ -1645,8 +1659,7 @@ ) { let windowFrameColor = new Color( ...ChromeUtils.import( - "resource:///modules/Windows8WindowFrameColor.jsm", - {} + "resource:///modules/Windows8WindowFrameColor.jsm" ).Windows8WindowFrameColor.get() ); // Default to black for foreground text. @@ -2339,8 +2352,7 @@ try { DownloadsCommon.initializeAllDataLinks(); ChromeUtils.import( - "resource:///modules/DownloadsTaskbar.jsm", - {} + "resource:///modules/DownloadsTaskbar.jsm" ).DownloadsTaskbar.registerIndicator(window); if (AppConstants.platform == "macosx") { ChromeUtils.import( @@ -4309,7 +4321,7 @@ "browser.search.searchEngineRemoval" ); link.setAttribute("data-l10n-name", "remove-search-engine-article"); - document.l10n.setAttributes(message, "remove-search-engine-message", { + document.l10n.setAttributes(message, "removed-search-engine-message", { oldEngine, newEngine, }); diff -Nru firefox-99.0.1+build1/browser/base/content/browser-sidebar.js firefox-100.0+build1/browser/base/content/browser-sidebar.js --- firefox-99.0.1+build1/browser/base/content/browser-sidebar.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/browser-sidebar.js 2022-04-26 05:44:41.000000000 +0000 @@ -10,37 +10,44 @@ if (this._sidebars) { return this._sidebars; } + + function makeSidebar({ elementId, ...rest }) { + return { + get sourceL10nEl() { + return document.getElementById(elementId); + }, + get title() { + return document.getElementById(elementId).getAttribute("label"); + }, + ...rest, + }; + } + return (this._sidebars = new Map([ [ "viewBookmarksSidebar", - { - title: document - .getElementById("sidebar-switcher-bookmarks") - .getAttribute("label"), + makeSidebar({ + elementId: "sidebar-switcher-bookmarks", url: "chrome://browser/content/places/bookmarksSidebar.xhtml", menuId: "menu_bookmarksSidebar", - }, + }), ], [ "viewHistorySidebar", - { - title: document - .getElementById("sidebar-switcher-history") - .getAttribute("label"), + makeSidebar({ + elementId: "sidebar-switcher-history", url: "chrome://browser/content/places/historySidebar.xhtml", menuId: "menu_historySidebar", triggerButtonId: "appMenuViewHistorySidebar", - }, + }), ], [ "viewTabsSidebar", - { - title: document - .getElementById("sidebar-switcher-tabs") - .getAttribute("label"), + makeSidebar({ + elementId: "sidebar-switcher-tabs", url: "chrome://browser/content/syncedtabs/sidebar.xhtml", menuId: "menu_tabsSidebar", - }, + }), ], ])); }, @@ -77,6 +84,11 @@ _switcherArrow: null, _inited: false, + /** + * @type {MutationObserver | null} + */ + _observer: null, + _initDeferred: PromiseUtils.defer(), get promiseInitialized() { @@ -104,6 +116,8 @@ this._inited = true; + Services.obs.addObserver(this, "intl:app-locales-changed"); + this._initDeferred.resolve(); }, @@ -133,6 +147,56 @@ xulStore.persist(this._box, "width"); xulStore.persist(this._title, "value"); } + + Services.obs.removeObserver(this, "intl:app-locales-changed"); + + if (this._observer) { + this._observer.disconnect(); + this._observer = null; + } + }, + + /** + * The handler for Services.obs.addObserver. + **/ + observe(_subject, topic, _data) { + switch (topic) { + case "intl:app-locales-changed": { + if (this.isOpen) { + // The component used in history and bookmarks, but it does not + // support live switching the app locale. Reload the entire sidebar to + // invalidate any old text. + this.hide(); + this._show(this.lastOpenedId); + break; + } + } + } + }, + + /** + * Ensure the title stays in sync with the source element, which updates for + * l10n changes. + * + * @param {HTMLElement} [element] + */ + observeTitleChanges(element) { + if (!element) { + return; + } + let observer = this._observer; + if (!observer) { + observer = new MutationObserver(() => { + this.title = this.sidebars.get(this.lastOpenedId).title; + }); + // Re-use the observer. + this._observer = observer; + } + observer.disconnect(); + observer.observe(element, { + attributes: true, + attributeFilter: ["label"], + }); }, /** @@ -509,8 +573,10 @@ this._box.setAttribute("sidebarcommand", commandID); this.lastOpenedId = commandID; - let { url, title } = this.sidebars.get(commandID); + let { url, title, sourceL10nEl } = this.sidebars.get(commandID); this.title = title; + // Keep the title element in sync with any l10n changes. + this.observeTitleChanges(sourceL10nEl); this.browser.setAttribute("src", url); // kick off async load if (this.browser.contentDocument.location.href != url) { diff -Nru firefox-99.0.1+build1/browser/base/content/browser-siteIdentity.js firefox-100.0+build1/browser/base/content/browser-siteIdentity.js --- firefox-99.0.1+build1/browser/base/content/browser-siteIdentity.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/browser-siteIdentity.js 2022-04-26 05:44:41.000000000 +0000 @@ -151,20 +151,9 @@ ); }, - get _isPDFViewer() { - return gBrowser.contentPrincipal?.originNoSuffix == "resource://pdf.js"; - }, - get _isPotentiallyTrustworthy() { - // For PDF viewer pages (pdf.js) we can't rely on the isSecureContext - // field. The backend will return isSecureContext = true, because the - // content principal has a resource:// URI. Since we don't check - // isSecureContext for PDF viewer pages anymore, otherwise secure - // contexts, such as a localhost, will me marked as insecure when showing - // PDFs. return ( !this._isBrokenConnection && - !this._isPDFViewer && (this._isSecureContext || gBrowser.selectedBrowser.documentURI?.scheme == "chrome") ); @@ -368,16 +357,6 @@ ); return this._httpsOnlyModeEnabledPBM; }, - get _useGrayLockIcon() { - delete this._useGrayLockIcon; - XPCOMUtils.defineLazyPreferenceGetter( - this, - "_useGrayLockIcon", - "security.secure_connection_icon_color_gray", - false - ); - return this._useGrayLockIcon; - }, /** * Handles clicks on the "Clear Cookies and Site Data" button. @@ -629,6 +608,32 @@ return result; }, + _getIsSecureContext() { + if (gBrowser.contentPrincipal?.originNoSuffix != "resource://pdf.js") { + return gBrowser.securityUI.isSecureContext; + } + + // For PDF viewer pages (pdf.js) we can't rely on the isSecureContext field. + // The backend will return isSecureContext = true, because the content + // principal has a resource:// URI. Instead use the URI of the selected + // browser to perform the isPotentiallyTrustWorthy check. + + let principal; + try { + principal = Services.scriptSecurityManager.createContentPrincipal( + gBrowser.selectedBrowser.documentURI, + {} + ); + return principal.isOriginPotentiallyTrustworthy; + } catch (error) { + Cu.reportError( + "Error while computing isPotentiallyTrustWorthy for pdf viewer page: " + + error + ); + return false; + } + }, + /** * Update the identity user interface for the page currently being displayed. * @@ -649,7 +654,7 @@ // the documentation of the individual properties for details. this.setURI(uri); this._secInfo = gBrowser.securityUI.secInfo; - this._isSecureContext = gBrowser.securityUI.isSecureContext; + this._isSecureContext = this._getIsSecureContext(); // Then, update the user interface with the available data. this.refreshIdentityBlock(); @@ -857,13 +862,6 @@ ); } - // Gray lock icon for secure connections if pref set - this._updateAttribute( - this._identityIcon, - "lock-icon-gray", - this._useGrayLockIcon - ); - // Push the appropriate strings out to the UI this._identityIcon.setAttribute("tooltiptext", tooltip); @@ -1013,13 +1011,6 @@ ciphers = "weak"; } - // Gray lock icon for secure connections if pref set - this._updateAttribute( - this._identityPopup, - "lock-icon-gray", - this._useGrayLockIcon - ); - // If HTTPS-Only Mode is enabled, check the permission status const privateBrowsingWindow = PrivateBrowsingUtils.isWindowPrivate(window); let httpsOnlyStatus = ""; diff -Nru firefox-99.0.1+build1/browser/base/content/navigator-toolbox.inc.xhtml firefox-100.0+build1/browser/base/content/navigator-toolbox.inc.xhtml --- firefox-99.0.1+build1/browser/base/content/navigator-toolbox.inc.xhtml 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/navigator-toolbox.inc.xhtml 2022-04-26 05:44:41.000000000 +0000 @@ -16,11 +16,6 @@ class="browser-toolbar chromeclass-menubar titlebar-color" customizable="true" mode="icons" -#ifdef MENUBAR_CAN_AUTOHIDE - data-l10n-id="toolbar-context-menu-menu-bar-cmd" - data-l10n-attrs="toolbarname" - autohide="true" -#endif context="toolbar-context-menu"> # The entire main menubar is placed into browser-menubar.inc, so that it can be @@ -572,7 +567,8 @@ class="subviewbutton" data-l10n-id="bookmarks-tools-sidebar-visibility" data-l10n-args='{ "isVisible": false }' - oncommand="SidebarUI.toggle('viewBookmarksSidebar');"/> + oncommand="SidebarUI.toggle('viewBookmarksSidebar');" + key="viewBookmarksSidebarKb"/> - + + diff -Nru firefox-99.0.1+build1/browser/base/content/spotlight.js firefox-100.0+build1/browser/base/content/spotlight.js --- firefox-99.0.1+build1/browser/base/content/spotlight.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/spotlight.js 2022-04-26 05:44:41.000000000 +0000 @@ -126,7 +126,12 @@ window.AWGetImportableSites = () => "[]"; window.AWGetRegion = receive("GET_REGION"); window.AWGetSelectedTheme = receive("GET_SELECTED_THEME"); - window.AWSendEventTelemetry = receive("TELEMETRY_EVENT"); + // Do not send telemetry if message (e.g. spotlight in PBM) config sets metrics as 'block'. + window.AWSendEventTelemetry = + CONFIG?.metrics === "block" ? () => {} : receive("TELEMETRY_EVENT"); + window.AWSendToDeviceEmailsSupported = receive( + "SEND_TO_DEVICE_EMAILS_SUPPORTED" + ); window.AWSendToParent = (name, data) => receive(name)(data); window.AWFinish = () => { window.close(); diff -Nru firefox-99.0.1+build1/browser/base/content/tabbrowser.js firefox-100.0+build1/browser/base/content/tabbrowser.js --- firefox-99.0.1+build1/browser/base/content/tabbrowser.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/tabbrowser.js 2022-04-26 05:44:41.000000000 +0000 @@ -63,6 +63,7 @@ XPCOMUtils.defineLazyModuleGetters(this, { E10SUtils: "resource://gre/modules/E10SUtils.jsm", + PictureInPicture: "resource://gre/modules/PictureInPicture.jsm", }); XPCOMUtils.defineLazyServiceGetters(this, { MacSharingService: [ @@ -2986,9 +2987,7 @@ restoreTabsLazily && !select && !tabData.pinned; let url = "about:blank"; - if (createLazyBrowser && tabData.entries && tabData.entries.length) { - // Let tabbrowser know the future URI because progress listeners won't - // get onLocationChange notification before the browser is inserted. + if (tabData.entries?.length) { let activeIndex = (tabData.index || tabData.entries.length) - 1; // Ensure the index is in bounds. activeIndex = Math.min(activeIndex, tabData.entries.length - 1); @@ -2996,10 +2995,23 @@ url = tabData.entries[activeIndex].url; } + let preferredRemoteType = E10SUtils.getRemoteTypeForURI( + url, + gMultiProcessBrowser, + gFissionBrowser, + E10SUtils.DEFAULT_REMOTE_TYPE, + null, + E10SUtils.predictOriginAttributes({ window, userContextId }) + ); + + // If we're creating a lazy browser, let tabbrowser know the future + // URI because progress listeners won't get onLocationChange + // notification before the browser is inserted. + // // Setting noInitialLabel is a perf optimization. Rendering tab labels // would make resizing the tabs more expensive as we're adding them. // Each tab will get its initial label set in restoreTab. - tab = this.addTrustedTab(url, { + tab = this.addTrustedTab(createLazyBrowser ? url : "about:blank", { createLazyBrowser, skipAnimation: true, allowInheritPrincipal: true, @@ -3008,6 +3020,8 @@ skipBackgroundNotify: true, bulkOrderedOpen: true, batchInsertingTabs: true, + skipLoad: !createLazyBrowser, + preferredRemoteType, }); if (select) { @@ -4777,16 +4791,25 @@ // This avoids tab-switches in the new window, preserving tab laziness. // However, to avoid multiple tab-switches in the original window, the other tabs // should be adopted before the selected one. - let selectedTabIndex = Math.max(0, tabs.indexOf(gBrowser.selectedTab)); - let selectedTab = tabs[selectedTabIndex]; + let { selectedTab } = gBrowser; + if (!tabs.includes(selectedTab)) { + selectedTab = tabs[0]; + } let win = this.replaceTabWithWindow(selectedTab, aOptions); win.addEventListener( "before-initial-tab-adopted", () => { - for (let i = 0; i < tabs.length; ++i) { - if (i != selectedTabIndex) { - win.gBrowser.adoptTab(tabs[i], i); + let index = 0; + for (let tab of tabs) { + if (tab !== selectedTab) { + const newTab = win.gBrowser.adoptTab(tab, index); + if (!newTab) { + // The adoption failed. Restore "fadein" and don't increase the index. + tab.setAttribute("fadein", "true"); + continue; + } } + ++index; } // Restore tab selection let winVisibleTabs = win.gBrowser.visibleTabs; @@ -5290,7 +5313,8 @@ (aBrowser == this.selectedBrowser && window.windowState != window.STATE_MINIMIZED && !window.isFullyOccluded) || - this._printPreviewBrowsers.has(aBrowser) + this._printPreviewBrowsers.has(aBrowser) || + this.PictureInPicture.isOriginatingBrowser(aBrowser) ); }, diff -Nru firefox-99.0.1+build1/browser/base/content/tabbrowser-tabs.js firefox-100.0+build1/browser/base/content/tabbrowser-tabs.js --- firefox-99.0.1+build1/browser/base/content/tabbrowser-tabs.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/tabbrowser-tabs.js 2022-04-26 05:44:41.000000000 +0000 @@ -61,13 +61,9 @@ ); this._hiddenSoundPlayingTabs = new Set(); - // Normal tab title is used also in the permanent private browsing mode. - let strId = - PrivateBrowsingUtils.isWindowPrivate(window) && - !Services.prefs.getBoolPref("browser.privatebrowsing.autostart") - ? "emptyPrivateTabTitle" - : "emptyTabTitle"; - this.emptyTabTitle = gTabBrowserBundle.GetStringFromName("tabs." + strId); + this.emptyTabTitle = gTabBrowserBundle.GetStringFromName( + this.getTabTitleMessageId() + ); var tab = this.allTabs[0]; tab.label = this.emptyTabTitle; @@ -100,6 +96,7 @@ this.boundObserve = (...args) => this.observe(...args); Services.prefs.addObserver("privacy.userContext", this.boundObserve); + Services.obs.addObserver(this.boundObserve, "intl:app-locales-changed"); this.observe(null, "nsPref:changed", "privacy.userContext.enabled"); XPCOMUtils.defineLazyPreferenceGetter( @@ -750,17 +747,39 @@ } } } else if (draggedTab) { - let newIndex = this._getDropIndex(event, false); - let newTabs = []; - for (let tab of movingTabs) { - let newTab = gBrowser.adoptTab(tab, newIndex++, tab == draggedTab); - newTabs.push(newTab); + // Move the tabs. To avoid multiple tab-switches in the original window, + // the selected tab should be adopted last. + const dropIndex = this._getDropIndex(event, false); + let newIndex = dropIndex; + let selectedTab; + let indexForSelectedTab; + for (let i = 0; i < movingTabs.length; ++i) { + const tab = movingTabs[i]; + if (tab.selected) { + selectedTab = tab; + indexForSelectedTab = newIndex; + } else { + const newTab = gBrowser.adoptTab(tab, newIndex, tab == draggedTab); + if (newTab) { + ++newIndex; + } + } + } + if (selectedTab) { + const newTab = gBrowser.adoptTab( + selectedTab, + indexForSelectedTab, + selectedTab == draggedTab + ); + if (newTab) { + ++newIndex; + } } // Restore tab selection gBrowser.addRangeToMultiSelectedTabs( - newTabs[0], - newTabs[newTabs.length - 1] + gBrowser.tabs[dropIndex], + gBrowser.tabs[newIndex - 1] ); } else { // Pass true to disallow dropping javascript: or data: urls @@ -977,6 +996,14 @@ event.stopPropagation(); } + getTabTitleMessageId() { + // Normal tab title is used also in the permanent private browsing mode. + return PrivateBrowsingUtils.isWindowPrivate(window) && + !Services.prefs.getBoolPref("browser.privatebrowsing.autostart") + ? "tabs.emptyPrivateTabTitle" + : "tabs.emptyTabTitle"; + } + get tabbox() { return document.getElementById("tabbrowser-tabbox"); } @@ -1149,6 +1176,19 @@ } break; + + case "intl:app-locales-changed": + document.l10n.ready.then(() => { + // The cached emptyTabTitle needs updating, create a new string bundle + // here to ensure the latest locale string is used. + const bundle = Services.strings.createBundle( + "chrome://browser/locale/tabbrowser.properties" + ); + this.emptyTabTitle = bundle.GetStringFromName( + this.getTabTitleMessageId() + ); + }); + break; } } @@ -2122,6 +2162,10 @@ destroy() { if (this.boundObserve) { Services.prefs.removeObserver("privacy.userContext", this.boundObserve); + Services.obs.removeObserver( + this.boundObserve, + "intl:app-locales-changed" + ); } CustomizableUI.removeListener(this); diff -Nru firefox-99.0.1+build1/browser/base/content/test/about/browser_aboutNewTab_bookmarksToolbarEmpty.js firefox-100.0+build1/browser/base/content/test/about/browser_aboutNewTab_bookmarksToolbarEmpty.js --- firefox-99.0.1+build1/browser/base/content/test/about/browser_aboutNewTab_bookmarksToolbarEmpty.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/about/browser_aboutNewTab_bookmarksToolbarEmpty.js 2022-04-26 05:44:41.000000000 +0000 @@ -18,7 +18,7 @@ }, ]; -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ // Ensure we can wait for about:newtab to load. set: [["browser.newtab.preload", false]], diff -Nru firefox-99.0.1+build1/browser/base/content/test/alerts/browser_notification_do_not_disturb.js firefox-100.0+build1/browser/base/content/test/alerts/browser_notification_do_not_disturb.js --- firefox-99.0.1+build1/browser/base/content/test/alerts/browser_notification_do_not_disturb.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/alerts/browser_notification_do_not_disturb.js 2022-04-26 05:44:41.000000000 +0000 @@ -20,7 +20,7 @@ // to show up before we decide that it's not coming. const NOTIFICATION_TIMEOUT_SECS = 2000; -add_task(async function setup() { +add_setup(async function() { await addNotificationPermission(PAGE); }); diff -Nru firefox-99.0.1+build1/browser/base/content/test/captivePortal/browser_captivePortal_certErrorUI.js firefox-100.0+build1/browser/base/content/test/captivePortal/browser_captivePortal_certErrorUI.js --- firefox-99.0.1+build1/browser/base/content/test/captivePortal/browser_captivePortal_certErrorUI.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/captivePortal/browser_captivePortal_certErrorUI.js 2022-04-26 05:44:41.000000000 +0000 @@ -3,7 +3,7 @@ "use strict"; -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ ["captivedetect.canonicalURL", CANONICAL_URL], diff -Nru firefox-99.0.1+build1/browser/base/content/test/captivePortal/browser_captivePortal_https_only.js firefox-100.0+build1/browser/base/content/test/captivePortal/browser_captivePortal_https_only.js --- firefox-99.0.1+build1/browser/base/content/test/captivePortal/browser_captivePortal_https_only.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/captivePortal/browser_captivePortal_https_only.js 2022-04-26 05:44:41.000000000 +0000 @@ -12,7 +12,7 @@ const CANONICAL_URI = Services.io.newURI(testPath); const PERMISSION_NAME = "https-only-load-insecure"; -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ // That changes the canoncicalURL from "http://{server}/captive-detect/success.txt" // to http://example.com diff -Nru firefox-99.0.1+build1/browser/base/content/test/captivePortal/browser_captivePortalTabReference.js firefox-100.0+build1/browser/base/content/test/captivePortal/browser_captivePortalTabReference.js --- firefox-99.0.1+build1/browser/base/content/test/captivePortal/browser_captivePortalTabReference.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/captivePortal/browser_captivePortalTabReference.js 2022-04-26 05:44:41.000000000 +0000 @@ -40,7 +40,7 @@ gBrowser.removeTab(errorTab); } -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ ["captivedetect.canonicalURL", CANONICAL_URL], diff -Nru firefox-99.0.1+build1/browser/base/content/test/captivePortal/browser_closeCapPortalTabCanonicalURL.js firefox-100.0+build1/browser/base/content/test/captivePortal/browser_closeCapPortalTabCanonicalURL.js --- firefox-99.0.1+build1/browser/base/content/test/captivePortal/browser_closeCapPortalTabCanonicalURL.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/captivePortal/browser_closeCapPortalTabCanonicalURL.js 2022-04-26 05:44:41.000000000 +0000 @@ -35,7 +35,7 @@ response.setHeader("Location", CANONICAL_SUCCESS_URL); } -add_task(async function setup() { +add_setup(async function() { // Set up a mock server for handling captive portal redirect. server = new HttpServer(); server.registerPathHandler("/success", redirectHandler); diff -Nru firefox-99.0.1+build1/browser/base/content/test/contextMenu/browser_contextmenu.js firefox-100.0+build1/browser/base/content/test/contextMenu/browser_contextmenu.js --- firefox-99.0.1+build1/browser/base/content/test/contextMenu/browser_contextmenu.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/contextMenu/browser_contextmenu.js 2022-04-26 05:44:41.000000000 +0000 @@ -234,6 +234,19 @@ ]); }); +add_task(async function test_tel() { + await test_contextmenu("#test-tel", [ + "context-copyphone", + true, + "---", + null, + "context-searchselect", + true, + "context-searchselect-private", + true, + ]); +}); + add_task(async function test_image() { for (let selector of ["#test-image", "#test-svg-image"]) { await test_contextmenu( diff -Nru firefox-99.0.1+build1/browser/base/content/test/contextMenu/browser_contextmenu_loadblobinnewtab.js firefox-100.0+build1/browser/base/content/test/contextMenu/browser_contextmenu_loadblobinnewtab.js --- firefox-99.0.1+build1/browser/base/content/test/contextMenu/browser_contextmenu_loadblobinnewtab.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/contextMenu/browser_contextmenu_loadblobinnewtab.js 2022-04-26 05:44:41.000000000 +0000 @@ -135,7 +135,7 @@ return blobDataFromContent; } -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [["privacy.partition.bloburl_per_agent_cluster", false]], }); diff -Nru firefox-99.0.1+build1/browser/base/content/test/contextMenu/browser_contextmenu_touch.js firefox-100.0+build1/browser/base/content/test/contextMenu/browser_contextmenu_touch.js --- firefox-99.0.1+build1/browser/base/content/test/contextMenu/browser_contextmenu_touch.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/contextMenu/browser_contextmenu_touch.js 2022-04-26 05:44:41.000000000 +0000 @@ -35,7 +35,7 @@ } // Ensure that we can run touch events properly for windows [10] -add_task(async function setup() { +add_setup(async function() { let isWindows = AppConstants.isPlatformAndVersionAtLeast("win", "10.0"); await SpecialPowers.pushPrefEnv({ set: [["apz.test.fails_with_native_injection", isWindows]], diff -Nru firefox-99.0.1+build1/browser/base/content/test/contextMenu/subtst_contextmenu.html firefox-100.0+build1/browser/base/content/test/contextMenu/subtst_contextmenu.html --- firefox-99.0.1+build1/browser/base/content/test/contextMenu/subtst_contextmenu.html 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/contextMenu/subtst_contextmenu.html 2022-04-26 05:44:41.000000000 +0000 @@ -19,6 +19,7 @@ "Click the monkey!"; Mail the monkey!
+Call random number!

diff -Nru firefox-99.0.1+build1/browser/base/content/test/favicons/browser_preferred_icons.js firefox-100.0+build1/browser/base/content/test/favicons/browser_preferred_icons.js --- firefox-99.0.1+build1/browser/base/content/test/favicons/browser_preferred_icons.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/favicons/browser_preferred_icons.js 2022-04-26 05:44:41.000000000 +0000 @@ -28,7 +28,7 @@ }); } -add_task(async function setup() { +add_setup(async function() { const URL = ROOT + "discovery.html"; let iconPromise = waitIcon("http://mochi.test:8888/favicon.ico"); let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, URL); diff -Nru firefox-99.0.1+build1/browser/base/content/test/forms/browser_selectpopup_colors.js firefox-100.0+build1/browser/base/content/test/forms/browser_selectpopup_colors.js --- firefox-99.0.1+build1/browser/base/content/test/forms/browser_selectpopup_colors.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/forms/browser_selectpopup_colors.js 2022-04-26 05:44:41.000000000 +0000 @@ -459,7 +459,7 @@ // System colors may be different in content pages and chrome pages. let kDefaultSelectStyles = {}; -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [["dom.forms.select.customstyling", true]], }); @@ -797,7 +797,10 @@ if (AppConstants.isPlatformAndVersionAtLeast("win", "10")) { add_task(async function test_darkmode() { // Force dark mode: + let darkModeQuery = matchMedia("(prefers-color-scheme: dark)"); + let darkModeChange = BrowserTestUtils.waitForEvent(darkModeQuery, "change"); await SpecialPowers.pushPrefEnv({ set: [["ui.systemUsesDarkTheme", 1]] }); + await darkModeChange; // Determine colours from the main context menu: let cs = getComputedStyle(document.documentElement); diff -Nru firefox-99.0.1+build1/browser/base/content/test/forms/browser_selectpopup.js firefox-100.0+build1/browser/base/content/test/forms/browser_selectpopup.js --- firefox-99.0.1+build1/browser/base/content/test/forms/browser_selectpopup.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/forms/browser_selectpopup.js 2022-04-26 05:44:41.000000000 +0000 @@ -321,7 +321,7 @@ BrowserTestUtils.removeTab(tab); } -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [["dom.forms.select.customstyling", true]], }); diff -Nru firefox-99.0.1+build1/browser/base/content/test/forms/browser_selectpopup_searchfocus.js firefox-100.0+build1/browser/base/content/test/forms/browser_selectpopup_searchfocus.js --- firefox-99.0.1+build1/browser/base/content/test/forms/browser_selectpopup_searchfocus.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/forms/browser_selectpopup_searchfocus.js 2022-04-26 05:44:41.000000000 +0000 @@ -6,7 +6,7 @@ ' ' + ""; -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [["dom.forms.selectSearch", true]], }); diff -Nru firefox-99.0.1+build1/browser/base/content/test/fullscreen/browser_fullscreen_window_focus.js firefox-100.0+build1/browser/base/content/test/fullscreen/browser_fullscreen_window_focus.js --- firefox-99.0.1+build1/browser/base/content/test/fullscreen/browser_fullscreen_window_focus.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/fullscreen/browser_fullscreen_window_focus.js 2022-04-26 05:44:41.000000000 +0000 @@ -76,7 +76,7 @@ BrowserTestUtils.removeTab(tab); } -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ ["dom.disable_open_during_load", false], // Allow window.focus calls without user interaction diff -Nru firefox-99.0.1+build1/browser/base/content/test/fullscreen/browser_fullscreen_window_open.js firefox-100.0+build1/browser/base/content/test/fullscreen/browser_fullscreen_window_open.js --- firefox-99.0.1+build1/browser/base/content/test/fullscreen/browser_fullscreen_window_open.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/fullscreen/browser_fullscreen_window_open.js 2022-04-26 05:44:41.000000000 +0000 @@ -28,7 +28,7 @@ BrowserTestUtils.removeTab(tab); } -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ ["dom.disable_open_during_load", false], // Allow window.open calls without user interaction diff -Nru firefox-99.0.1+build1/browser/base/content/test/general/browser_bug676619.js firefox-100.0+build1/browser/base/content/test/general/browser_bug676619.js --- firefox-99.0.1+build1/browser/base/content/test/general/browser_bug676619.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/general/browser_bug676619.js 2022-04-26 05:44:41.000000000 +0000 @@ -156,9 +156,8 @@ } async function setDownloadDir() { - let tmpDir = await PathUtils.getTempDir(); - tmpDir = PathUtils.join( - tmpDir, + let tmpDir = PathUtils.join( + PathUtils.tempDir, "testsavedir" + Math.floor(Math.random() * 2 ** 32) ); // Create this dir if it doesn't exist (ignores existing dirs) diff -Nru firefox-99.0.1+build1/browser/base/content/test/general/browser_datachoices_notification.js firefox-100.0+build1/browser/base/content/test/general/browser_datachoices_notification.js --- firefox-99.0.1+build1/browser/base/content/test/general/browser_datachoices_notification.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/general/browser_datachoices_notification.js 2022-04-26 05:44:41.000000000 +0000 @@ -6,14 +6,12 @@ // Pass an empty scope object to the import to prevent "leaked window property" // errors in tests. -var Preferences = ChromeUtils.import( - "resource://gre/modules/Preferences.jsm", - {} -).Preferences; -var TelemetryReportingPolicy = ChromeUtils.import( - "resource://gre/modules/TelemetryReportingPolicy.jsm", - {} -).TelemetryReportingPolicy; +var { Preferences } = ChromeUtils.import( + "resource://gre/modules/Preferences.jsm" +); +var { TelemetryReportingPolicy } = ChromeUtils.import( + "resource://gre/modules/TelemetryReportingPolicy.jsm" +); const PREF_BRANCH = "datareporting.policy."; const PREF_FIRST_RUN = "toolkit.telemetry.reportingpolicy.firstRun"; @@ -115,7 +113,7 @@ await promiseNextTick(); }; -add_task(async function setup() { +add_setup(async function() { const isFirstRun = Preferences.get(PREF_FIRST_RUN, true); const bypassNotification = Preferences.get(PREF_BYPASS_NOTIFICATION, true); const currentPolicyVersion = Preferences.get(PREF_CURRENT_POLICY_VERSION, 1); diff -Nru firefox-99.0.1+build1/browser/base/content/test/general/browser.ini firefox-100.0+build1/browser/base/content/test/general/browser.ini --- firefox-99.0.1+build1/browser/base/content/test/general/browser.ini 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/general/browser.ini 2022-04-26 05:44:41.000000000 +0000 @@ -233,6 +233,7 @@ # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. [browser_middleMouse_noJSPaste.js] https_first_disabled = true +skip-if = apple_silicon && !debug # Bug 1724711 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. [browser_minimize.js] # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. diff -Nru firefox-99.0.1+build1/browser/base/content/test/general/browser_newWindowDrop.js firefox-100.0+build1/browser/base/content/test/general/browser_newWindowDrop.js --- firefox-99.0.1+build1/browser/base/content/test/general/browser_newWindowDrop.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/general/browser_newWindowDrop.js 2022-04-26 05:44:41.000000000 +0000 @@ -211,9 +211,6 @@ let newWindowButton = document.getElementById("new-window-button"); ok(newWindowButton, "New Window button exists"); - let tmp = {}; - ChromeUtils.import("resource://testing-common/TestUtils.jsm", tmp); - let awaitDrop = BrowserTestUtils.waitForEvent(newWindowButton, "drop"); let loadedPromises = expectedURLs.map(url => diff -Nru firefox-99.0.1+build1/browser/base/content/test/general/browser_remoteTroubleshoot.js firefox-100.0+build1/browser/base/content/test/general/browser_remoteTroubleshoot.js --- firefox-99.0.1+build1/browser/base/content/test/general/browser_remoteTroubleshoot.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/general/browser_remoteTroubleshoot.js 2022-04-26 05:44:41.000000000 +0000 @@ -82,10 +82,8 @@ let updateChannel = null; try { - updateChannel = ChromeUtils.import( - "resource://gre/modules/UpdateUtils.jsm", - {} - ).UpdateUtils.UpdateChannel; + updateChannel = ChromeUtils.import("resource://gre/modules/UpdateUtils.jsm") + .UpdateUtils.UpdateChannel; } catch (ex) {} if (!updateChannel) { Assert.ok( diff -Nru firefox-99.0.1+build1/browser/base/content/test/general/browser_remoteWebNavigation_postdata.js firefox-100.0+build1/browser/base/content/test/general/browser_remoteWebNavigation_postdata.js --- firefox-99.0.1+build1/browser/base/content/test/general/browser_remoteWebNavigation_postdata.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/general/browser_remoteWebNavigation_postdata.js 2022-04-26 05:44:41.000000000 +0000 @@ -11,18 +11,17 @@ } add_task(async function test_remoteWebNavigation_postdata() { - let obj = {}; - ChromeUtils.import("resource://testing-common/httpd.js", obj); - ChromeUtils.import("resource://services-common/utils.js", obj); + let { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js"); + let { CommonUtils } = ChromeUtils.import( + "resource://services-common/utils.js" + ); - let server = new obj.HttpServer(); + let server = new HttpServer(); server.start(-1); await new Promise(resolve => { server.registerPathHandler("/test", (request, response) => { - let body = obj.CommonUtils.readBytesFromInputStream( - request.bodyInputStream - ); + let body = CommonUtils.readBytesFromInputStream(request.bodyInputStream); is(body, "success", "request body is correct"); is(request.method, "POST", "request was a post"); response.write("Received from POST: " + body); diff -Nru firefox-99.0.1+build1/browser/base/content/test/general/browser_storagePressure_notification.js firefox-100.0+build1/browser/base/content/test/general/browser_storagePressure_notification.js --- firefox-99.0.1+build1/browser/base/content/test/general/browser_storagePressure_notification.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/general/browser_storagePressure_notification.js 2022-04-26 05:44:41.000000000 +0000 @@ -27,7 +27,7 @@ ]; return Promise.all(promises); } -add_task(async function setup() { +add_setup(async function() { let win = await BrowserTestUtils.openNewBrowserWindow(); // Open a new tab to keep the window open. await BrowserTestUtils.openNewForegroundTab( diff -Nru firefox-99.0.1+build1/browser/base/content/test/general/browser_unknownContentType_title.js firefox-100.0+build1/browser/base/content/test/general/browser_unknownContentType_title.js --- firefox-99.0.1+build1/browser/base/content/test/general/browser_unknownContentType_title.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/general/browser_unknownContentType_title.js 2022-04-26 05:44:41.000000000 +0000 @@ -16,10 +16,9 @@ }); } -add_task(async function setup() { - let tmpDir = await PathUtils.getTempDir(); - tmpDir = PathUtils.join( - tmpDir, +add_setup(async function() { + let tmpDir = PathUtils.join( + PathUtils.tempDir, "testsavedir" + Math.floor(Math.random() * 2 ** 32) ); // Create this dir if it doesn't exist (ignores existing dirs) diff -Nru firefox-99.0.1+build1/browser/base/content/test/keyboard/browser_toolbarKeyNav.js firefox-100.0+build1/browser/base/content/test/keyboard/browser_toolbarKeyNav.js --- firefox-99.0.1+build1/browser/base/content/test/keyboard/browser_toolbarKeyNav.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/keyboard/browser_toolbarKeyNav.js 2022-04-26 05:44:41.000000000 +0000 @@ -94,7 +94,7 @@ const BOOKMARKS_COUNT = 100; -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ ["browser.toolbars.keyboard_navigation", true], diff -Nru firefox-99.0.1+build1/browser/base/content/test/permissions/browser_autoplay_blocked.js firefox-100.0+build1/browser/base/content/test/permissions/browser_autoplay_blocked.js --- firefox-99.0.1+build1/browser/base/content/test/permissions/browser_autoplay_blocked.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/permissions/browser_autoplay_blocked.js 2022-04-26 05:44:41.000000000 +0000 @@ -73,7 +73,7 @@ ok(!listEntryCount, "List of permissions is empty"); } -add_task(async function setup() { +add_setup(async function() { registerCleanupFunction(() => { Services.perms.removeAll(); Services.prefs.clearUserPref(AUTOPLAY_PREF); diff -Nru firefox-99.0.1+build1/browser/base/content/test/permissions/browser_permission_delegate_geo.js firefox-100.0+build1/browser/base/content/test/permissions/browser_permission_delegate_geo.js --- firefox-99.0.1+build1/browser/base/content/test/permissions/browser_permission_delegate_geo.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/permissions/browser_permission_delegate_geo.js 2022-04-26 05:44:41.000000000 +0000 @@ -112,7 +112,7 @@ } } -add_task(async function setup() { +add_setup(async function() { await new Promise(r => { SpecialPowers.pushPrefEnv( { diff -Nru firefox-99.0.1+build1/browser/base/content/test/permissions/browser_permissions.js firefox-100.0+build1/browser/base/content/test/permissions/browser_permissions.js --- firefox-99.0.1+build1/browser/base/content/test/permissions/browser_permissions.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/permissions/browser_permissions.js 2022-04-26 05:44:41.000000000 +0000 @@ -447,9 +447,6 @@ // that this works correctly, i.e. the permission items are added to the // anchor when relevant, and other permission items are added to the default // anchor, and adding/removing permissions preserves this behavior correctly. - SpecialPowers.pushPrefEnv({ - set: [["browser.contentblocking.state-partitioning.mvp.ui.enabled", true]], - }); await BrowserTestUtils.withNewTab(PERMISSIONS_PAGE, async function(browser) { await openPermissionPopup(); diff -Nru firefox-99.0.1+build1/browser/base/content/test/popupNotifications/browser_displayURI.js firefox-100.0+build1/browser/base/content/test/popupNotifications/browser_displayURI.js --- firefox-99.0.1+build1/browser/base/content/test/popupNotifications/browser_displayURI.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/popupNotifications/browser_displayURI.js 2022-04-26 05:44:41.000000000 +0000 @@ -84,7 +84,7 @@ } } -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ ["media.navigator.permission.fake", true], diff -Nru firefox-99.0.1+build1/browser/base/content/test/popupNotifications/head.js firefox-100.0+build1/browser/base/content/test/popupNotifications/head.js --- firefox-99.0.1+build1/browser/base/content/test/popupNotifications/head.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/popupNotifications/head.js 2022-04-26 05:44:41.000000000 +0000 @@ -348,7 +348,7 @@ } // Extra secondary actions appear in a menu. - notification.secondaryButton.nextElementSibling.nextElementSibling.focus(); + notification.secondaryButton.nextElementSibling.focus(); popup.addEventListener( "popupshown", diff -Nru firefox-99.0.1+build1/browser/base/content/test/popups/browser_popup_blocker.js firefox-100.0+build1/browser/base/content/test/popups/browser_popup_blocker.js --- firefox-99.0.1+build1/browser/base/content/test/popups/browser_popup_blocker.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/popups/browser_popup_blocker.js 2022-04-26 05:44:41.000000000 +0000 @@ -15,7 +15,7 @@ } } -add_task(async function setup() { +add_setup(async function() { // Enable the popup blocker. await SpecialPowers.pushPrefEnv({ set: [["dom.disable_open_during_load", true]], diff -Nru firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_cookies_subview.js firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_cookies_subview.js --- firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_cookies_subview.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_cookies_subview.js 2022-04-26 05:44:41.000000000 +0000 @@ -12,7 +12,7 @@ const TPC_PREF = "network.cookie.cookieBehavior"; -add_task(async function setup() { +add_setup(async function() { await UrlClassifierTestUtils.addTestTrackers(); registerCleanupFunction(() => { diff -Nru firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_cryptominers.js firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_cryptominers.js --- firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_cryptominers.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_cryptominers.js 2022-04-26 05:44:41.000000000 +0000 @@ -8,7 +8,7 @@ const CM_PROTECTION_PREF = "privacy.trackingprotection.cryptomining.enabled"; let cmHistogram; -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ [ diff -Nru firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_dfpi_rollout.js firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_dfpi_rollout.js --- firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_dfpi_rollout.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_dfpi_rollout.js 2022-04-26 05:44:41.000000000 +0000 @@ -5,6 +5,12 @@ "resource://testing-common/TelemetryTestUtils.jsm" ); +ChromeUtils.defineModuleGetter( + this, + "BrowserGlue", + "resource:///modules/BrowserGlue.jsm" +); + const PREF_DFPI_ENABLED_BY_DEFAULT = "privacy.restrict3rdpartystorage.rollout.enabledByDefault"; const COOKIE_BEHAVIOR_PREF = "network.cookie.cookieBehavior"; @@ -19,19 +25,27 @@ ]; const SEARCH_PREFS_OPT_OUT = [ - ["browser.search.param.google_channel_us", "xus7"], - ["browser.search.param.google_channel_row", "xrow7"], - ["browser.search.param.bing_ptag", "MOZZ0000000032"], + ["browser.search.param.google_channel_us", "tus7"], + ["browser.search.param.google_channel_row", "trow7"], + ["browser.search.param.bing_ptag", "MOZZ0000000031"], ]; -registerCleanupFunction(function() { +function cleanup() { + [...SEARCH_PREFS_OPT_IN, ...SEARCH_PREFS_OPT_OUT].forEach(([key]) => + Services.prefs.clearUserPref(key) + ); + + [COOKIE_BEHAVIOR_PREF, PREF_DFPI_ENABLED_BY_DEFAULT].forEach( + Services.prefs.clearUserPref + ); + + BrowserGlue._defaultCookieBehaviorAtStartup = previousDefaultCB; defaultPrefs.setIntPref(COOKIE_BEHAVIOR_PREF, previousDefaultCB); - [ - PREF_DFPI_ENABLED_BY_DEFAULT, - ...SEARCH_PREFS_OPT_IN, - ...SEARCH_PREFS_OPT_OUT, - ].forEach(([key]) => Services.prefs.clearUserPref(key)); -}); + + // Reset the rollout scalar back to 2 = unset. We have to simulate this on + // test cleanup, because BrowserGlue only sets this once initially. + Services.telemetry.scalarSet("privacy.dfpi_rollout_enabledByDefault", 2); +} function testSearchPrefState(optIn) { let expectedPrefs = optIn ? SEARCH_PREFS_OPT_IN : SEARCH_PREFS_OPT_OUT; @@ -68,6 +82,11 @@ // Tests that the dFPI rollout pref updates the default cookieBehavior to 5, // sets the correct search prefs and records telemetry. add_task(async function testdFPIRolloutPref() { + // The BrowserGlue code which computes this flag runs before we can set the + // default cookie behavior for this test. Thus we need to overwrite it in + // order for the opt-out to work correctly. + BrowserGlue._defaultCookieBehaviorAtStartup = + Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER; defaultPrefs.setIntPref( COOKIE_BEHAVIOR_PREF, Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER @@ -107,4 +126,77 @@ ); testSearchPrefState(true); testTelemetryState(true); + + cleanup(); +}); + +/** + * Setting the rollout pref to false should revert to the initial default cookie + * behavior, not always BEHAVIOR_REJECT_TRACKER. + */ +add_task(async function testdFPIRolloutPrefNonDefaultCookieBehavior() { + BrowserGlue._defaultCookieBehaviorAtStartup = + Ci.nsICookieService.BEHAVIOR_ACCEPT; + defaultPrefs.setIntPref( + COOKIE_BEHAVIOR_PREF, + Ci.nsICookieService.BEHAVIOR_ACCEPT + ); + + is( + Services.prefs.getIntPref(COOKIE_BEHAVIOR_PREF), + Ci.nsICookieService.BEHAVIOR_ACCEPT, + "Initial cookie behavior should be BEHAVIOR_ACCEPT" + ); + + Services.prefs.setBoolPref(PREF_DFPI_ENABLED_BY_DEFAULT, true); + is( + defaultPrefs.getIntPref(COOKIE_BEHAVIOR_PREF), + Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN, + "Default cookie behavior should be set to dFPI." + ); + + Services.prefs.setBoolPref(PREF_DFPI_ENABLED_BY_DEFAULT, false); + is( + defaultPrefs.getIntPref(COOKIE_BEHAVIOR_PREF), + Ci.nsICookieService.BEHAVIOR_ACCEPT, + "Default cookie behavior should be set to BEHAVIOR_ACCEPT." + ); + + cleanup(); +}); + +/** + * When a client already ships with dFPI enabled, toggling the rollout pref + * should not change cookie behavior. + */ +add_task(async function testdFPIRolloutPrefDFPIAlreadyEnabled() { + // Simulate TCP enabled by default. + BrowserGlue._defaultCookieBehaviorAtStartup = + Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN; + defaultPrefs.setIntPref( + COOKIE_BEHAVIOR_PREF, + Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN + ); + + is( + Services.prefs.getIntPref(COOKIE_BEHAVIOR_PREF), + Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN, + "Initial cookie behavior should be BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN" + ); + + Services.prefs.setBoolPref(PREF_DFPI_ENABLED_BY_DEFAULT, true); + is( + defaultPrefs.getIntPref(COOKIE_BEHAVIOR_PREF), + Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN, + "Default cookie behavior should still be BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN." + ); + + Services.prefs.setBoolPref(PREF_DFPI_ENABLED_BY_DEFAULT, false); + is( + defaultPrefs.getIntPref(COOKIE_BEHAVIOR_PREF), + Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN, + "Default cookie behavior should still be BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN." + ); + + cleanup(); }); diff -Nru firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_fingerprinters.js firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_fingerprinters.js --- firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_fingerprinters.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_fingerprinters.js 2022-04-26 05:44:41.000000000 +0000 @@ -8,7 +8,7 @@ const FP_PROTECTION_PREF = "privacy.trackingprotection.fingerprinting.enabled"; let fpHistogram; -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ [ diff -Nru firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI.js firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI.js --- firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI.js 2022-04-26 05:44:41.000000000 +0000 @@ -18,7 +18,7 @@ "resource://testing-common/CustomizableUITestUtils.jsm" ); -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ // Set the auto hide timing to 100ms for blocking the test less. diff -Nru firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_milestones.js firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_milestones.js --- firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_milestones.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_milestones.js 2022-04-26 05:44:41.000000000 +0000 @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ // Hide protections cards so as not to trigger more async messaging diff -Nru firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_open_preferences.js firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_open_preferences.js --- firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_open_preferences.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_open_preferences.js 2022-04-26 05:44:41.000000000 +0000 @@ -40,7 +40,7 @@ BrowserTestUtils.removeTab(gBrowser.selectedTab); } -add_task(async function setup() { +add_setup(async function() { await UrlClassifierTestUtils.addTestTrackers(); let oldCanRecord = Services.telemetry.canRecordExtended; Services.telemetry.canRecordExtended = true; diff -Nru firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_report_breakage.js firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_report_breakage.js --- firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_report_breakage.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_report_breakage.js 2022-04-26 05:44:41.000000000 +0000 @@ -23,7 +23,7 @@ "resource://gre/modules/Preferences.jsm" ); -add_task(async function setup() { +add_setup(async function() { await UrlClassifierTestUtils.addTestTrackers(); registerCleanupFunction(() => { diff -Nru firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_shield_visibility.js firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_shield_visibility.js --- firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_shield_visibility.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_shield_visibility.js 2022-04-26 05:44:41.000000000 +0000 @@ -74,7 +74,7 @@ }, ]; -add_task(async function setup() { +add_task(async function() { await SpecialPowers.pushPrefEnv({ set: [ // By default, proxies don't apply to 127.0.0.1. We need them to for this test, though: diff -Nru firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_socialtracking.js firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_socialtracking.js --- firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_socialtracking.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_socialtracking.js 2022-04-26 05:44:41.000000000 +0000 @@ -9,7 +9,7 @@ const ST_PROTECTION_PREF = "privacy.trackingprotection.socialtracking.enabled"; const ST_BLOCK_COOKIES_PREF = "privacy.socialtracking.block_cookies.enabled"; -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ [ST_BLOCK_COOKIES_PREF, true], diff -Nru firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_subview_shim.js firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_subview_shim.js --- firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_subview_shim.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_subview_shim.js 2022-04-26 05:44:41.000000000 +0000 @@ -14,7 +14,7 @@ const TRACKING_PAGE = "http://example.net/browser/browser/base/content/test/protectionsUI/trackingPage.html"; -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ ["privacy.trackingprotection.enabled", true], diff -Nru firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_telemetry.js firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_telemetry.js --- firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_telemetry.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_telemetry.js 2022-04-26 05:44:41.000000000 +0000 @@ -29,7 +29,7 @@ return getShieldHistogram().snapshot().values; } -add_task(async function setup() { +add_setup(async function() { await UrlClassifierTestUtils.addTestTrackers(); Services.prefs.setBoolPref(DTSCBN_PREF, true); diff -Nru firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_trackers_subview.js firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_trackers_subview.js --- firefox-99.0.1+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_trackers_subview.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/protectionsUI/browser_protectionsUI_trackers_subview.js 2022-04-26 05:44:41.000000000 +0000 @@ -13,7 +13,7 @@ const TP_PREF = "privacy.trackingprotection.enabled"; -add_task(async function setup() { +add_setup(async function() { await UrlClassifierTestUtils.addTestTrackers(); registerCleanupFunction(() => { diff -Nru firefox-99.0.1+build1/browser/base/content/test/sidebar/browser.ini firefox-100.0+build1/browser/base/content/test/sidebar/browser.ini --- firefox-99.0.1+build1/browser/base/content/test/sidebar/browser.ini 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/sidebar/browser.ini 2022-04-26 05:44:41.000000000 +0000 @@ -1,6 +1,7 @@ [DEFAULT] [browser_sidebar_adopt.js] +[browser_sidebar_app_locale_changed.js] [browser_sidebar_keys.js] [browser_sidebar_move.js] [browser_sidebar_switcher.js] diff -Nru firefox-99.0.1+build1/browser/base/content/test/sidebar/browser_sidebar_adopt.js firefox-100.0+build1/browser/base/content/test/sidebar/browser_sidebar_adopt.js --- firefox-99.0.1+build1/browser/base/content/test/sidebar/browser_sidebar_adopt.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/sidebar/browser_sidebar_adopt.js 2022-04-26 05:44:41.000000000 +0000 @@ -12,7 +12,7 @@ ok(false, "This event shouldn't have fired"); } -add_task(function setup() { +add_setup(function() { CustomizableUI.addWidgetToArea("sidebar-button", "nav-bar"); registerCleanupFunction(() => CustomizableUI.removeWidgetFromArea("sidebar-button") diff -Nru firefox-99.0.1+build1/browser/base/content/test/sidebar/browser_sidebar_app_locale_changed.js firefox-100.0+build1/browser/base/content/test/sidebar/browser_sidebar_app_locale_changed.js --- firefox-99.0.1+build1/browser/base/content/test/sidebar/browser_sidebar_app_locale_changed.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/sidebar/browser_sidebar_app_locale_changed.js 2022-04-26 05:44:41.000000000 +0000 @@ -0,0 +1,56 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * This file tests that the sidebar recreates the contents of the element + * for live app locale switching. + */ + +add_task(function cleanup() { + registerCleanupFunction(() => { + SidebarUI.hide(); + }); +}); + +/** + * @param {string} sidebarName + */ +async function testLiveReloading(sidebarName) { + info("Showing the sidebar " + sidebarName); + await SidebarUI.show(sidebarName); + + function getTreeChildren() { + const sidebarDoc = document.querySelector("#sidebar").contentWindow + .document; + return sidebarDoc.querySelector(".sidebar-placesTreechildren"); + } + + const childrenBefore = getTreeChildren(); + ok(childrenBefore, "Found the sidebar children"); + is(childrenBefore, getTreeChildren(), "The children start out as equal"); + + info("Simulating an app locale change."); + Services.obs.notifyObservers(null, "intl:app-locales-changed"); + + await TestUtils.waitForCondition( + getTreeChildren, + "Waiting for a new child tree element." + ); + + isnot( + childrenBefore, + getTreeChildren(), + "The tree's contents are re-computed." + ); + + info("Hiding the sidebar"); + SidebarUI.hide(); +} + +add_task(async function test_bookmarks_sidebar() { + await testLiveReloading("viewBookmarksSidebar"); +}); + +add_task(async function test_history_sidebar() { + await testLiveReloading("viewHistorySidebar"); +}); diff -Nru firefox-99.0.1+build1/browser/base/content/test/siteIdentity/browser_bug902156.js firefox-100.0+build1/browser/base/content/test/siteIdentity/browser_bug902156.js --- firefox-99.0.1+build1/browser/base/content/test/siteIdentity/browser_bug902156.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/siteIdentity/browser_bug902156.js 2022-04-26 05:44:41.000000000 +0000 @@ -34,7 +34,7 @@ "https://test2.example.com" ); -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [[PREF_ACTIVE, true]] }); }); diff -Nru firefox-99.0.1+build1/browser/base/content/test/siteIdentity/browser_check_identity_state.js firefox-100.0+build1/browser/base/content/test/siteIdentity/browser_check_identity_state.js --- firefox-99.0.1+build1/browser/base/content/test/siteIdentity/browser_check_identity_state.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/siteIdentity/browser_check_identity_state.js 2022-04-26 05:44:41.000000000 +0000 @@ -18,10 +18,6 @@ return BrowserTestUtils.openNewForegroundTab(gBrowser, url, true); } -function getIdentityMode(aWindow = window) { - return aWindow.document.getElementById("identity-box").className; -} - function getConnectionState() { // Prevents items that are being lazy loaded causing issues document.getElementById("identity-icon-box").click(); @@ -75,7 +71,6 @@ "certificate", "compat", "config", - "devtools", "downloads", "ion", "license", @@ -879,32 +874,3 @@ ]; await pbModeTest(prefs, false); }); - -/** - * Tests that sites opened via the PDF viewer have the correct identity state. - */ -add_task(async function test_pdf() { - const PDF_URI_NOSCHEME = - getRootDirectory(gTestPath).replace( - "chrome://mochitests/content", - "example.com" - ) + "file_pdf.pdf"; - - const PDF_URI_SECURE = "https://" + PDF_URI_NOSCHEME; - const PDF_URI_INSECURE = "http://" + PDF_URI_NOSCHEME; - - await BrowserTestUtils.withNewTab(PDF_URI_INSECURE, async () => { - is( - getIdentityMode(), - "notSecure", - "Identity should be notSecure for a PDF served via HTTP." - ); - }); - await BrowserTestUtils.withNewTab(PDF_URI_SECURE, async () => { - is( - getIdentityMode(), - "verifiedDomain", - "Identity should be verifiedDomain for a PDF served via HTTPS." - ); - }); -}); diff -Nru firefox-99.0.1+build1/browser/base/content/test/siteIdentity/browser_check_identity_state_pdf.js firefox-100.0+build1/browser/base/content/test/siteIdentity/browser_check_identity_state_pdf.js --- firefox-99.0.1+build1/browser/base/content/test/siteIdentity/browser_check_identity_state_pdf.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/siteIdentity/browser_check_identity_state_pdf.js 2022-04-26 05:44:41.000000000 +0000 @@ -0,0 +1,80 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that sites opened via the PDF viewer have the correct identity state. + */ + +"use strict"; + +const { AppConstants } = ChromeUtils.import( + "resource://gre/modules/AppConstants.jsm" +); + +function testIdentityMode(uri, expectedState, message) { + return BrowserTestUtils.withNewTab(uri, () => { + is(getIdentityMode(), expectedState, message); + }); +} + +/** + * Test site identity state for PDFs served via file URI. + */ +add_task(async function test_pdf_fileURI() { + let path = getTestFilePath("./file_pdf.pdf"); + info("path:" + path); + + await testIdentityMode( + path, + "localResource", + "Identity should be localResource for a PDF served via file URI" + ); +}); + +/** + * Test site identity state for PDFs served via blob URI. + */ +add_task(async function test_pdf_blobURI() { + let uri = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" + ) + "file_pdf_blob.html"; + + await BrowserTestUtils.withNewTab(uri, async browser => { + let newTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, null, true); + + await BrowserTestUtils.synthesizeMouseAtCenter("a", {}, browser); + await newTabOpened; + + is( + getIdentityMode(), + "localResource", + "Identity should be localResource for a PDF served via blob URI" + ); + + BrowserTestUtils.removeTab(gBrowser.selectedTab); + }); +}); + +/** + * Test site identity state for PDFs served via HTTP. + */ +add_task(async function test_pdf_http() { + const PDF_URI_NOSCHEME = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "example.com" + ) + "file_pdf.pdf"; + + await testIdentityMode( + "http://" + PDF_URI_NOSCHEME, + "notSecure", + "Identity should be notSecure for a PDF served via HTTP." + ); + await testIdentityMode( + "https://" + PDF_URI_NOSCHEME, + "verifiedDomain", + "Identity should be verifiedDomain for a PDF served via HTTPS." + ); +}); diff -Nru firefox-99.0.1+build1/browser/base/content/test/siteIdentity/browser.ini firefox-100.0+build1/browser/base/content/test/siteIdentity/browser.ini --- firefox-99.0.1+build1/browser/base/content/test/siteIdentity/browser.ini 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/siteIdentity/browser.ini 2022-04-26 05:44:41.000000000 +0000 @@ -131,8 +131,11 @@ test_no_mcb_for_onions.html [browser_check_identity_state.js] https_first_disabled = true +[browser_check_identity_state_pdf.js] +https_first_disabled = true support-files = - file_pdf.pdf + file_pdf.pdf + file_pdf_blob.html [browser_iframe_navigation.js] https_first_disabled = true support-files = diff -Nru firefox-99.0.1+build1/browser/base/content/test/siteIdentity/file_pdf_blob.html firefox-100.0+build1/browser/base/content/test/siteIdentity/file_pdf_blob.html --- firefox-99.0.1+build1/browser/base/content/test/siteIdentity/file_pdf_blob.html 1970-01-01 00:00:00.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/siteIdentity/file_pdf_blob.html 2022-04-26 05:44:41.000000000 +0000 @@ -0,0 +1,18 @@ + + + + + + + + + diff -Nru firefox-99.0.1+build1/browser/base/content/test/siteIdentity/head.js firefox-100.0+build1/browser/base/content/test/siteIdentity/head.js --- firefox-99.0.1+build1/browser/base/content/test/siteIdentity/head.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/siteIdentity/head.js 2022-04-26 05:44:41.000000000 +0000 @@ -18,6 +18,10 @@ return viewShown; } +function getIdentityMode(aWindow = window) { + return aWindow.document.getElementById("identity-box").className; +} + /** * Waits for a load (or custom) event to finish in a given tab. If provided * load an uri into the tab. diff -Nru firefox-99.0.1+build1/browser/base/content/test/static/browser_all_files_referenced.js firefox-100.0+build1/browser/base/content/test/static/browser_all_files_referenced.js --- firefox-99.0.1+build1/browser/base/content/test/static/browser_all_files_referenced.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/static/browser_all_files_referenced.js 2022-04-26 05:44:41.000000000 +0000 @@ -33,6 +33,13 @@ "chrome://browser/content/assets/moz-vpn.svg", "chrome://browser/content/assets/vpn-logo.svg", "chrome://browser/content/assets/focus-promo.png", + "chrome://browser/content/preferences/more-from-mozilla-qr-code-advanced.svg", + "chrome://browser/content/assets/klar-qr-code.svg", + + // These app marketplace icons are referenced based on the user's locale + // in browser/components/newtab/content-src/aboutwelcome/components/MobileDownloads.jsx + "chrome://activity-stream/content/data/content/assets/app-marketplace-icons/en-US/ios.svg", + "chrome://activity-stream/content/data/content/assets/app-marketplace-icons/en-US/android.png", // toolkit/components/pdfjs/content/build/pdf.js "resource://pdf.js/web/images/", @@ -58,6 +65,12 @@ // Page data schemas are referenced programmatically. "chrome://browser/content/pagedata/schemas/", + + // Nimbus schemas are referenced programmatically. + "resource://nimbus/schemas/", + + // Activity stream schemas are referenced programmatically. + "resource://activity-stream/schemas", ]; // These are not part of the omni.ja file, so we find them only when running @@ -127,10 +140,6 @@ // These files URLs are constructed programatically at run time. { file: - "chrome://browser/content/preferences/more-from-mozilla-qr-code-advanced.svg", - }, - { - file: "chrome://browser/content/preferences/more-from-mozilla-qr-code-simple.svg", }, { diff -Nru firefox-99.0.1+build1/browser/base/content/test/static/browser_misused_characters_in_strings.js firefox-100.0+build1/browser/base/content/test/static/browser_misused_characters_in_strings.js --- firefox-99.0.1+build1/browser/base/content/test/static/browser_misused_characters_in_strings.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/static/browser_misused_characters_in_strings.js 2022-04-26 05:44:41.000000000 +0000 @@ -265,8 +265,7 @@ add_task(async function checkAllTheFluents() { let uris = await getAllTheFiles(".ftl"); let { FluentParser, Visitor } = ChromeUtils.import( - "resource://testing-common/FluentSyntax.jsm", - {} + "resource://testing-common/FluentSyntax.jsm" ); class TextElementVisitor extends Visitor { diff -Nru firefox-99.0.1+build1/browser/base/content/test/static/browser_parsable_css.js firefox-100.0+build1/browser/base/content/test/static/browser_parsable_css.js --- firefox-99.0.1+build1/browser/base/content/test/static/browser_parsable_css.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/static/browser_parsable_css.js 2022-04-26 05:44:41.000000000 +0000 @@ -407,10 +407,9 @@ // Create a clean iframe to load all the files into. This needs to live at a // chrome URI so that it's allowed to load and parse any styles. let testFile = getRootDirectory(gTestPath) + "dummy_page.html"; - let HiddenFrame = ChromeUtils.import( - "resource://gre/modules/HiddenFrame.jsm", - {} - ).HiddenFrame; + let { HiddenFrame } = ChromeUtils.import( + "resource://gre/modules/HiddenFrame.jsm" + ); let hiddenFrame = new HiddenFrame(); let win = await hiddenFrame.get(); let iframe = win.document.createElementNS( diff -Nru firefox-99.0.1+build1/browser/base/content/test/sync/browser_contextmenu_sendpage.js firefox-100.0+build1/browser/base/content/test/sync/browser_contextmenu_sendpage.js --- firefox-99.0.1+build1/browser/base/content/test/sync/browser_contextmenu_sendpage.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/sync/browser_contextmenu_sendpage.js 2022-04-26 05:44:41.000000000 +0000 @@ -27,7 +27,7 @@ { id: 4, name: "Homer" }, // Incompatible target. ]; -add_task(async function setup() { +add_setup(async function() { await promiseSyncReady(); await Services.search.init(); // gSync.init() is called in a requestIdleCallback. Force its initialization. diff -Nru firefox-99.0.1+build1/browser/base/content/test/sync/browser_contextmenu_sendtab.js firefox-100.0+build1/browser/base/content/test/sync/browser_contextmenu_sendtab.js --- firefox-99.0.1+build1/browser/base/content/test/sync/browser_contextmenu_sendtab.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/sync/browser_contextmenu_sendtab.js 2022-04-26 05:44:41.000000000 +0000 @@ -50,7 +50,7 @@ menu.hidePopup(); } -add_task(async function setup() { +add_setup(async function() { await promiseSyncReady(); await Services.search.init(); // gSync.init() is called in a requestIdleCallback. Force its initialization. diff -Nru firefox-99.0.1+build1/browser/base/content/test/sync/browser_fxa_web_channel.js firefox-100.0+build1/browser/base/content/test/sync/browser_fxa_web_channel.js --- firefox-99.0.1+build1/browser/base/content/test/sync/browser_fxa_web_channel.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/sync/browser_fxa_web_channel.js 2022-04-26 05:44:41.000000000 +0000 @@ -3,7 +3,7 @@ */ XPCOMUtils.defineLazyGetter(this, "FxAccountsCommon", function() { - return ChromeUtils.import("resource://gre/modules/FxAccountsCommon.js", {}); + return ChromeUtils.import("resource://gre/modules/FxAccountsCommon.js"); }); ChromeUtils.defineModuleGetter( @@ -12,11 +12,8 @@ "resource://gre/modules/WebChannel.jsm" ); -// FxAccountsWebChannel isn't explicitly exported by FxAccountsWebChannel.jsm -// but we can get it here via a backstage pass. var { FxAccountsWebChannel } = ChromeUtils.import( - "resource://gre/modules/FxAccountsWebChannel.jsm", - null + "resource://gre/modules/FxAccountsWebChannel.jsm" ); const TEST_HTTP_PATH = "http://example.com"; diff -Nru firefox-99.0.1+build1/browser/base/content/test/sync/browser_sync.js firefox-100.0+build1/browser/base/content/test/sync/browser_sync.js --- firefox-99.0.1+build1/browser/base/content/test/sync/browser_sync.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/sync/browser_sync.js 2022-04-26 05:44:41.000000000 +0000 @@ -9,7 +9,7 @@ let gCUITestUtils = new CustomizableUITestUtils(window); -add_task(async function setup() { +add_setup(async function() { // gSync.init() is called in a requestIdleCallback. Force its initialization. gSync.init(); // This preference gets set the very first time that the FxA menu gets opened, diff -Nru firefox-99.0.1+build1/browser/base/content/test/tabcrashed/browser_noPermanentKey.js firefox-100.0+build1/browser/base/content/test/tabcrashed/browser_noPermanentKey.js --- firefox-99.0.1+build1/browser/base/content/test/tabcrashed/browser_noPermanentKey.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/tabcrashed/browser_noPermanentKey.js 2022-04-26 05:44:41.000000000 +0000 @@ -3,7 +3,7 @@ const PAGE = "data:text/html,A%20regular,%20everyday,%20normal%20page."; -add_task(async function setup() { +add_setup(async function() { await setupLocalCrashReportServer(); }); diff -Nru firefox-99.0.1+build1/browser/base/content/test/tabcrashed/browser_shown.js firefox-100.0+build1/browser/base/content/test/tabcrashed/browser_shown.js --- firefox-99.0.1+build1/browser/base/content/test/tabcrashed/browser_shown.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/tabcrashed/browser_shown.js 2022-04-26 05:44:41.000000000 +0000 @@ -7,7 +7,7 @@ // Avoid timeouts, as in bug 1325530 requestLongerTimeout(2); -add_task(async function setup() { +add_setup(async function() { await setupLocalCrashReportServer(); }); diff -Nru firefox-99.0.1+build1/browser/base/content/test/tabcrashed/browser_withoutDump.js firefox-100.0+build1/browser/base/content/test/tabcrashed/browser_withoutDump.js --- firefox-99.0.1+build1/browser/base/content/test/tabcrashed/browser_withoutDump.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/tabcrashed/browser_withoutDump.js 2022-04-26 05:44:41.000000000 +0000 @@ -3,7 +3,7 @@ const PAGE = "data:text/html,A%20regular,%20everyday,%20normal%20page."; -add_task(async function setup() { +add_setup(async function() { prepareNoDump(); }); diff -Nru firefox-99.0.1+build1/browser/base/content/test/tabdialogs/browser_tabdialogbox_content_prompts.js firefox-100.0+build1/browser/base/content/test/tabdialogs/browser_tabdialogbox_content_prompts.js --- firefox-99.0.1+build1/browser/base/content/test/tabdialogs/browser_tabdialogbox_content_prompts.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/tabdialogs/browser_tabdialogbox_content_prompts.js 2022-04-26 05:44:41.000000000 +0000 @@ -41,7 +41,7 @@ ); // Setup. -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [[CONTENT_PROMPT_PREF, true]], }); diff -Nru firefox-99.0.1+build1/browser/base/content/test/tabPrompts/browser_confirmFolderUpload.js firefox-100.0+build1/browser/base/content/test/tabPrompts/browser_confirmFolderUpload.js --- firefox-99.0.1+build1/browser/base/content/test/tabPrompts/browser_confirmFolderUpload.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/tabPrompts/browser_confirmFolderUpload.js 2022-04-26 05:44:41.000000000 +0000 @@ -36,7 +36,7 @@ return tmpDir.path; } -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ // Allow using our MockFilePicker in the content process. diff -Nru firefox-99.0.1+build1/browser/base/content/test/tabPrompts/browser_contentOrigins.js firefox-100.0+build1/browser/base/content/test/tabPrompts/browser_contentOrigins.js --- firefox-99.0.1+build1/browser/base/content/test/tabPrompts/browser_contentOrigins.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/tabPrompts/browser_contentOrigins.js 2022-04-26 05:44:41.000000000 +0000 @@ -125,7 +125,7 @@ }); } -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ ["prompts.contentPromptSubDialog", true], diff -Nru firefox-99.0.1+build1/browser/base/content/test/tabs/browser_adoptTab_failure.js firefox-100.0+build1/browser/base/content/test/tabs/browser_adoptTab_failure.js --- firefox-99.0.1+build1/browser/base/content/test/tabs/browser_adoptTab_failure.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/tabs/browser_adoptTab_failure.js 2022-04-26 05:44:41.000000000 +0000 @@ -0,0 +1,81 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// 'adoptTab' aborts when swapBrowsersAndCloseOther returns false. +// That's usually a bug, but this function forces it to happen in order to check +// that callers will behave as good as possible when it happens accidentally. +function makeAdoptTabFailOnceFor(gBrowser, tab) { + const original = gBrowser.swapBrowsersAndCloseOther; + gBrowser.swapBrowsersAndCloseOther = function(aOurTab, aOtherTab) { + if (tab !== aOtherTab) { + return original.call(gBrowser, aOurTab, aOtherTab); + } + gBrowser.swapBrowsersAndCloseOther = original; + return false; + }; +} + +add_task(async function test_adoptTab() { + const tab = await addTab(); + const win2 = await BrowserTestUtils.openNewBrowserWindow(); + const gBrowser2 = win2.gBrowser; + + makeAdoptTabFailOnceFor(gBrowser2, tab); + is(gBrowser2.adoptTab(tab), null, "adoptTab returns null in case of failure"); + ok(gBrowser2.adoptTab(tab), "adoptTab returns new tab in case of success"); + + await BrowserTestUtils.closeWindow(win2); +}); + +add_task(async function test_replaceTabsWithWindow() { + const nonAdoptableTab = await addTab("data:text/plain,nonAdoptableTab"); + const auxiliaryTab = await addTab("data:text/plain,auxiliaryTab"); + const selectedTab = await addTab("data:text/plain,selectedTab"); + gBrowser.selectedTabs = [selectedTab, nonAdoptableTab, auxiliaryTab]; + + const windowOpenedPromise = BrowserTestUtils.waitForNewWindow(); + const win2 = gBrowser.replaceTabsWithWindow(selectedTab); + await BrowserTestUtils.waitForEvent(win2, "DOMContentLoaded"); + const gBrowser2 = win2.gBrowser; + makeAdoptTabFailOnceFor(gBrowser2, nonAdoptableTab); + await windowOpenedPromise; + + // nonAdoptableTab couldn't be adopted, but the new window should have adopted + // the other 2 tabs, and they should be in the proper order. + is(gBrowser2.tabs.length, 2); + is(gBrowser2.tabs[0].label, "data:text/plain,auxiliaryTab"); + is(gBrowser2.tabs[1].label, "data:text/plain,selectedTab"); + + gBrowser.removeTab(nonAdoptableTab); + await BrowserTestUtils.closeWindow(win2); +}); + +add_task(async function test_on_drop() { + const nonAdoptableTab = await addTab("data:text/html,nonAdoptableTab"); + const auxiliaryTab = await addTab("data:text/html,<title>auxiliaryTab"); + const selectedTab = await addTab("data:text/html,<title>selectedTab"); + gBrowser.selectedTabs = [selectedTab, nonAdoptableTab, auxiliaryTab]; + + const win2 = await BrowserTestUtils.openNewBrowserWindow(); + const gBrowser2 = win2.gBrowser; + makeAdoptTabFailOnceFor(gBrowser2, nonAdoptableTab); + const initialTab = gBrowser2.tabs[0]; + await dragAndDrop(selectedTab, initialTab, false, win2, false); + + // nonAdoptableTab couldn't be adopted, but the new window should have adopted + // the other 2 tabs, and they should be in the right position. + is(gBrowser2.tabs.length, 3, "There are 3 tabs"); + is(gBrowser2.tabs[0].label, "auxiliaryTab", "auxiliaryTab became tab 0"); + is(gBrowser2.tabs[1].label, "selectedTab", "selectedTab became tab 1"); + is(gBrowser2.tabs[2], initialTab, "initialTab became tab 2"); + is(gBrowser2.selectedTab, gBrowser2.tabs[1], "Tab 1 is selected"); + is(gBrowser2.multiSelectedTabsCount, 2, "Three multiselected tabs"); + ok(gBrowser2.tabs[0].multiselected, "Tab 0 is multiselected"); + ok(gBrowser2.tabs[1].multiselected, "Tab 1 is multiselected"); + ok(!gBrowser2.tabs[2].multiselected, "Tab 2 is not multiselected"); + + gBrowser.removeTab(nonAdoptableTab); + await BrowserTestUtils.closeWindow(win2); +}); diff -Nru firefox-99.0.1+build1/browser/base/content/test/tabs/browser.ini firefox-100.0+build1/browser/base/content/test/tabs/browser.ini --- firefox-99.0.1+build1/browser/base/content/test/tabs/browser.ini 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/tabs/browser.ini 2022-04-26 05:44:41.000000000 +0000 @@ -12,6 +12,7 @@ skip-if = (verify && debug && (os == 'linux')) || (os == 'win' && processor == 'aarch64') [browser_addAdjacentNewTab.js] [browser_addTab_index.js] +[browser_adoptTab_failure.js] [browser_allow_process_switches_despite_related_browser.js] [browser_audioTabIcon.js] tags = audiochannel diff -Nru firefox-99.0.1+build1/browser/base/content/test/tabs/browser_multiselect_tabs_move_to_another_window_drag.js firefox-100.0+build1/browser/base/content/test/tabs/browser_multiselect_tabs_move_to_another_window_drag.js --- firefox-99.0.1+build1/browser/base/content/test/tabs/browser_multiselect_tabs_move_to_another_window_drag.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/tabs/browser_multiselect_tabs_move_to_another_window_drag.js 2022-04-26 05:44:41.000000000 +0000 @@ -72,3 +72,47 @@ BrowserTestUtils.closeWindow(newWindow); BrowserTestUtils.removeTab(tab4); }); + +add_task(async function test_laziness() { + const params = { createLazyBrowser: true }; + const url = "http://mochi.test:8888/?"; + const tab1 = BrowserTestUtils.addTab(gBrowser, url + "1", params); + const tab2 = BrowserTestUtils.addTab(gBrowser, url + "2"); + const tab3 = BrowserTestUtils.addTab(gBrowser, url + "3", params); + + await BrowserTestUtils.switchTab(gBrowser, tab2); + await triggerClickOn(tab1, { ctrlKey: true }); + await triggerClickOn(tab3, { ctrlKey: true }); + + is(gBrowser.selectedTab, tab2, "Tab2 is selected"); + is(gBrowser.multiSelectedTabsCount, 3, "Three multiselected tabs"); + ok(tab1.multiselected, "Tab1 is multiselected"); + ok(tab2.multiselected, "Tab2 is multiselected"); + ok(tab3.multiselected, "Tab3 is multiselected"); + ok(!tab1.linkedPanel, "Tab1 is lazy"); + ok(tab2.linkedPanel, "Tab2 is not lazy"); + ok(!tab3.linkedPanel, "Tab3 is lazy"); + + const win2 = await BrowserTestUtils.openNewBrowserWindow(); + const gBrowser2 = win2.gBrowser; + is(gBrowser2.tabs.length, 1, "Second window has 1 tab"); + + await dragAndDrop(tab2, gBrowser2.tabs[0], false, win2); + await TestUtils.waitForCondition( + () => gBrowser2.tabs.length == 4, + "Moved tabs into second window" + ); + is(gBrowser2.tabs[1].linkedBrowser.currentURI.spec, url + "1"); + is(gBrowser2.tabs[2].linkedBrowser.currentURI.spec, url + "2"); + is(gBrowser2.tabs[3].linkedBrowser.currentURI.spec, url + "3"); + is(gBrowser2.selectedTab, gBrowser2.tabs[2], "Tab2 is selected"); + is(gBrowser2.multiSelectedTabsCount, 3, "Three multiselected tabs"); + ok(gBrowser2.tabs[1].multiselected, "Tab1 is multiselected"); + ok(gBrowser2.tabs[2].multiselected, "Tab2 is multiselected"); + ok(gBrowser2.tabs[3].multiselected, "Tab3 is multiselected"); + ok(!gBrowser2.tabs[1].linkedPanel, "Tab1 is lazy"); + ok(gBrowser2.tabs[2].linkedPanel, "Tab2 is not lazy"); + ok(!gBrowser2.tabs[3].linkedPanel, "Tab3 is lazy"); + + await BrowserTestUtils.closeWindow(win2); +}); diff -Nru firefox-99.0.1+build1/browser/base/content/test/tabs/browser_multiselect_tabs_using_keyboard.js firefox-100.0+build1/browser/base/content/test/tabs/browser_multiselect_tabs_using_keyboard.js --- firefox-99.0.1+build1/browser/base/content/test/tabs/browser_multiselect_tabs_using_keyboard.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/tabs/browser_multiselect_tabs_using_keyboard.js 2022-04-26 05:44:41.000000000 +0000 @@ -15,7 +15,7 @@ return focused; } -add_task(async function setup() { +add_setup(async function() { // The DevEdition has the DevTools button in the toolbar by default. Remove it // to prevent branch-specific rules what button should be focused. CustomizableUI.removeWidgetFromArea("developer-button"); diff -Nru firefox-99.0.1+build1/browser/base/content/test/tabs/browser_navigate_through_urls_origin_attributes.js firefox-100.0+build1/browser/base/content/test/tabs/browser_navigate_through_urls_origin_attributes.js --- firefox-99.0.1+build1/browser/base/content/test/tabs/browser_navigate_through_urls_origin_attributes.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/tabs/browser_navigate_through_urls_origin_attributes.js 2022-04-26 05:44:41.000000000 +0000 @@ -33,7 +33,7 @@ var gPrevRemoteTypeContainerTab; var gPrevRemoteTypePrivateTab; -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ ["privacy.userContext.enabled", true], diff -Nru firefox-99.0.1+build1/browser/base/content/test/tabs/browser_new_tab_in_privilegedabout_process_pref.js firefox-100.0+build1/browser/base/content/test/tabs/browser_new_tab_in_privilegedabout_process_pref.js --- firefox-99.0.1+build1/browser/base/content/test/tabs/browser_new_tab_in_privilegedabout_process_pref.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/tabs/browser_new_tab_in_privilegedabout_process_pref.js 2022-04-26 05:44:41.000000000 +0000 @@ -17,7 +17,7 @@ const ABOUT_WELCOME = "about:welcome"; const TEST_HTTP = "http://example.org/"; -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ ["browser.newtab.preload", false], diff -Nru firefox-99.0.1+build1/browser/base/content/test/tabs/browser_origin_attrs_in_remote_type.js firefox-100.0+build1/browser/base/content/test/tabs/browser_origin_attrs_in_remote_type.js --- firefox-99.0.1+build1/browser/base/content/test/tabs/browser_origin_attrs_in_remote_type.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/tabs/browser_origin_attrs_in_remote_type.js 2022-04-26 05:44:41.000000000 +0000 @@ -21,7 +21,7 @@ const NUM_PAGES_OPEN_FOR_EACH_TEST_CASE = 5; var remoteTypes; -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ ["privacy.userContext.enabled", true], diff -Nru firefox-99.0.1+build1/browser/base/content/test/tabs/browser_origin_attrs_rel.js firefox-100.0+build1/browser/base/content/test/tabs/browser_origin_attrs_rel.js --- firefox-99.0.1+build1/browser/base/content/test/tabs/browser_origin_attrs_rel.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/tabs/browser_origin_attrs_rel.js 2022-04-26 05:44:41.000000000 +0000 @@ -49,7 +49,7 @@ } } -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ ["privacy.userContext.enabled", true], diff -Nru firefox-99.0.1+build1/browser/base/content/test/tabs/browser_positional_attributes.js firefox-100.0+build1/browser/base/content/test/tabs/browser_positional_attributes.js --- firefox-99.0.1+build1/browser/base/content/test/tabs/browser_positional_attributes.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/tabs/browser_positional_attributes.js 2022-04-26 05:44:41.000000000 +0000 @@ -26,7 +26,7 @@ ); } -add_task(async function setup() { +add_setup(async function() { is(gBrowser.tabs.length, 1, "one tab is open initially"); addTab("http://mochi.test:8888/#0"); diff -Nru firefox-99.0.1+build1/browser/base/content/test/tabs/browser_privilegedmozilla_process_pref.js firefox-100.0+build1/browser/base/content/test/tabs/browser_privilegedmozilla_process_pref.js --- firefox-99.0.1+build1/browser/base/content/test/tabs/browser_privilegedmozilla_process_pref.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/tabs/browser_privilegedmozilla_process_pref.js 2022-04-26 05:44:41.000000000 +0000 @@ -17,7 +17,7 @@ const TEST_LOW1 = "http://example.org/"; const TEST_LOW2 = "https://example.com/"; -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ ["browser.tabs.remote.separatePrivilegedMozillaWebContentProcess", true], diff -Nru firefox-99.0.1+build1/browser/base/content/test/tabs/browser_progress_keyword_search_handling.js firefox-100.0+build1/browser/base/content/test/tabs/browser_progress_keyword_search_handling.js --- firefox-99.0.1+build1/browser/base/content/test/tabs/browser_progress_keyword_search_handling.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/tabs/browser_progress_keyword_search_handling.js 2022-04-26 05:44:41.000000000 +0000 @@ -11,7 +11,7 @@ const kButton = document.getElementById("reload-button"); -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [["browser.fixup.dns_first_for_single_words", true]], }); diff -Nru firefox-99.0.1+build1/browser/base/content/test/tabs/browser_removeTabs_order.js firefox-100.0+build1/browser/base/content/test/tabs/browser_removeTabs_order.js --- firefox-99.0.1+build1/browser/base/content/test/tabs/browser_removeTabs_order.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/tabs/browser_removeTabs_order.js 2022-04-26 05:44:41.000000000 +0000 @@ -5,7 +5,7 @@ const { sinon } = ChromeUtils.import("resource://testing-common/Sinon.jsm"); -add_task(async function setup() { +add_task(async function() { let tab1 = await addTab(); let tab2 = await addTab(); let tab3 = await addTab(); diff -Nru firefox-99.0.1+build1/browser/base/content/test/tabs/browser_removeTabs_skipPermitUnload.js firefox-100.0+build1/browser/base/content/test/tabs/browser_removeTabs_skipPermitUnload.js --- firefox-99.0.1+build1/browser/base/content/test/tabs/browser_removeTabs_skipPermitUnload.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/tabs/browser_removeTabs_skipPermitUnload.js 2022-04-26 05:44:41.000000000 +0000 @@ -38,7 +38,7 @@ let nonBeforeUnloadTab; let beforeUnloadTab; -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [["dom.require_user_interaction_for_beforeunload", false]], }); diff -Nru firefox-99.0.1+build1/browser/base/content/test/tabs/browser_tabCloseProbes.js firefox-100.0+build1/browser/base/content/test/tabs/browser_tabCloseProbes.js --- firefox-99.0.1+build1/browser/base/content/test/tabs/browser_tabCloseProbes.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/tabs/browser_tabCloseProbes.js 2022-04-26 05:44:41.000000000 +0000 @@ -58,7 +58,7 @@ }, `Collected value should become ${expectedCount}.`); } -add_task(async function setup() { +add_setup(async function() { // Force-enable tab animations gReduceMotionOverride = false; diff -Nru firefox-99.0.1+build1/browser/base/content/test/tabs/browser_tabSpinnerProbe.js firefox-100.0+build1/browser/base/content/test/tabs/browser_tabSpinnerProbe.js --- firefox-99.0.1+build1/browser/base/content/test/tabs/browser_tabSpinnerProbe.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/tabs/browser_tabSpinnerProbe.js 2022-04-26 05:44:41.000000000 +0000 @@ -82,7 +82,7 @@ ); } -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ ["dom.ipc.processCount", 1], diff -Nru firefox-99.0.1+build1/browser/base/content/test/tabs/head.js firefox-100.0+build1/browser/base/content/test/tabs/head.js --- firefox-99.0.1+build1/browser/base/content/test/tabs/head.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/tabs/head.js 2022-04-26 05:44:41.000000000 +0000 @@ -243,12 +243,18 @@ return mutedPromise; } -async function dragAndDrop(tab1, tab2, copy, destWindow = window) { +async function dragAndDrop( + tab1, + tab2, + copy, + destWindow = window, + afterTab = true +) { let rect = tab2.getBoundingClientRect(); let event = { ctrlKey: copy, altKey: copy, - clientX: rect.left + rect.width / 2 + 10, + clientX: rect.left + rect.width / 2 + 10 * (afterTab ? 1 : -1), clientY: rect.top + rect.height / 2, }; diff -Nru firefox-99.0.1+build1/browser/base/content/test/touch/browser_menu_touch.js firefox-100.0+build1/browser/base/content/test/touch/browser_menu_touch.js --- firefox-99.0.1+build1/browser/base/content/test/touch/browser_menu_touch.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/touch/browser_menu_touch.js 2022-04-26 05:44:41.000000000 +0000 @@ -112,7 +112,7 @@ } // Ensure that we can run touch events properly for windows [10] -add_task(async function setup() { +add_setup(async function() { let isWindows = AppConstants.isPlatformAndVersionAtLeast("win", "10.0"); await SpecialPowers.pushPrefEnv({ set: [["apz.test.fails_with_native_injection", isWindows]], diff -Nru firefox-99.0.1+build1/browser/base/content/test/webextensions/browser_extension_update_background.js firefox-100.0+build1/browser/base/content/test/webextensions/browser_extension_update_background.js --- firefox-99.0.1+build1/browser/base/content/test/webextensions/browser_extension_update_background.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/webextensions/browser_extension_update_background.js 2022-04-26 05:44:41.000000000 +0000 @@ -35,7 +35,7 @@ } // Set some prefs that apply to all the tests in this file -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ // We don't have pre-pinned certificates for the local mochitest server diff -Nru firefox-99.0.1+build1/browser/base/content/test/webextensions/browser_extension_update_background_noprompt.js firefox-100.0+build1/browser/base/content/test/webextensions/browser_extension_update_background_noprompt.js --- firefox-99.0.1+build1/browser/base/content/test/webextensions/browser_extension_update_background_noprompt.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/webextensions/browser_extension_update_background_noprompt.js 2022-04-26 05:44:41.000000000 +0000 @@ -3,8 +3,7 @@ ); const { AddonTestUtils } = ChromeUtils.import( - "resource://testing-common/AddonTestUtils.jsm", - {} + "resource://testing-common/AddonTestUtils.jsm" ); AddonTestUtils.initMochitest(this); @@ -21,7 +20,7 @@ } // Set some prefs that apply to all the tests in this file -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ // We don't have pre-pinned certificates for the local mochitest server diff -Nru firefox-99.0.1+build1/browser/base/content/test/webextensions/browser_permissions_dismiss.js firefox-100.0+build1/browser/base/content/test/webextensions/browser_permissions_dismiss.js --- firefox-99.0.1+build1/browser/base/content/test/webextensions/browser_permissions_dismiss.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/webextensions/browser_permissions_dismiss.js 2022-04-26 05:44:41.000000000 +0000 @@ -3,7 +3,7 @@ const INSTALL_PAGE = `${BASE}/file_install_extensions.html`; const INSTALL_XPI = `${BASE}/browser_webext_permissions.xpi`; -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ ["extensions.webapi.testing", true], diff -Nru firefox-99.0.1+build1/browser/base/content/test/webextensions/browser_permissions_installTrigger.js firefox-100.0+build1/browser/base/content/test/webextensions/browser_permissions_installTrigger.js --- firefox-99.0.1+build1/browser/base/content/test/webextensions/browser_permissions_installTrigger.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/webextensions/browser_permissions_installTrigger.js 2022-04-26 05:44:41.000000000 +0000 @@ -3,6 +3,14 @@ const INSTALL_PAGE = `${BASE}/file_install_extensions.html`; async function installTrigger(filename) { + await SpecialPowers.pushPrefEnv({ + set: [ + ["extensions.InstallTrigger.enabled", true], + ["extensions.InstallTriggerImpl.enabled", true], + // Relax the user input requirements while running this test. + ["xpinstall.userActivation.required", false], + ], + }); BrowserTestUtils.loadURI(gBrowser.selectedBrowser, INSTALL_PAGE); await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); diff -Nru firefox-99.0.1+build1/browser/base/content/test/webextensions/browser_permissions_unsigned.js firefox-100.0+build1/browser/base/content/test/webextensions/browser_permissions_unsigned.js --- firefox-99.0.1+build1/browser/base/content/test/webextensions/browser_permissions_unsigned.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/webextensions/browser_permissions_unsigned.js 2022-04-26 05:44:41.000000000 +0000 @@ -8,6 +8,10 @@ set: [ ["extensions.webapi.testing", true], ["extensions.install.requireBuiltInCerts", false], + ["extensions.InstallTrigger.enabled", true], + ["extensions.InstallTriggerImpl.enabled", true], + // Relax the user input requirements while running this test. + ["xpinstall.userActivation.required", false], ], }); diff -Nru firefox-99.0.1+build1/browser/base/content/test/webextensions/browser_update_interactive_noprompt.js firefox-100.0+build1/browser/base/content/test/webextensions/browser_update_interactive_noprompt.js --- firefox-99.0.1+build1/browser/base/content/test/webextensions/browser_update_interactive_noprompt.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/webextensions/browser_update_interactive_noprompt.js 2022-04-26 05:44:41.000000000 +0000 @@ -1,5 +1,5 @@ // Set some prefs that apply to all the tests in this file -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ // We don't have pre-pinned certificates for the local mochitest server diff -Nru firefox-99.0.1+build1/browser/base/content/test/webextensions/head.js firefox-100.0+build1/browser/base/content/test/webextensions/head.js --- firefox-99.0.1+build1/browser/base/content/test/webextensions/head.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/webextensions/head.js 2022-04-26 05:44:41.000000000 +0000 @@ -648,7 +648,7 @@ // Individual tests can store a cleanup function in the testCleanup global // to ensure it gets called before the final check is performed. let testCleanup; -add_task(async function() { +add_setup(async function head_setup() { let addons = await AddonManager.getAllAddons(); let existingAddons = new Set(addons.map(a => a.id)); diff -Nru firefox-99.0.1+build1/browser/base/content/test/webrtc/browser_global_mute_toggles.js firefox-100.0+build1/browser/base/content/test/webrtc/browser_global_mute_toggles.js --- firefox-99.0.1+build1/browser/base/content/test/webrtc/browser_global_mute_toggles.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/webrtc/browser_global_mute_toggles.js 2022-04-26 05:44:41.000000000 +0000 @@ -15,7 +15,7 @@ "getUserMedia:unmuteAudio", ]; -add_task(async function setup() { +add_setup(async function() { let prefs = [ [PREF_PERMISSION_FAKE, true], [PREF_AUDIO_LOOPBACK, ""], diff -Nru firefox-99.0.1+build1/browser/base/content/test/webrtc/browser.ini firefox-100.0+build1/browser/base/content/test/webrtc/browser.ini --- firefox-99.0.1+build1/browser/base/content/test/webrtc/browser.ini 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/webrtc/browser.ini 2022-04-26 05:44:41.000000000 +0000 @@ -70,6 +70,8 @@ apple_catalina # platform migration apple_silicon # bug 1707735 [browser_devices_get_user_media_unprompted_access.js] +skip-if = + os == "linux" && bits == 64 && !debug # Bug 1712012 https_first_disabled = true [browser_devices_get_user_media_unprompted_access_in_frame.js] https_first_disabled = true diff -Nru firefox-99.0.1+build1/browser/base/content/test/webrtc/browser_notification_silencing.js firefox-100.0+build1/browser/base/content/test/webrtc/browser_notification_silencing.js --- firefox-99.0.1+build1/browser/base/content/test/webrtc/browser_notification_silencing.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/webrtc/browser_notification_silencing.js 2022-04-26 05:44:41.000000000 +0000 @@ -129,7 +129,7 @@ ); } -add_task(async function setup() { +add_setup(async function() { // Set prefs so that permissions prompts are shown and loopback devices // are not used. To test the chrome we want prompts to be shown, and // these tests are flakey when using loopback devices (though it would diff -Nru firefox-99.0.1+build1/browser/base/content/test/webrtc/browser_stop_sharing_button.js firefox-100.0+build1/browser/base/content/test/webrtc/browser_stop_sharing_button.js --- firefox-99.0.1+build1/browser/base/content/test/webrtc/browser_stop_sharing_button.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/webrtc/browser_stop_sharing_button.js 2022-04-26 05:44:41.000000000 +0000 @@ -9,7 +9,7 @@ ); const TEST_PAGE = TEST_ROOT + "get_user_media.html"; -add_task(async function setup() { +add_setup(async function() { let prefs = [ [PREF_PERMISSION_FAKE, true], [PREF_AUDIO_LOOPBACK, ""], diff -Nru firefox-99.0.1+build1/browser/base/content/test/webrtc/browser_stop_streams_on_indicator_close.js firefox-100.0+build1/browser/base/content/test/webrtc/browser_stop_streams_on_indicator_close.js --- firefox-99.0.1+build1/browser/base/content/test/webrtc/browser_stop_streams_on_indicator_close.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/webrtc/browser_stop_streams_on_indicator_close.js 2022-04-26 05:44:41.000000000 +0000 @@ -9,7 +9,7 @@ ); const TEST_PAGE = TEST_ROOT + "get_user_media.html"; -add_task(async function setup() { +add_setup(async function() { let prefs = [ [PREF_PERMISSION_FAKE, true], [PREF_AUDIO_LOOPBACK, ""], diff -Nru firefox-99.0.1+build1/browser/base/content/test/webrtc/browser_tab_switch_warning.js firefox-100.0+build1/browser/base/content/test/webrtc/browser_tab_switch_warning.js --- firefox-99.0.1+build1/browser/base/content/test/webrtc/browser_tab_switch_warning.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/webrtc/browser_tab_switch_warning.js 2022-04-26 05:44:41.000000000 +0000 @@ -188,7 +188,7 @@ ); } -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [["privacy.webrtc.sharedTabWarning", true]], }); diff -Nru firefox-99.0.1+build1/browser/base/content/test/webrtc/head.js firefox-100.0+build1/browser/base/content/test/webrtc/head.js --- firefox-99.0.1+build1/browser/base/content/test/webrtc/head.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/test/webrtc/head.js 2022-04-26 05:44:41.000000000 +0000 @@ -104,7 +104,7 @@ } async function assertWebRTCIndicatorStatus(expected) { - let ui = ChromeUtils.import("resource:///modules/webrtcUI.jsm", {}).webrtcUI; + let ui = ChromeUtils.import("resource:///modules/webrtcUI.jsm").webrtcUI; let expectedState = expected ? "visible" : "hidden"; let msg = "WebRTC indicator " + expectedState; if (!expected && ui.showGlobalIndicator) { diff -Nru firefox-99.0.1+build1/browser/base/content/upgradeDialog.js firefox-100.0+build1/browser/base/content/upgradeDialog.js --- firefox-99.0.1+build1/browser/base/content/upgradeDialog.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/content/upgradeDialog.js 2022-04-26 05:44:41.000000000 +0000 @@ -212,7 +212,7 @@ 1; const enableVariant = () => enableTheme( - THEME_IDS[variations.getAttribute("next")][getVariantIndex()] + THEME_IDS[variations.getAttribute("next") ?? 0][getVariantIndex()] ); // Prepare random theme selection that's not (first) default. diff -Nru firefox-99.0.1+build1/browser/base/jar.mn firefox-100.0+build1/browser/base/jar.mn --- firefox-99.0.1+build1/browser/base/jar.mn 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/base/jar.mn 2022-04-26 05:44:41.000000000 +0000 @@ -34,7 +34,7 @@ content/browser/aboutTabCrashed.css (content/aboutTabCrashed.css) content/browser/aboutTabCrashed.js (content/aboutTabCrashed.js) content/browser/aboutTabCrashed.xhtml (content/aboutTabCrashed.xhtml) -* content/browser/browser.css (content/browser.css) + content/browser/browser.css (content/browser.css) content/browser/browser.js (content/browser.js) * content/browser/browser.xhtml (content/browser.xhtml) content/browser/browser-a11yUtils.js (content/browser-a11yUtils.js) Binary files /tmp/tmpjuoo3qa3/xzt4d8M_TV/firefox-99.0.1+build1/browser/branding/aurora/document_pdf.ico and /tmp/tmpjuoo3qa3/995CmTgYpO/firefox-100.0+build1/browser/branding/aurora/document_pdf.ico differ diff -Nru firefox-99.0.1+build1/browser/branding/aurora/locales/en-US/brand.properties firefox-100.0+build1/browser/branding/aurora/locales/en-US/brand.properties --- firefox-99.0.1+build1/browser/branding/aurora/locales/en-US/brand.properties 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/branding/aurora/locales/en-US/brand.properties 2022-04-26 05:44:41.000000000 +0000 @@ -5,4 +5,3 @@ brandShorterName=Firefox brandShortName=Firefox Developer Edition brandFullName=Firefox Developer Edition -vendorShortName=Mozilla Binary files /tmp/tmpjuoo3qa3/xzt4d8M_TV/firefox-99.0.1+build1/browser/branding/nightly/document_pdf.ico and /tmp/tmpjuoo3qa3/995CmTgYpO/firefox-100.0+build1/browser/branding/nightly/document_pdf.ico differ diff -Nru firefox-99.0.1+build1/browser/branding/nightly/locales/en-US/brand.properties firefox-100.0+build1/browser/branding/nightly/locales/en-US/brand.properties --- firefox-99.0.1+build1/browser/branding/nightly/locales/en-US/brand.properties 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/branding/nightly/locales/en-US/brand.properties 2022-04-26 05:44:41.000000000 +0000 @@ -5,4 +5,3 @@ brandShorterName=Nightly brandShortName=Nightly brandFullName=Firefox Nightly -vendorShortName=Mozilla Binary files /tmp/tmpjuoo3qa3/xzt4d8M_TV/firefox-99.0.1+build1/browser/branding/official/document_pdf.ico and /tmp/tmpjuoo3qa3/995CmTgYpO/firefox-100.0+build1/browser/branding/official/document_pdf.ico differ diff -Nru firefox-99.0.1+build1/browser/branding/official/locales/en-US/brand.properties firefox-100.0+build1/browser/branding/official/locales/en-US/brand.properties --- firefox-99.0.1+build1/browser/branding/official/locales/en-US/brand.properties 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/branding/official/locales/en-US/brand.properties 2022-04-26 05:44:41.000000000 +0000 @@ -5,4 +5,3 @@ brandShorterName=Firefox brandShortName=Firefox brandFullName=Mozilla Firefox -vendorShortName=Mozilla Binary files /tmp/tmpjuoo3qa3/xzt4d8M_TV/firefox-99.0.1+build1/browser/branding/official/msix/Assets/LargeTile.scale-200.png and /tmp/tmpjuoo3qa3/995CmTgYpO/firefox-100.0+build1/browser/branding/official/msix/Assets/LargeTile.scale-200.png differ Binary files /tmp/tmpjuoo3qa3/xzt4d8M_TV/firefox-99.0.1+build1/browser/branding/official/msix/Assets/Square150x150Logo.scale-200.png and /tmp/tmpjuoo3qa3/995CmTgYpO/firefox-100.0+build1/browser/branding/official/msix/Assets/Square150x150Logo.scale-200.png differ Binary files /tmp/tmpjuoo3qa3/xzt4d8M_TV/firefox-99.0.1+build1/browser/branding/unofficial/document_pdf.ico and /tmp/tmpjuoo3qa3/995CmTgYpO/firefox-100.0+build1/browser/branding/unofficial/document_pdf.ico differ diff -Nru firefox-99.0.1+build1/browser/branding/unofficial/locales/en-US/brand.properties firefox-100.0+build1/browser/branding/unofficial/locales/en-US/brand.properties --- firefox-99.0.1+build1/browser/branding/unofficial/locales/en-US/brand.properties 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/branding/unofficial/locales/en-US/brand.properties 2022-04-26 05:44:41.000000000 +0000 @@ -5,4 +5,3 @@ brandShorterName=Nightly brandShortName=Nightly brandFullName=Nightly -vendorShortName=Mozilla diff -Nru firefox-99.0.1+build1/browser/components/about/AboutRedirector.cpp firefox-100.0+build1/browser/components/about/AboutRedirector.cpp --- firefox-99.0.1+build1/browser/components/about/AboutRedirector.cpp 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/about/AboutRedirector.cpp 2022-04-26 05:44:41.000000000 +0000 @@ -73,9 +73,9 @@ nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS | nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | nsIAboutModule::IS_SECURE_CHROME_UI}, - {"tabcrashed", "chrome://browser/content/aboutTabCrashed.xhtml", - nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | - nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT}, + {"myfirefox", "chrome://browser/content/myfirefox.html", + nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI | + nsIAboutModule::HIDE_FROM_ABOUTABOUT}, {"policies", "chrome://browser/content/policies/aboutPolicies.html", nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI}, {"privatebrowsing", "chrome://browser/content/aboutPrivateBrowsing.html", @@ -94,6 +94,9 @@ {"sessionrestore", "chrome://browser/content/aboutSessionRestore.xhtml", nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT | nsIAboutModule::IS_SECURE_CHROME_UI}, + {"tabcrashed", "chrome://browser/content/aboutTabCrashed.xhtml", + nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | + nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT}, {"welcomeback", "chrome://browser/content/aboutWelcomeBack.xhtml", nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT | nsIAboutModule::IS_SECURE_CHROME_UI}, diff -Nru firefox-99.0.1+build1/browser/components/about/components.conf firefox-100.0+build1/browser/components/about/components.conf --- firefox-99.0.1+build1/browser/components/about/components.conf 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/about/components.conf 2022-04-26 05:44:41.000000000 +0000 @@ -12,6 +12,7 @@ 'home', 'logins', 'loginsimportreport', + 'myfirefox', 'newtab', 'ion', 'pocket-home', diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/AboutLoginsChild.jsm firefox-100.0+build1/browser/components/aboutlogins/AboutLoginsChild.jsm --- firefox-99.0.1+build1/browser/components/aboutlogins/AboutLoginsChild.jsm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/AboutLoginsChild.jsm 2022-04-26 05:44:41.000000000 +0000 @@ -14,12 +14,6 @@ ); const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); -ChromeUtils.defineModuleGetter( - this, - "AppConstants", - "resource://gre/modules/AppConstants.jsm" -); - XPCOMUtils.defineLazyServiceGetter( this, "ClipboardHelper", @@ -55,81 +49,75 @@ handleEvent(event) { switch (event.type) { case "AboutLoginsInit": { - this.onAboutLoginsInit(); + this.#aboutLoginsInit(); break; } case "AboutLoginsImportReportInit": { - this.onAboutLoginsImportReportInit(); + this.#aboutLoginsImportReportInit(); break; } case "AboutLoginsCopyLoginDetail": { - this.onAboutLoginsCopyLoginDetail(event.detail); + this.#aboutLoginsCopyLoginDetail(event.detail); break; } case "AboutLoginsCreateLogin": { - this.onAboutLoginsCreateLogin(event.detail); + this.#aboutLoginsCreateLogin(event.detail); break; } case "AboutLoginsDeleteLogin": { - this.onAboutLoginsDeleteLogin(event.detail); + this.#aboutLoginsDeleteLogin(event.detail); break; } case "AboutLoginsExportPasswords": { - this.onAboutLoginsExportPasswords(); + this.#aboutLoginsExportPasswords(); break; } case "AboutLoginsGetHelp": { - this.onAboutLoginsGetHelp(); + this.#aboutLoginsGetHelp(); break; } case "AboutLoginsImportFromBrowser": { - this.onAboutLoginsImportFromBrowser(); + this.#aboutLoginsImportFromBrowser(); break; } case "AboutLoginsImportFromFile": { - this.onAboutLoginsImportFromFile(); + this.#aboutLoginsImportFromFile(); break; } case "AboutLoginsOpenPreferences": { - this.onAboutLoginsOpenPreferences(); + this.#aboutLoginsOpenPreferences(); break; } case "AboutLoginsRecordTelemetryEvent": { - this.onAboutLoginsRecordTelemetryEvent(event); + this.#aboutLoginsRecordTelemetryEvent(event); break; } case "AboutLoginsRemoveAllLogins": { - this.onAboutLoginsRemoveAllLogins(); + this.#aboutLoginsRemoveAllLogins(); break; } case "AboutLoginsSortChanged": { - this.onAboutLoginsSortChanged(event.detail); + this.#aboutLoginsSortChanged(event.detail); break; } case "AboutLoginsSyncEnable": { - this.onAboutLoginsSyncEnable(); + this.#aboutLoginsSyncEnable(); break; } case "AboutLoginsSyncOptions": { - this.onAboutLoginsSyncOptions(); + this.#aboutLoginsSyncOptions(); break; } case "AboutLoginsUpdateLogin": { - this.onAboutLoginsUpdateLogin(event.detail); + this.#aboutLoginsUpdateLogin(event.detail); break; } } } - onAboutLoginsInit() { + #aboutLoginsInit() { this.sendAsyncMessage("AboutLogins:Subscribe"); - let documentElement = this.document.documentElement; - documentElement.classList.toggle( - "official-branding", - AppConstants.MOZILLA_OFFICIAL - ); - let win = this.browsingContext.window; let waivedContent = Cu.waiveXrays(win); let that = this; @@ -175,40 +163,35 @@ ); } - onAboutLoginsImportReportInit() { + #aboutLoginsImportReportInit() { this.sendAsyncMessage("AboutLogins:ImportReportInit"); - let documentElement = this.document.documentElement; - documentElement.classList.toggle( - "official-branding", - AppConstants.MOZILLA_OFFICIAL - ); } - onAboutLoginsCopyLoginDetail(detail) { + #aboutLoginsCopyLoginDetail(detail) { ClipboardHelper.copyString(detail, ClipboardHelper.Sensitive); } - onAboutLoginsCreateLogin(login) { + #aboutLoginsCreateLogin(login) { this.sendAsyncMessage("AboutLogins:CreateLogin", { login, }); } - onAboutLoginsDeleteLogin(login) { + #aboutLoginsDeleteLogin(login) { this.sendAsyncMessage("AboutLogins:DeleteLogin", { login, }); } - onAboutLoginsExportPasswords() { + #aboutLoginsExportPasswords() { this.sendAsyncMessage("AboutLogins:ExportPasswords"); } - onAboutLoginsGetHelp() { + #aboutLoginsGetHelp() { this.sendAsyncMessage("AboutLogins:GetHelp"); } - onAboutLoginsImportFromBrowser() { + #aboutLoginsImportFromBrowser() { this.sendAsyncMessage("AboutLogins:ImportFromBrowser"); recordTelemetryEvent({ object: "import_from_browser", @@ -216,7 +199,7 @@ }); } - onAboutLoginsImportFromFile() { + #aboutLoginsImportFromFile() { this.sendAsyncMessage("AboutLogins:ImportFromFile"); recordTelemetryEvent({ object: "import_from_csv", @@ -224,7 +207,7 @@ }); } - onAboutLoginsOpenPreferences() { + #aboutLoginsOpenPreferences() { this.sendAsyncMessage("AboutLogins:OpenPreferences"); recordTelemetryEvent({ object: "preferences", @@ -232,7 +215,7 @@ }); } - onAboutLoginsRecordTelemetryEvent(event) { + #aboutLoginsRecordTelemetryEvent(event) { let { method } = event.detail; if (method == "open_management") { @@ -256,23 +239,23 @@ recordTelemetryEvent(event.detail); } - onAboutLoginsRemoveAllLogins() { + #aboutLoginsRemoveAllLogins() { this.sendAsyncMessage("AboutLogins:RemoveAllLogins"); } - onAboutLoginsSortChanged(detail) { + #aboutLoginsSortChanged(detail) { this.sendAsyncMessage("AboutLogins:SortChanged", detail); } - onAboutLoginsSyncEnable() { + #aboutLoginsSyncEnable() { this.sendAsyncMessage("AboutLogins:SyncEnable"); } - onAboutLoginsSyncOptions() { + #aboutLoginsSyncOptions() { this.sendAsyncMessage("AboutLogins:SyncOptions"); } - onAboutLoginsUpdateLogin(login) { + #aboutLoginsUpdateLogin(login) { this.sendAsyncMessage("AboutLogins:UpdateLogin", { login, }); @@ -281,51 +264,49 @@ receiveMessage(message) { switch (message.name) { case "AboutLogins:ImportReportData": - this.onImportReportData(message.data); + this.#importReportData(message.data); break; case "AboutLogins:PrimaryPasswordResponse": - this.onPrimaryPasswordResponse(message.data); + this.#primaryPasswordResponse(message.data); break; case "AboutLogins:RemaskPassword": - this.onRemaskPassword(message.data); + this.#remaskPassword(message.data); break; case "AboutLogins:Setup": - this.onSetup(message.data); + this.#setup(message.data); break; default: - this.passMessageDataToContent(message); + this.#passMessageDataToContent(message); } } - onImportReportData(data) { + #importReportData(data) { this.sendToContent("ImportReportData", data); } - onPrimaryPasswordResponse(data) { + #primaryPasswordResponse(data) { if (gPrimaryPasswordPromise) { gPrimaryPasswordPromise.resolve(data.result); recordTelemetryEvent(data.telemetryEvent); } } - onRemaskPassword(data) { + #remaskPassword(data) { this.sendToContent("RemaskPassword", data); } - onSetup(data) { - let waivedContent = Cu.waiveXrays(this.browsingContext.window); - waivedContent.AboutLoginsUtils.primaryPasswordEnabled = - data.primaryPasswordEnabled; - waivedContent.AboutLoginsUtils.passwordRevealVisible = - data.passwordRevealVisible; - waivedContent.AboutLoginsUtils.importVisible = data.importVisible; - waivedContent.AboutLoginsUtils.supportBaseURL = Services.urlFormatter.formatURLPref( + #setup(data) { + let utils = Cu.waiveXrays(this.browsingContext.window).AboutLoginsUtils; + utils.primaryPasswordEnabled = data.primaryPasswordEnabled; + utils.passwordRevealVisible = data.passwordRevealVisible; + utils.importVisible = data.importVisible; + utils.supportBaseURL = Services.urlFormatter.formatURLPref( "app.support.baseURL" ); this.sendToContent("Setup", data); } - passMessageDataToContent(message) { + #passMessageDataToContent(message) { this.sendToContent(message.name.replace("AboutLogins:", ""), message.data); } diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/AboutLoginsParent.jsm firefox-100.0+build1/browser/components/aboutlogins/AboutLoginsParent.jsm --- firefox-99.0.1+build1/browser/components/aboutlogins/AboutLoginsParent.jsm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/AboutLoginsParent.jsm 2022-04-26 05:44:41.000000000 +0000 @@ -103,7 +103,7 @@ ); } - AboutLogins._subscribers.add(this.browsingContext); + AboutLogins.subscribers.add(this.browsingContext); switch (message.name) { case "AboutLogins:CreateLogin": { @@ -204,7 +204,7 @@ try { Services.logins.addLogin(newLogin); } catch (error) { - this.handleLoginStorageErrors(newLogin, error); + this.#handleLoginStorageErrors(newLogin, error); } } @@ -299,14 +299,7 @@ async #subscribe() { AboutLogins._authExpirationTime = Number.NEGATIVE_INFINITY; - if (!AboutLogins._observersAdded) { - Services.obs.addObserver(AboutLogins, "passwordmgr-crypto-login"); - Services.obs.addObserver(AboutLogins, "passwordmgr-crypto-loginCanceled"); - Services.obs.addObserver(AboutLogins, "passwordmgr-storage-changed"); - Services.obs.addObserver(AboutLogins, "passwordmgr-reload-all"); - Services.obs.addObserver(AboutLogins, UIState.ON_UPDATE); - AboutLogins._observersAdded = true; - } + AboutLogins.addObservers(); const logins = await AboutLogins.getAllLogins(); try { @@ -332,7 +325,7 @@ AppConstants.platform != "linux", }); - await AboutLogins._sendAllLoginRelatedObjects( + await AboutLogins.sendAllLoginRelatedObjects( logins, this.browsingContext ); @@ -370,7 +363,7 @@ try { Services.logins.modifyLogin(logins[0], modifiedLogin); } catch (error) { - this.handleLoginStorageErrors(modifiedLogin, error); + this.#handleLoginStorageErrors(modifiedLogin, error); } } @@ -513,7 +506,7 @@ Services.logins.removeAllUserFacingLogins(); } - handleLoginStorageErrors(login, error) { + #handleLoginStorageErrors(login, error) { let messageObject = { login: augmentVanillaLoginObject(LoginHelper.loginToVanillaObject(login)), errorMessage: error.message, @@ -544,117 +537,128 @@ } } -var AboutLogins = { - _subscribers: new WeakSet(), - _observersAdded: false, - _authExpirationTime: Number.NEGATIVE_INFINITY, +class AboutLoginsInternal { + subscribers = new WeakSet(); + #observersAdded = false; + authExpirationTime = Number.NEGATIVE_INFINITY; async observe(subject, topic, type) { - if (!ChromeUtils.nondeterministicGetWeakSetKeys(this._subscribers).length) { - Services.obs.removeObserver(this, "passwordmgr-crypto-login"); - Services.obs.removeObserver(this, "passwordmgr-crypto-loginCanceled"); - Services.obs.removeObserver(this, "passwordmgr-storage-changed"); - Services.obs.removeObserver(this, "passwordmgr-reload-all"); - Services.obs.removeObserver(this, UIState.ON_UPDATE); - this._observersAdded = false; + if (!ChromeUtils.nondeterministicGetWeakSetKeys(this.subscribers).length) { + this.#removeObservers(); return; } - if (topic == "passwordmgr-reload-all") { - await this._reloadAllLogins(); - return; + switch (topic) { + case "passwordmgr-reload-all": { + await this.#reloadAllLogins(); + break; + } + case "passwordmgr-crypto-login": { + this.#removeNotifications(PRIMARY_PASSWORD_NOTIFICATION_ID); + await this.#reloadAllLogins(); + break; + } + case "passwordmgr-crypto-loginCanceled": { + this.#showPrimaryPasswordLoginNotifications(); + break; + } + case UIState.ON_UPDATE: { + this.#messageSubscribers("AboutLogins:SyncState", this.getSyncState()); + break; + } + case "passwordmgr-storage-changed": { + switch (type) { + case "addLogin": { + await this.#addLogin(subject); + break; + } + case "modifyLogin": { + this.#modifyLogin(subject); + break; + } + case "removeLogin": { + this.#removeLogin(subject); + break; + } + case "removeAllLogins": { + this.#removeAllLogins(); + break; + } + } + } } + } - if (topic == "passwordmgr-crypto-login") { - this.removeNotifications(PRIMARY_PASSWORD_NOTIFICATION_ID); - await this._reloadAllLogins(); + async #addLogin(subject) { + const login = convertSubjectToLogin(subject); + if (!login) { return; } - if (topic == "passwordmgr-crypto-loginCanceled") { - this.showPrimaryPasswordLoginNotifications(); - return; + if (BREACH_ALERTS_ENABLED) { + this.#messageSubscribers( + "AboutLogins:UpdateBreaches", + await LoginBreaches.getPotentialBreachesByLoginGUID([login]) + ); + if (VULNERABLE_PASSWORDS_ENABLED) { + this.#messageSubscribers( + "AboutLogins:UpdateVulnerableLogins", + await LoginBreaches.getPotentiallyVulnerablePasswordsByLoginGUID([ + login, + ]) + ); + } } - if (topic == UIState.ON_UPDATE) { - this.messageSubscribers("AboutLogins:SyncState", this.getSyncState()); + this.#messageSubscribers("AboutLogins:LoginAdded", login); + } + + async #modifyLogin(subject) { + subject.QueryInterface(Ci.nsIArrayExtensions); + const login = convertSubjectToLogin(subject.GetElementAt(1)); + if (!login) { return; } - switch (type) { - case "addLogin": { - const login = convertSubjectToLogin(subject); - if (!login) { - return; - } - - if (BREACH_ALERTS_ENABLED) { - this.messageSubscribers( - "AboutLogins:UpdateBreaches", - await LoginBreaches.getPotentialBreachesByLoginGUID([login]) - ); - if (VULNERABLE_PASSWORDS_ENABLED) { - this.messageSubscribers( - "AboutLogins:UpdateVulnerableLogins", - await LoginBreaches.getPotentiallyVulnerablePasswordsByLoginGUID([ - login, - ]) - ); - } - } - - this.messageSubscribers("AboutLogins:LoginAdded", login); - break; + if (BREACH_ALERTS_ENABLED) { + let breachesForThisLogin = await LoginBreaches.getPotentialBreachesByLoginGUID( + [login] + ); + let breachData = breachesForThisLogin.size + ? breachesForThisLogin.get(login.guid) + : false; + this.#messageSubscribers( + "AboutLogins:UpdateBreaches", + new Map([[login.guid, breachData]]) + ); + if (VULNERABLE_PASSWORDS_ENABLED) { + let vulnerablePasswordsForThisLogin = await LoginBreaches.getPotentiallyVulnerablePasswordsByLoginGUID( + [login] + ); + let isLoginVulnerable = !!vulnerablePasswordsForThisLogin.size; + this.#messageSubscribers( + "AboutLogins:UpdateVulnerableLogins", + new Map([[login.guid, isLoginVulnerable]]) + ); } - case "modifyLogin": { - subject.QueryInterface(Ci.nsIArrayExtensions); - const login = convertSubjectToLogin(subject.GetElementAt(1)); - if (!login) { - return; - } + } - if (BREACH_ALERTS_ENABLED) { - let breachesForThisLogin = await LoginBreaches.getPotentialBreachesByLoginGUID( - [login] - ); - let breachData = breachesForThisLogin.size - ? breachesForThisLogin.get(login.guid) - : false; - this.messageSubscribers( - "AboutLogins:UpdateBreaches", - new Map([[login.guid, breachData]]) - ); - if (VULNERABLE_PASSWORDS_ENABLED) { - let vulnerablePasswordsForThisLogin = await LoginBreaches.getPotentiallyVulnerablePasswordsByLoginGUID( - [login] - ); - let isLoginVulnerable = !!vulnerablePasswordsForThisLogin.size; - this.messageSubscribers( - "AboutLogins:UpdateVulnerableLogins", - new Map([[login.guid, isLoginVulnerable]]) - ); - } - } + this.#messageSubscribers("AboutLogins:LoginModified", login); + } - this.messageSubscribers("AboutLogins:LoginModified", login); - break; - } - case "removeLogin": { - const login = convertSubjectToLogin(subject); - if (!login) { - return; - } - this.messageSubscribers("AboutLogins:LoginRemoved", login); - break; - } - case "removeAllLogins": { - this.messageSubscribers("AboutLogins:RemoveAllLogins", []); - break; - } + #removeLogin(subject) { + const login = convertSubjectToLogin(subject); + if (!login) { + return; } - }, + this.#messageSubscribers("AboutLogins:LoginRemoved", login); + } + + #removeAllLogins() { + this.#messageSubscribers("AboutLogins:RemoveAllLogins", []); + } - async getFavicon(login) { + async #getFavicon(login) { try { const faviconData = await PlacesUtils.promiseFaviconData(login.origin); return { @@ -664,11 +668,11 @@ } catch (ex) { return null; } - }, + } async getAllFavicons(logins) { let favicons = await Promise.all( - logins.map(login => this.getFavicon(login)) + logins.map(login => this.#getFavicon(login)) ); let vanillaFavicons = {}; for (let favicon of favicons) { @@ -684,16 +688,16 @@ } catch (ex) {} } return vanillaFavicons; - }, + } - async _reloadAllLogins() { + async #reloadAllLogins() { let logins = await this.getAllLogins(); - this.messageSubscribers("AboutLogins:AllLogins", logins); - await this._sendAllLoginRelatedObjects(logins); - }, + this.#messageSubscribers("AboutLogins:AllLogins", logins); + await this.sendAllLoginRelatedObjects(logins); + } - showPrimaryPasswordLoginNotifications() { - this.showNotifications({ + #showPrimaryPasswordLoginNotifications() { + this.#showNotifications({ id: PRIMARY_PASSWORD_NOTIFICATION_ID, priority: "PRIORITY_WARNING_MEDIUM", iconURL: "chrome://browser/skin/login.svg", @@ -705,10 +709,10 @@ }, ], }); - this.messageSubscribers("AboutLogins:PrimaryPasswordAuthRequired"); - }, + this.#messageSubscribers("AboutLogins:PrimaryPasswordAuthRequired"); + } - showNotifications({ + #showNotifications({ id, priority, iconURL, @@ -717,7 +721,7 @@ onClicks, extraFtl = [], } = {}) { - for (let subscriber of this._subscriberIterator()) { + for (let subscriber of this.#subscriberIterator()) { let browser = subscriber.embedderElement; let MozXULElement = browser.ownerGlobal.MozXULElement; MozXULElement.insertFTLIfNeeded("browser/aboutLogins.ftl"); @@ -754,10 +758,10 @@ buttons ); } - }, + } - removeNotifications(notificationId) { - for (let subscriber of this._subscriberIterator()) { + #removeNotifications(notificationId) { + for (let subscriber of this.#subscriberIterator()) { let browser = subscriber.embedderElement; let { gBrowser } = browser.ownerGlobal; let notificationBox = gBrowser.getNotificationBox(browser); @@ -769,47 +773,45 @@ } notificationBox.removeNotification(notification); } - }, + } - *_subscriberIterator() { + *#subscriberIterator() { let subscribers = ChromeUtils.nondeterministicGetWeakSetKeys( - this._subscribers + this.subscribers ); for (let subscriber of subscribers) { let browser = subscriber.embedderElement; if ( - !browser || - browser.remoteType != EXPECTED_ABOUTLOGINS_REMOTE_TYPE || - !browser.contentPrincipal || - browser.contentPrincipal.originNoSuffix != ABOUT_LOGINS_ORIGIN + browser?.remoteType != EXPECTED_ABOUTLOGINS_REMOTE_TYPE || + browser?.contentPrincipal?.originNoSuffix != ABOUT_LOGINS_ORIGIN ) { - this._subscribers.delete(subscriber); + this.subscribers.delete(subscriber); continue; } yield subscriber; } - }, + } - messageSubscribers(name, details) { - for (let subscriber of this._subscriberIterator()) { + #messageSubscribers(name, details) { + for (let subscriber of this.#subscriberIterator()) { try { if (subscriber.currentWindowGlobal) { let actor = subscriber.currentWindowGlobal.getActor("AboutLogins"); actor.sendAsyncMessage(name, details); } } catch (ex) { - if (ex.result != Cr.NS_ERROR_NOT_INITIALIZED) { + if (ex.result == Cr.NS_ERROR_NOT_INITIALIZED) { + // The actor may be destroyed before the message is sent. + log.debug( + "messageSubscribers: exception when calling sendAsyncMessage", + ex + ); + } else { throw ex; } - - // The actor may be destroyed before the message is sent. - log.debug( - "messageSubscribers: exception when calling sendAsyncMessage", - ex - ); } } - }, + } async getAllLogins() { try { @@ -824,15 +826,15 @@ } throw e; } - }, + } - async _sendAllLoginRelatedObjects(logins, browsingContext) { + async sendAllLoginRelatedObjects(logins, browsingContext) { let sendMessageFn = (name, details) => { - if (browsingContext && browsingContext.currentWindowGlobal) { + if (browsingContext?.currentWindowGlobal) { let actor = browsingContext.currentWindowGlobal.getActor("AboutLogins"); actor.sendAsyncMessage(name, details); } else { - this.messageSubscribers(name, details); + this.#messageSubscribers(name, details); } }; @@ -855,7 +857,7 @@ "AboutLogins:SendFavicons", await AboutLogins.getAllFavicons(logins) ); - }, + } getSyncState() { const state = UIState.get(); @@ -872,12 +874,38 @@ fxAccountsEnabled: FXA_ENABLED, passwordSyncEnabled, }; - }, + } onPasswordSyncEnabledPreferenceChange(data, previous, latest) { - this.messageSubscribers("AboutLogins:SyncState", this.getSyncState()); - }, -}; + this.#messageSubscribers("AboutLogins:SyncState", this.getSyncState()); + } + + #observedTopics = [ + "passwordmgr-crypto-login", + "passwordmgr-crypto-loginCanceled", + "passwordmgr-storage-changed", + "passwordmgr-reload-all", + UIState.ON_UPDATE, + ]; + + addObservers() { + if (!this.#observersAdded) { + for (const topic of this.#observedTopics) { + Services.obs.addObserver(this, topic); + } + this.#observersAdded = true; + } + } + + #removeObservers() { + for (const topic of this.#observedTopics) { + Services.obs.removeObserver(this, topic); + } + this.#observersAdded = false; + } +} + +let AboutLogins = new AboutLoginsInternal(); var _AboutLogins = AboutLogins; XPCOMUtils.defineLazyPreferenceGetter( @@ -885,5 +913,5 @@ "PASSWORD_SYNC_ENABLED", "services.sync.engine.passwords", false, - AboutLogins.onPasswordSyncEnabledPreferenceChange.bind(AboutLogins) + AboutLogins.onPasswordSyncEnabledPreferenceChange ); diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/content/aboutLogins.css firefox-100.0+build1/browser/components/aboutlogins/content/aboutLogins.css --- firefox-99.0.1+build1/browser/components/aboutlogins/content/aboutLogins.css 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/content/aboutLogins.css 2022-04-26 05:44:41.000000000 +0000 @@ -15,7 +15,7 @@ --sidebar-width: 320px; display: grid; grid-template-columns: var(--sidebar-width) 1fr; - grid-template-rows: 1fr; + grid-template-rows: auto 1fr; } @media (max-width: 830px) { @@ -69,10 +69,6 @@ font-weight: 600; } -:root:not(.official-branding) #branding-logo { - visibility: hidden; -} - :root:not(.primary-password-auth-required) #primary-password-required-overlay { display: none; } diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/content/aboutLogins.html firefox-100.0+build1/browser/components/aboutlogins/content/aboutLogins.html --- firefox-99.0.1+build1/browser/components/aboutlogins/content/aboutLogins.html 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/content/aboutLogins.html 2022-04-26 05:44:41.000000000 +0000 @@ -33,16 +33,14 @@ <link rel="icon" href="chrome://branding/content/icon32.png"> </head> <body> + <header> + <login-filter></login-filter> + <fxaccounts-button hidden></fxaccounts-button> + <menu-button></menu-button> + </header> <login-list></login-list> - <section> - <header> - <login-filter></login-filter> - <fxaccounts-button hidden></fxaccounts-button> - <menu-button></menu-button> - </header> - <login-item></login-item> - <login-intro></login-intro> - </section> + <login-item></login-item> + <login-intro></login-intro> <confirmation-dialog hidden></confirmation-dialog> <remove-logins-dialog hidden></remove-logins-dialog> <import-summary-dialog hidden></import-summary-dialog> diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_aaa_eventTelemetry_run_first.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_aaa_eventTelemetry_run_first.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_aaa_eventTelemetry_run_first.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_aaa_eventTelemetry_run_first.js 2022-04-26 05:44:41.000000000 +0000 @@ -30,7 +30,7 @@ "password" ); -add_task(async function setup() { +add_setup(async function() { TEST_LOGIN1 = await addLogin(TEST_LOGIN1); VULNERABLE_TEST_LOGIN2 = await addLogin(VULNERABLE_TEST_LOGIN2); TEST_LOGIN3 = await addLogin(TEST_LOGIN3); diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_alertDismissedAfterChangingPassword.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_alertDismissedAfterChangingPassword.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_alertDismissedAfterChangingPassword.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_alertDismissedAfterChangingPassword.js 2022-04-26 05:44:41.000000000 +0000 @@ -24,7 +24,7 @@ "password" ); -add_task(async function setup() { +add_setup(async function() { TEST_LOGIN1 = await addLogin(TEST_LOGIN1); VULNERABLE_TEST_LOGIN2 = await addLogin(VULNERABLE_TEST_LOGIN2); TEST_LOGIN3 = await addLogin(TEST_LOGIN3); diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_breachAlertShowingForAddedLogin.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_breachAlertShowingForAddedLogin.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_breachAlertShowingForAddedLogin.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_breachAlertShowingForAddedLogin.js 2022-04-26 05:44:41.000000000 +0000 @@ -14,7 +14,7 @@ schema: "1541615609018", }; -add_task(async function setup() { +add_setup(async function() { await BrowserTestUtils.openNewForegroundTab({ gBrowser, url: "about:logins", diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_confirmDeleteDialog.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_confirmDeleteDialog.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_confirmDeleteDialog.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_confirmDeleteDialog.js 2022-04-26 05:44:41.000000000 +0000 @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -add_task(async function setup() { +add_setup(async function() { await BrowserTestUtils.openNewForegroundTab({ gBrowser, url: "about:logins", diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_contextmenuFillLogins.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_contextmenuFillLogins.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_contextmenuFillLogins.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_contextmenuFillLogins.js 2022-04-26 05:44:41.000000000 +0000 @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -add_task(async function setup() { +add_setup(async function() { TEST_LOGIN1 = await addLogin(TEST_LOGIN1); await BrowserTestUtils.openNewForegroundTab({ gBrowser, diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_createLogin.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_createLogin.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_createLogin.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_createLogin.js 2022-04-26 05:44:41.000000000 +0000 @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -add_task(async function setup() { +add_setup(async function() { let aboutLoginsTab = await BrowserTestUtils.openNewForegroundTab({ gBrowser, url: "about:logins", diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_deleteLogin.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_deleteLogin.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_deleteLogin.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_deleteLogin.js 2022-04-26 05:44:41.000000000 +0000 @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -add_task(async function setup() { +add_setup(async function() { TEST_LOGIN1 = await addLogin(TEST_LOGIN1); TEST_LOGIN2 = await addLogin(TEST_LOGIN2); await BrowserTestUtils.openNewForegroundTab({ diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_fxAccounts.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_fxAccounts.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_fxAccounts.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_fxAccounts.js 2022-04-26 05:44:41.000000000 +0000 @@ -1,9 +1,6 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -let syncService = {}; -ChromeUtils.import("resource://services-sync/service.js", syncService); -const service = syncService.Service; const { UIState } = ChromeUtils.import("resource://services-sync/UIState.jsm"); function mockState(state) { @@ -15,7 +12,7 @@ }); } -add_task(async function setup() { +add_setup(async function() { let aboutLoginsTab = await BrowserTestUtils.openNewForegroundTab({ gBrowser, url: "about:logins", diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_loginItemErrors.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_loginItemErrors.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_loginItemErrors.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_loginItemErrors.js 2022-04-26 05:44:41.000000000 +0000 @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -add_task(async function setup() { +add_setup(async function() { await BrowserTestUtils.openNewForegroundTab({ gBrowser, url: "about:logins", diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_loginListChanges.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_loginListChanges.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_loginListChanges.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_loginListChanges.js 2022-04-26 05:44:41.000000000 +0000 @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -add_task(async function setup() { +add_setup(async function() { await BrowserTestUtils.openNewForegroundTab({ gBrowser, url: "about:logins", diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_loginSortOrderRestored.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_loginSortOrderRestored.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_loginSortOrderRestored.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_loginSortOrderRestored.js 2022-04-26 05:44:41.000000000 +0000 @@ -16,7 +16,7 @@ const SORT_PREF_NAME = "signon.management.page.sort"; -add_task(async function setup() { +add_setup(async function() { TEST_LOGIN3.QueryInterface(Ci.nsILoginMetaInfo).timePasswordChanged = 1; TEST_LOGIN1 = await addLogin(TEST_LOGIN1); info(`TEST_LOGIN1 added with guid=${TEST_LOGIN1.guid}`); diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_noLoginsView.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_noLoginsView.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_noLoginsView.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_noLoginsView.js 2022-04-26 05:44:41.000000000 +0000 @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -add_task(async function setup() { +add_setup(async function() { await BrowserTestUtils.openNewForegroundTab({ gBrowser, url: "about:logins", diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_openExport.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_openExport.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_openExport.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_openExport.js 2022-04-26 05:44:41.000000000 +0000 @@ -17,7 +17,7 @@ let { MockFilePicker } = SpecialPowers; -add_task(async function setup() { +add_setup(async function() { await TestUtils.waitForCondition(() => { Services.telemetry.clearEvents(); let events = Services.telemetry.snapshotEvents( diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_openFiltered.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_openFiltered.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_openFiltered.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_openFiltered.js 2022-04-26 05:44:41.000000000 +0000 @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -add_task(async function setup() { +add_setup(async function() { let storageChangedPromised = TestUtils.topicObserved( "passwordmgr-storage-changed", (_, data) => data == "addLogin" diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_openImportCSV.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_openImportCSV.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_openImportCSV.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_openImportCSV.js 2022-04-26 05:44:41.000000000 +0000 @@ -257,7 +257,7 @@ const random = Math.round(Math.random() * 100000001); -add_task(async function setup() { +add_setup(async function() { registerCleanupFunction(() => { Services.logins.removeAllUserFacingLogins(); }); diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_openImport.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_openImport.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_openImport.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_openImport.js 2022-04-26 05:44:41.000000000 +0000 @@ -5,7 +5,7 @@ "resource://testing-common/TelemetryTestUtils.jsm" ); -add_task(async function setup() { +add_setup(async function() { await TestUtils.waitForCondition(() => { Services.telemetry.clearEvents(); let events = Services.telemetry.snapshotEvents( diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_openPreferencesExternal.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_openPreferencesExternal.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_openPreferencesExternal.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_openPreferencesExternal.js 2022-04-26 05:44:41.000000000 +0000 @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -add_task(async function setup() { +add_setup(async function() { await BrowserTestUtils.openNewForegroundTab({ gBrowser, url: "about:logins", diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_openPreferences.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_openPreferences.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_openPreferences.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_openPreferences.js 2022-04-26 05:44:41.000000000 +0000 @@ -5,7 +5,7 @@ "resource://testing-common/TelemetryTestUtils.jsm" ); -add_task(async function setup() { +add_setup(async function() { await TestUtils.waitForCondition(() => { Services.telemetry.clearEvents(); let events = Services.telemetry.snapshotEvents( diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_openSite.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_openSite.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_openSite.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_openSite.js 2022-04-26 05:44:41.000000000 +0000 @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -add_task(async function setup() { +add_setup(async function() { TEST_LOGIN1 = await addLogin(TEST_LOGIN1); await BrowserTestUtils.openNewForegroundTab({ gBrowser, diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_primaryPassword.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_primaryPassword.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_primaryPassword.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_primaryPassword.js 2022-04-26 05:44:41.000000000 +0000 @@ -17,7 +17,7 @@ ); } -add_task(async function setup() { +add_setup(async function() { await addLogin(TEST_LOGIN1); registerCleanupFunction(() => { Services.logins.removeAllUserFacingLogins(); diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_removeAllDialog.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_removeAllDialog.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_removeAllDialog.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_removeAllDialog.js 2022-04-26 05:44:41.000000000 +0000 @@ -76,7 +76,7 @@ }); } -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [[OS_REAUTH_PREF, false]], }); diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_tabKeyNav.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_tabKeyNav.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_tabKeyNav.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_tabKeyNav.js 2022-04-26 05:44:41.000000000 +0000 @@ -14,7 +14,7 @@ schema: "1541615609018", }; -add_task(async function setup() { +add_setup(async function() { TEST_LOGIN1 = await addLogin(TEST_LOGIN1); await BrowserTestUtils.openNewForegroundTab({ gBrowser, @@ -30,70 +30,103 @@ let browser = gBrowser.selectedBrowser; await SpecialPowers.spawn(browser, [], async () => { const EventUtils = ContentTaskUtils.getEventUtils(content); + // list [selector, shadow root selector] for each element + // in the order we expect them to be navigated. + let expectedElementsInOrder = [ + ["login-filter", "input"], + ["fxaccounts-button", "button"], + ["menu-button", "button"], + ["login-list", "select"], + ["login-list", "ol"], + ["login-list", "button"], + ["login-item", "button.edit-button"], + ["login-item", "button.delete-button"], + ["login-item", "a.origin-input"], + ["login-item", "button.copy-username-button"], + ["login-item", "input.reveal-password-checkbox"], + ["login-item", "button.copy-password-button"], + ]; + + let firstElement = content.document + .querySelector(expectedElementsInOrder.at(0).at(0)) + .shadowRoot.querySelector(expectedElementsInOrder.at(0).at(1)); + let lastElement = content.document + .querySelector(expectedElementsInOrder.at(-1).at(0)) + .shadowRoot.querySelector(expectedElementsInOrder.at(-1).at(1)); async function tab() { EventUtils.synthesizeKey("KEY_Tab", {}, content); await new Promise(resolve => content.requestAnimationFrame(resolve)); // The following line can help with focus trap debugging: - // await new Promise(resolve => content.window.setTimeout(resolve, 100)); + // await new Promise(resolve => content.window.setTimeout(resolve, 500)); + } + async function shiftTab() { + EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true }, content); + await new Promise(resolve => content.requestAnimationFrame(resolve)); + // await new Promise(resolve => content.window.setTimeout(resolve, 500)); } // Getting focused shadow DOM element itself instead of shadowRoot, // using recursion for any component-nesting level, as in: // document.activeElement.shadowRoot.activeElement.shadowRoot.activeElement - function getFocusedEl() { - let el = content.document.activeElement; + function getFocusedElement() { + let element = content.document.activeElement; const getShadowRootFocus = e => { if (e.shadowRoot) { return getShadowRootFocus(e.shadowRoot.activeElement); } return e; }; - return getShadowRootFocus(el); + return getShadowRootFocus(element); } - - // We’re making two passes with focused and focusedAgain sets of DOM elements, - // rather than just checking we get back to the first focused element, - // so we can cover more edge-cases involving issues with focus tree. - const focused = new Set(); - const focusedAgain = new Set(); - - // Initializing: some element is supposed to be automatically focused - const firstEl = getFocusedEl(); - focused.add(firstEl); - - // Setting a maximum number of keypresses to escape the loop, - // should we fall into a focus trap. - // Fixed amount of keypresses also helps assess consistency with keyboard navigation. - const maxKeypresses = content.document.getElementsByTagName("*").length * 2; - let keypresses = 1; - - while (focusedAgain.size < focused.size && keypresses <= maxKeypresses) { + // Helper function for getting the DOM element given an entry in the ordered list + function getElementFromOrderedArray(combinedSelectors) { + let [selector, shadowRootSelector] = combinedSelectors; + return content.document + .querySelector(selector) + .shadowRoot.querySelector(shadowRootSelector); + } + // Ensure the test starts in a valid state + firstElement.focus(); + // Assert that we tab navigate correctly + for (let expectedSelector of expectedElementsInOrder) { + let expectedElement = getElementFromOrderedArray(expectedSelector); + // By default, MacOS will skip over certain text controls, such as links. + if ( + content.window.navigator.platform.toLowerCase().includes("mac") && + expectedElement.tagName === "A" + ) { + continue; + } + let actualElem = getFocusedElement(); + is( + actualElem, + expectedElement, + "Actual focused element should equal the expected focused element" + ); await tab(); - keypresses++; - let el = getFocusedEl(); + } - // The following block is needed to get back to document - // after tabbing to browser chrome. - // This focus trap in browser chrome is a testing artifact we’re fixing below. - if (el.tagName === "BODY" && el !== firstEl) { - firstEl.focus(); - await new Promise(resolve => content.requestAnimationFrame(resolve)); - el = getFocusedEl(); - } + lastElement.focus(); - if (focused.has(el)) { - focusedAgain.add(el); - } else { - focused.add(el); + // Assert that we shift + tab navigate correctly starting from the last ordered element + for (let expectedSelector of expectedElementsInOrder.reverse()) { + let expectedElement = getElementFromOrderedArray(expectedSelector); + // By default, MacOS will skip over certain text controls, such as links. + if ( + content.window.navigator.platform.toLowerCase().includes("mac") && + expectedElement.tagName === "A" + ) { + continue; } + let actualElement = getFocusedElement(); + is( + actualElement, + expectedElement, + "Actual focused element should equal the expected focused element" + ); + await shiftTab(); } - - is( - focusedAgain.size, - focused.size, - "All focusable elements should be kept accessible with TAB key (no focus trap)." - ); }); }); @@ -117,22 +150,22 @@ let createButton = loginList.shadowRoot.querySelector( ".create-login-button" ); - let getFocusedEl = () => loginList.shadowRoot.activeElement; + let getFocusedElement = () => loginList.shadowRoot.activeElement; - is(getFocusedEl(), null, "login-list isn't focused"); + is(getFocusedElement(), null, "login-list isn't focused"); loginSort.focus(); await waitForAnimationFrame(); - is(getFocusedEl(), loginSort, "login sort is focused"); + is(getFocusedElement(), loginSort, "login sort is focused"); await tab(); - is(getFocusedEl(), loginListbox, "listbox is focused next"); + is(getFocusedElement(), loginListbox, "listbox is focused next"); await tab(); - is(getFocusedEl(), createButton, "create button is after"); + is(getFocusedElement(), createButton, "create button is after"); await tab(); - is(getFocusedEl(), null, "login-list isn't focused again"); + is(getFocusedElement(), null, "login-list isn't focused again"); }); }); @@ -156,18 +189,22 @@ let loginList = content.document.querySelector("login-list"); let loginItem = content.document.querySelector("login-item"); + let loginFilter = content.document.querySelector("login-filter"); let createButton = loginList.shadowRoot.querySelector( ".create-login-button" ); let editButton = loginItem.shadowRoot.querySelector(".edit-button"); let breachAlert = loginItem.shadowRoot.querySelector(".breach-alert"); - let getFocusedEl = () => { + let getFocusedElement = () => { if (content.document.activeElement == loginList) { return loginList.shadowRoot.activeElement; } if (content.document.activeElement == loginItem) { return loginItem.shadowRoot.activeElement; } + if (content.document.activeElement == loginFilter) { + return loginFilter.shadowRoot.activeElement; + } ok( false, "not expecting a different element to get focused in this test: " + @@ -198,11 +235,11 @@ createButton.focus(); await waitForAnimationFrame(); - is(getFocusedEl(), createButton, "create button is focused"); + is(getFocusedElement(), createButton, "create button is focused"); await tab(); await waitForAnimationFrame(); - is(getFocusedEl(), editButton, "edit button is focused"); + is(getFocusedElement(), editButton, "edit button is focused"); } } ); diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_updateLogin.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_updateLogin.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_updateLogin.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_updateLogin.js 2022-04-26 05:44:41.000000000 +0000 @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -add_task(async function setup() { +add_setup(async function() { TEST_LOGIN1 = await addLogin(TEST_LOGIN1); await BrowserTestUtils.openNewForegroundTab({ gBrowser, diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_vulnerableLoginAddedInSecondaryWindow.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_vulnerableLoginAddedInSecondaryWindow.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/browser_vulnerableLoginAddedInSecondaryWindow.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/browser_vulnerableLoginAddedInSecondaryWindow.js 2022-04-26 05:44:41.000000000 +0000 @@ -16,7 +16,7 @@ let tabInSecondWindow; -add_task(async function setup() { +add_setup(async function() { TEST_LOGIN1 = await addLogin(TEST_LOGIN1); TEST_LOGIN2 = await addLogin(TEST_LOGIN2); TEST_LOGIN3 = await addLogin(TEST_LOGIN3); diff -Nru firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/head.js firefox-100.0+build1/browser/components/aboutlogins/tests/browser/head.js --- firefox-99.0.1+build1/browser/components/aboutlogins/tests/browser/head.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/aboutlogins/tests/browser/head.js 2022-04-26 05:44:41.000000000 +0000 @@ -81,7 +81,7 @@ let EXPECTED_BREACH = null; let EXPECTED_ERROR_MESSAGE = null; -add_task(async function setup_head() { +add_setup(async function setup_head() { const db = await RemoteSettings(LoginBreaches.REMOTE_SETTINGS_COLLECTION).db; if (EXPECTED_BREACH) { await db.create(EXPECTED_BREACH, { diff -Nru firefox-99.0.1+build1/browser/components/attribution/AttributionCode.jsm firefox-100.0+build1/browser/components/attribution/AttributionCode.jsm --- firefox-99.0.1+build1/browser/components/attribution/AttributionCode.jsm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/attribution/AttributionCode.jsm 2022-04-26 05:44:41.000000000 +0000 @@ -33,8 +33,7 @@ "resource:///modules/MacAttribution.jsm" ); XPCOMUtils.defineLazyGetter(this, "log", () => { - let ConsoleAPI = ChromeUtils.import("resource://gre/modules/Console.jsm", {}) - .ConsoleAPI; + let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm"); let consoleOptions = { // tip: set maxLogLevel to "debug" and use log.debug() to create detailed // messages during development. See LOG_LEVELS in Console.jsm for details. diff -Nru firefox-99.0.1+build1/browser/components/attribution/MacAttribution.jsm firefox-100.0+build1/browser/components/attribution/MacAttribution.jsm --- firefox-99.0.1+build1/browser/components/attribution/MacAttribution.jsm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/attribution/MacAttribution.jsm 2022-04-26 05:44:41.000000000 +0000 @@ -9,8 +9,7 @@ "resource://gre/modules/XPCOMUtils.jsm" ); XPCOMUtils.defineLazyGetter(this, "log", () => { - let ConsoleAPI = ChromeUtils.import("resource://gre/modules/Console.jsm", {}) - .ConsoleAPI; + let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm"); let consoleOptions = { // tip: set maxLogLevel to "debug" and use log.debug() to create detailed // messages during development. See LOG_LEVELS in Console.jsm for details. diff -Nru firefox-99.0.1+build1/browser/components/attribution/test/browser/head.js firefox-100.0+build1/browser/components/attribution/test/browser/head.js --- firefox-99.0.1+build1/browser/components/attribution/test/browser/head.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/attribution/test/browser/head.js 2022-04-26 05:44:41.000000000 +0000 @@ -13,7 +13,7 @@ const INDEX_WRITE_ERROR = 2; const INDEX_QUARANTINE_ERROR = 3; -add_task(function setup() { +add_setup(function() { // AttributionCode._clearCache is only possible in a testing environment let env = Cc["@mozilla.org/process/environment;1"].getService( Ci.nsIEnvironment diff -Nru firefox-99.0.1+build1/browser/components/BrowserGlue.jsm firefox-100.0+build1/browser/components/BrowserGlue.jsm --- firefox-99.0.1+build1/browser/components/BrowserGlue.jsm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/BrowserGlue.jsm 2022-04-26 05:44:41.000000000 +0000 @@ -58,6 +58,8 @@ NewTabUtils: "resource://gre/modules/NewTabUtils.jsm", NimbusFeatures: "resource://nimbus/ExperimentAPI.jsm", Normandy: "resource://normandy/Normandy.jsm", + OnboardingMessageProvider: + "resource://activity-stream/lib/OnboardingMessageProvider.jsm", OsEnvironment: "resource://gre/modules/OsEnvironment.jsm", PageActions: "resource:///modules/PageActions.jsm", PageThumbs: "resource://gre/modules/PageThumbs.jsm", @@ -85,6 +87,8 @@ ShellService: "resource:///modules/ShellService.jsm", ShortcutUtils: "resource://gre/modules/ShortcutUtils.jsm", SnapshotMonitor: "resource:///modules/SnapshotMonitor.jsm", + SpecialMessageActions: + "resource://messaging-system/lib/SpecialMessageActions.jsm", TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm", TabUnloader: "resource:///modules/TabUnloader.jsm", TelemetryUtils: "resource://gre/modules/TelemetryUtils.jsm", @@ -215,6 +219,8 @@ }, }, matches: ["about:logins", "about:logins?*", "about:loginsimportreport"], + allFrames: true, + remoteTypes: ["privilegedabout"], }, AboutNewTab: { @@ -776,97 +782,6 @@ }, }; -(function earlyBlankFirstPaint() { - let startTime = Cu.now(); - if ( - AppConstants.platform == "macosx" || - Services.startup.wasSilentlyStarted || - !Services.prefs.getBoolPref("browser.startup.blankWindow", false) - ) { - return; - } - - // Until bug 1450626 and bug 1488384 are fixed, skip the blank window when - // using a non-default theme. - if ( - !Services.startup.showedPreXULSkeletonUI && - Services.prefs.getCharPref( - "extensions.activeThemeID", - "default-theme@mozilla.org" - ) != "default-theme@mozilla.org" - ) { - return; - } - - let store = Services.xulStore; - let getValue = attr => - store.getValue(AppConstants.BROWSER_CHROME_URL, "main-window", attr); - let width = getValue("width"); - let height = getValue("height"); - - // The clean profile case isn't handled yet. Return early for now. - if (!width || !height) { - return; - } - - let browserWindowFeatures = - "chrome,all,dialog=no,extrachrome,menubar,resizable,scrollbars,status," + - "location,toolbar,personalbar"; - let win = Services.ww.openWindow( - null, - "about:blank", - null, - browserWindowFeatures, - null - ); - - // Hide the titlebar if the actual browser window will draw in it. - let hiddenTitlebar = Services.appinfo.drawInTitlebar; - if (hiddenTitlebar) { - win.windowUtils.setChromeMargin(0, 2, 2, 2); - } - - let docElt = win.document.documentElement; - docElt.setAttribute("screenX", getValue("screenX")); - docElt.setAttribute("screenY", getValue("screenY")); - - // The sizemode="maximized" attribute needs to be set before first paint. - let sizemode = getValue("sizemode"); - if (sizemode == "maximized") { - docElt.setAttribute("sizemode", sizemode); - - // Set the size to use when the user leaves the maximized mode. - // The persisted size is the outer size, but the height/width - // attributes set the inner size. - let appWin = win.docShell.treeOwner - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIAppWindow); - height -= appWin.outerToInnerHeightDifferenceInCSSPixels; - width -= appWin.outerToInnerWidthDifferenceInCSSPixels; - docElt.setAttribute("height", height); - docElt.setAttribute("width", width); - } else { - // Setting the size of the window in the features string instead of here - // causes the window to grow by the size of the titlebar. - win.resizeTo(width, height); - } - - // Set this before showing the window so that graphics code can use it to - // decide to skip some expensive code paths (eg. starting the GPU process). - docElt.setAttribute("windowtype", "navigator:blank"); - - // The window becomes visible after OnStopRequest, so make this happen now. - win.stop(); - - ChromeUtils.addProfilerMarker("earlyBlankFirstPaint", startTime); - win.openTime = Cu.now(); - - let { TelemetryTimestamps } = ChromeUtils.import( - "resource://gre/modules/TelemetryTimestamps.jsm" - ); - TelemetryTimestamps.add("blankWindowShown"); -})(); - XPCOMUtils.defineLazyGetter( this, "WeaveService", @@ -974,6 +889,7 @@ _migrationImportsDefaultBookmarks: false, _placesBrowserInitComplete: false, _isNewProfile: undefined, + _defaultCookieBehaviorAtStartup: null, _setPrefToSaveSession: function BG__setPrefToSaveSession(aForce) { if (!this._saveSession && !aForce) { @@ -993,35 +909,6 @@ Services.prefs.savePrefFile(null); }, - _setSyncAutoconnectDelay: function BG__setSyncAutoconnectDelay() { - // Assume that a non-zero value for services.sync.autoconnectDelay should override - if (Services.prefs.prefHasUserValue("services.sync.autoconnectDelay")) { - let prefDelay = Services.prefs.getIntPref( - "services.sync.autoconnectDelay" - ); - - if (prefDelay > 0) { - return; - } - } - - // delays are in seconds - const MAX_DELAY = 300; - let delay = 3; - for (let win of Services.wm.getEnumerator("navigator:browser")) { - // browser windows without a gBrowser almost certainly means we are - // shutting down, so instead of just ignoring that window we abort. - if (win.closed || !win.gBrowser) { - return; - } - delay += win.gBrowser.tabs.length; - } - delay = delay <= MAX_DELAY ? delay : MAX_DELAY; - - const { Weave } = ChromeUtils.import("resource://services-sync/main.js"); - Weave.Service.scheduler.delayedAutoConnect(delay); - }, - // nsIObserver implementation observe: async function BG_observe(subject, topic, data) { switch (topic) { @@ -1064,9 +951,6 @@ this._setPrefToSaveSession(); } break; - case "weave:service:ready": - this._setSyncAutoconnectDelay(); - break; case "fxaccounts:onverified": this._onThisDeviceConnected(); break; @@ -1218,6 +1102,9 @@ DownloadsViewableInternally.register(); break; + case "app-startup": + this._earlyBlankFirstPaint(subject); + break; } }, @@ -1232,7 +1119,6 @@ "browser:purge-session-history", "quit-application-requested", "quit-application-granted", - "weave:service:ready", "fxaccounts:onverified", "fxaccounts:device_connected", "fxaccounts:verify_login", @@ -1311,6 +1197,10 @@ this._matchCBCategory ); Services.prefs.removeObserver( + "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation", + this._matchCBCategory + ); + Services.prefs.removeObserver( "privacy.partition.network_state.ocsp_cache", this._matchCBCategory ); @@ -1555,6 +1445,104 @@ ); }, + _earlyBlankFirstPaint(cmdLine) { + let startTime = Cu.now(); + if ( + AppConstants.platform == "macosx" || + Services.startup.wasSilentlyStarted || + !Services.prefs.getBoolPref("browser.startup.blankWindow", false) + ) { + return; + } + + // Until bug 1450626 and bug 1488384 are fixed, skip the blank window when + // using a non-default theme. + if ( + !Services.startup.showedPreXULSkeletonUI && + Services.prefs.getCharPref( + "extensions.activeThemeID", + "default-theme@mozilla.org" + ) != "default-theme@mozilla.org" + ) { + return; + } + + let store = Services.xulStore; + let getValue = attr => + store.getValue(AppConstants.BROWSER_CHROME_URL, "main-window", attr); + let width = getValue("width"); + let height = getValue("height"); + + // The clean profile case isn't handled yet. Return early for now. + if (!width || !height) { + return; + } + + let browserWindowFeatures = + "chrome,all,dialog=no,extrachrome,menubar,resizable,scrollbars,status," + + "location,toolbar,personalbar"; + // This needs to be set when opening the window to ensure that the AppUserModelID + // is set correctly on Windows. Without it, initial launches with `-private-window` + // will show up under the regular Firefox taskbar icon first, and then switch + // to the Private Browsing icon shortly thereafter. + if (cmdLine.findFlag("private-window", false) != -1) { + browserWindowFeatures += ",private"; + } + let win = Services.ww.openWindow( + null, + "about:blank", + null, + browserWindowFeatures, + null + ); + + // Hide the titlebar if the actual browser window will draw in it. + let hiddenTitlebar = Services.appinfo.drawInTitlebar; + if (hiddenTitlebar) { + win.windowUtils.setChromeMargin(0, 2, 2, 2); + } + + let docElt = win.document.documentElement; + docElt.setAttribute("screenX", getValue("screenX")); + docElt.setAttribute("screenY", getValue("screenY")); + + // The sizemode="maximized" attribute needs to be set before first paint. + let sizemode = getValue("sizemode"); + if (sizemode == "maximized") { + docElt.setAttribute("sizemode", sizemode); + + // Set the size to use when the user leaves the maximized mode. + // The persisted size is the outer size, but the height/width + // attributes set the inner size. + let appWin = win.docShell.treeOwner + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIAppWindow); + height -= appWin.outerToInnerHeightDifferenceInCSSPixels; + width -= appWin.outerToInnerWidthDifferenceInCSSPixels; + docElt.setAttribute("height", height); + docElt.setAttribute("width", width); + } else { + // Setting the size of the window in the features string instead of here + // causes the window to grow by the size of the titlebar. + win.resizeTo(width, height); + } + + // Set this before showing the window so that graphics code can use it to + // decide to skip some expensive code paths (eg. starting the GPU process). + docElt.setAttribute("windowtype", "navigator:blank"); + + // The window becomes visible after OnStopRequest, so make this happen now. + win.stop(); + + ChromeUtils.addProfilerMarker("earlyBlankFirstPaint", startTime); + win.openTime = Cu.now(); + + let { TelemetryTimestamps } = ChromeUtils.import( + "resource://gre/modules/TelemetryTimestamps.jsm" + ); + TelemetryTimestamps.add("blankWindowShown"); + }, + _firstWindowTelemetry(aWindow) { let scaling = aWindow.devicePixelRatio * 100; try { @@ -1642,8 +1630,7 @@ let updateChannel; try { updateChannel = ChromeUtils.import( - "resource://gre/modules/UpdateUtils.jsm", - {} + "resource://gre/modules/UpdateUtils.jsm" ).UpdateUtils.UpdateChannel; } catch (ex) {} if (updateChannel) { @@ -1694,9 +1681,16 @@ PlacesUtils.favicons.setDefaultIconURIPreferredSize( 16 * aWindow.devicePixelRatio ); + + // Keep track of the initial default cookie behavior to revert to when + // users opt-out. This is used by _setDefaultCookieBehavior. + BrowserGlue._defaultCookieBehaviorAtStartup = Services.prefs + .getDefaultBranch("") + .getIntPref("network.cookie.cookieBehavior"); // _setDefaultCookieBehavior needs to run before other functions that modify // privacy preferences such as _setPrefExpectationsAndUpdate and _matchCBCategory this._setDefaultCookieBehavior(); + this._setPrefExpectationsAndUpdate(); this._matchCBCategory(); @@ -1718,6 +1712,10 @@ this._matchCBCategory ); Services.prefs.addObserver( + "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation", + this._matchCBCategory + ); + Services.prefs.addObserver( "privacy.partition.network_state.ocsp_cache", this._matchCBCategory ); @@ -1768,7 +1766,7 @@ "network.cookie.cookieBehavior", dFPIEnabled ? Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN - : Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER + : BrowserGlue._defaultCookieBehaviorAtStartup ); Services.telemetry.scalarSet( @@ -1792,15 +1790,15 @@ } else { Services.prefs.setStringPref( "browser.search.param.google_channel_us", - "xus7" + "tus7" ); Services.prefs.setStringPref( "browser.search.param.google_channel_row", - "xrow7" + "trow7" ); Services.prefs.setStringPref( "browser.search.param.bing_ptag", - "MOZZ0000000032" + "MOZZ0000000031" ); } }, @@ -2057,45 +2055,6 @@ }); }, - // Set up a listener to enable/disable the translation extension - // based on its preference. - _monitorTranslationsPref() { - const PREF = "extensions.translations.disabled"; - const ID = "firefox-translations@mozilla.org"; - const oldID = "firefox-infobar-ui-bergamot-browser-extension@browser.mt"; - - // First, try to uninstall the old extension, if exists. - (async () => { - let addon = await AddonManager.getAddonByID(oldID); - if (addon) { - addon.uninstall().catch(Cu.reportError); - } - })(); - - const _checkTranslationsPref = async () => { - let addon = await AddonManager.getAddonByID(ID); - let disabled = Services.prefs.getBoolPref(PREF, false); - if (!addon && disabled) { - // not installed, bail out early. - return; - } - if (!disabled) { - // first time install of addon and install on firefox update - addon = - (await AddonManager.maybeInstallBuiltinAddon( - ID, - "0.4.3", - "resource://builtin-addons/translations/" - )) || addon; - await addon.enable(); - } else if (addon) { - await addon.disable(); - } - }; - Services.prefs.addObserver(PREF, _checkTranslationsPref); - _checkTranslationsPref(); - }, - async _setupSearchDetection() { // There is no pref for this add-on because it shouldn't be disabled. const ID = "addons-search-detection@mozilla.com"; @@ -2366,9 +2325,6 @@ this._monitorIonStudies(); this._setupSearchDetection(); - if (AppConstants.NIGHTLY_BUILD) { - this._monitorTranslationsPref(); - } this._monitorGPCPref(); this._monitorPrivacySegmentationPref(); }, @@ -2496,11 +2452,22 @@ let shellService = Cc[ "@mozilla.org/browser/shell-service;1" ].getService(Ci.nsIWindowsShellService); + let winTaskbar = Cc["@mozilla.org/windows-taskbar;1"].getService( + Ci.nsIWinTaskbar + ); try { Services.telemetry.scalarSet( "os.environment.is_taskbar_pinned", - await shellService.isCurrentAppPinnedToTaskbarAsync() + await shellService.isCurrentAppPinnedToTaskbarAsync( + winTaskbar.defaultGroupId + ) + ); + Services.telemetry.scalarSet( + "os.environment.is_taskbar_pinned_private", + await shellService.isCurrentAppPinnedToTaskbarAsync( + winTaskbar.defaultPrivateGroupId + ) ); } catch (ex) { Cu.reportError(ex); @@ -2560,12 +2527,10 @@ WINTASKBAR_CONTRACTID in Cc && Cc[WINTASKBAR_CONTRACTID].getService(Ci.nsIWinTaskbar).available ) { - let temp = {}; - ChromeUtils.import( - "resource:///modules/WindowsJumpLists.jsm", - temp + const { WinTaskbarJumpList } = ChromeUtils.import( + "resource:///modules/WindowsJumpLists.jsm" ); - temp.WinTaskbarJumpList.startup(); + WinTaskbarJumpList.startup(); } }, }, @@ -2765,6 +2730,15 @@ this._collectTelemetryPiPEnabled(); }, }, + // Schedule a sync (if enabled) after we've loaded + { + task: async () => { + if (WeaveService.enabled) { + await WeaveService.whenLoaded(); + WeaveService.Weave.Service.scheduler.autoConnect(); + } + }, + }, { condition: AppConstants.platform == "win", @@ -2853,9 +2827,10 @@ }, () => { - let obj = {}; - ChromeUtils.import("resource://gre/modules/GMPInstallManager.jsm", obj); - this._gmpInstallManager = new obj.GMPInstallManager(); + let { GMPInstallManager } = ChromeUtils.import( + "resource://gre/modules/GMPInstallManager.jsm" + ); + this._gmpInstallManager = new GMPInstallManager(); // We don't really care about the results, if someone is interested they // can check the log. this._gmpInstallManager.simpleCheckAndInstall().catch(() => {}); @@ -4194,16 +4169,24 @@ Services.prefs.setIntPref("browser.migration.version", UI_VERSION); }, - _showUpgradeDialog() { - BrowserWindowTracker.getTopWindow().gDialogBox.open( - "chrome://browser/content/upgradeDialog.html" - ); + async _showUpgradeDialog() { + // TO DO Bug 1762666: Remove "chrome://browser/content/upgradeDialog.html" + const msg = await OnboardingMessageProvider.getUpgradeMessage(); + const win = BrowserWindowTracker.getTopWindow(); + const browser = win.gBrowser.selectedBrowser; + const config = { + type: "SHOW_SPOTLIGHT", + data: { + content: msg.content, + }, + }; + SpecialMessageActions.handleAction(config, browser); }, async _maybeShowDefaultBrowserPrompt() { // Highest priority is the upgrade dialog, which can include a "primary // browser" request and is limited in various ways, e.g., major upgrades. - const dialogVersion = 94; + const dialogVersion = 100; const dialogVersionPref = "browser.startup.upgradeDialog.version"; const dialogReason = await (async () => { if (!BrowserHandler.majorUpgrade) { @@ -4249,10 +4232,6 @@ // Show the upgrade dialog if allowed and remember the version. if (!dialogReason) { Services.prefs.setIntPref(dialogVersionPref, dialogVersion); - - // Show Firefox Home behind the upgrade dialog to see theme changes. - const { gBrowser } = BrowserWindowTracker.getTopWindow(); - gBrowser.selectedTab = gBrowser.addTrustedTab("about:home"); this._showUpgradeDialog(); return; } @@ -4744,6 +4723,7 @@ "privacy.trackingprotection.cryptomining.enabled": null, "privacy.annotate_channels.strict_list.enabled": null, "network.http.referer.disallowCrossSiteRelaxingDefault": null, + "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation": null, "privacy.partition.network_state.ocsp_cache": null, }, standard: { @@ -4756,6 +4736,7 @@ "privacy.trackingprotection.cryptomining.enabled": null, "privacy.annotate_channels.strict_list.enabled": null, "network.http.referer.disallowCrossSiteRelaxingDefault": null, + "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation": null, "privacy.partition.network_state.ocsp_cache": null, }, }; @@ -4835,6 +4816,16 @@ "network.http.referer.disallowCrossSiteRelaxingDefault" ] = false; break; + case "rpTop": + this.CATEGORY_PREFS[type][ + "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation" + ] = true; + break; + case "-rpTop": + this.CATEGORY_PREFS[type][ + "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation" + ] = false; + break; case "ocsp": this.CATEGORY_PREFS[type][ "privacy.partition.network_state.ocsp_cache" diff -Nru firefox-99.0.1+build1/browser/components/colorways/colorwaycloset.html firefox-100.0+build1/browser/components/colorways/colorwaycloset.html --- firefox-99.0.1+build1/browser/components/colorways/colorwaycloset.html 1970-01-01 00:00:00.000000000 +0000 +++ firefox-100.0+build1/browser/components/colorways/colorwaycloset.html 2022-04-26 05:44:41.000000000 +0000 @@ -0,0 +1,18 @@ +<!-- 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/. --> + +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <meta http-equiv="Content-Security-Policy" + content="default-src chrome:; object-src 'none'"> + <meta name="color-scheme" content="light dark"> + <link rel="stylesheet" type="text/css" + href="chrome://global/skin/in-content/common.css"> +</head> +<body> + <p>Nothing to see here. 🤔 +</body> +</html> diff -Nru firefox-99.0.1+build1/browser/components/colorways/ColorwayClosetOpener.jsm firefox-100.0+build1/browser/components/colorways/ColorwayClosetOpener.jsm --- firefox-99.0.1+build1/browser/components/colorways/ColorwayClosetOpener.jsm 1970-01-01 00:00:00.000000000 +0000 +++ firefox-100.0+build1/browser/components/colorways/ColorwayClosetOpener.jsm 2022-04-26 05:44:41.000000000 +0000 @@ -0,0 +1,22 @@ +/* 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/. */ + +"use strict"; + +var EXPORTED_SYMBOLS = ["ColorwayClosetOpener"]; + +const { BrowserWindowTracker } = ChromeUtils.import( + "resource:///modules/BrowserWindowTracker.jsm" +); + +let ColorwayClosetOpener = { + openModal: () => { + let win = BrowserWindowTracker.getTopWindow(); + let dialogBox = win.gBrowser.getTabDialogBox(win.gBrowser.selectedBrowser); + dialogBox.open("chrome://browser/content/colorwaycloset.html", { + features: "resizable=no", + sizeTo: "available", + }); + }, +}; diff -Nru firefox-99.0.1+build1/browser/components/colorways/jar.mn firefox-100.0+build1/browser/components/colorways/jar.mn --- firefox-99.0.1+build1/browser/components/colorways/jar.mn 1970-01-01 00:00:00.000000000 +0000 +++ firefox-100.0+build1/browser/components/colorways/jar.mn 2022-04-26 05:44:41.000000000 +0000 @@ -0,0 +1,6 @@ +# 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/. + +browser.jar: + content/browser/colorwaycloset.html diff -Nru firefox-99.0.1+build1/browser/components/colorways/moz.build firefox-100.0+build1/browser/components/colorways/moz.build --- firefox-99.0.1+build1/browser/components/colorways/moz.build 1970-01-01 00:00:00.000000000 +0000 +++ firefox-100.0+build1/browser/components/colorways/moz.build 2022-04-26 05:44:41.000000000 +0000 @@ -0,0 +1,12 @@ +# 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/. + +with Files("**"): + BUG_COMPONENT = ("Firefox", "Theme") + +JAR_MANIFESTS += ["jar.mn"] + +EXTRA_JS_MODULES += [ + "ColorwayClosetOpener.jsm", +] diff -Nru firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_blobUrl.js firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_blobUrl.js --- firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_blobUrl.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_blobUrl.js 2022-04-26 05:44:41.000000000 +0000 @@ -6,7 +6,7 @@ "http://mochi.test:8888/browser/browser/components/" + "contextualidentity/test/browser/empty_file.html"; -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [["privacy.userContext.enabled", true]], }); diff -Nru firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_broadcastchannel.js firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_broadcastchannel.js --- firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_broadcastchannel.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_broadcastchannel.js 2022-04-26 05:44:41.000000000 +0000 @@ -68,7 +68,7 @@ gBrowser.removeTab(sender2.tab); } -add_task(async function setup() { +add_setup(async function() { // make sure userContext is enabled. await SpecialPowers.pushPrefEnv({ set: [["privacy.userContext.enabled", true]], diff -Nru firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_count_and_remove.js firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_count_and_remove.js --- firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_count_and_remove.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_count_and_remove.js 2022-04-26 05:44:41.000000000 +0000 @@ -10,7 +10,7 @@ gBrowser.selectedTab = tab; } -add_task(async function setup() { +add_setup(async function() { // make sure userContext is enabled. await SpecialPowers.pushPrefEnv({ set: [["privacy.userContext.enabled", true]], diff -Nru firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_eme.js firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_eme.js --- firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_eme.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_eme.js 2022-04-26 05:44:41.000000000 +0000 @@ -90,7 +90,7 @@ return keyInfo; } -add_task(async function setup() { +add_setup(async function() { // Make sure userContext is enabled. await SpecialPowers.pushPrefEnv({ set: [ diff -Nru firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_favicon.js firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_favicon.js --- firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_favicon.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_favicon.js 2022-04-26 05:44:41.000000000 +0000 @@ -78,7 +78,7 @@ response.bodyOutputStream.write(gFaviconData, gFaviconData.length); } -add_task(async function setup() { +add_setup(async function() { // Make sure userContext is enabled. await SpecialPowers.pushPrefEnv({ set: [["privacy.userContext.enabled", true]], diff -Nru firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js --- firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js 2022-04-26 05:44:41.000000000 +0000 @@ -390,7 +390,7 @@ } } -add_task(async function setup() { +add_setup(async function() { // Make sure userContext is enabled. await SpecialPowers.pushPrefEnv({ set: [["privacy.userContext.enabled", true]], diff -Nru firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js --- firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js 2022-04-26 05:44:41.000000000 +0000 @@ -37,7 +37,7 @@ // Test functions. // -add_task(async function setup() { +add_setup(async function() { // Make sure userContext is enabled. await SpecialPowers.pushPrefEnv({ set: [["privacy.userContext.enabled", true]], diff -Nru firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js --- firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js 2022-04-26 05:44:41.000000000 +0000 @@ -199,7 +199,7 @@ // Test functions. // -add_task(async function setup() { +add_setup(async function() { // Make sure userContext is enabled. await SpecialPowers.pushPrefEnv({ set: [ diff -Nru firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_forgetAPI_quota_clearStoragesForPrincipal.js firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_forgetAPI_quota_clearStoragesForPrincipal.js --- firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_forgetAPI_quota_clearStoragesForPrincipal.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_forgetAPI_quota_clearStoragesForPrincipal.js 2022-04-26 05:44:41.000000000 +0000 @@ -107,7 +107,7 @@ // Test functions. // -add_task(async function setup() { +add_setup(async function() { // Make sure userContext is enabled. await SpecialPowers.pushPrefEnv({ set: [["privacy.userContext.enabled", true]], diff -Nru firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_imageCache.js firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_imageCache.js --- firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_imageCache.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_imageCache.js 2022-04-26 05:44:41.000000000 +0000 @@ -30,7 +30,7 @@ response.bodyOutputStream.write(body, body.length); } -add_task(async function setup() { +add_setup(async function() { // make sure userContext is enabled. await SpecialPowers.pushPrefEnv({ set: [["privacy.userContext.enabled", true]], diff -Nru firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_originattrs_reopenin.js firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_originattrs_reopenin.js --- firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_originattrs_reopenin.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_originattrs_reopenin.js 2022-04-26 05:44:41.000000000 +0000 @@ -34,7 +34,7 @@ } } const NUM_PAGES_OPEN_FOR_EACH_TEST_CASE = 5; -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ ["privacy.userContext.enabled", true], diff -Nru firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_restore_getCookiesWithOriginAttributes.js firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_restore_getCookiesWithOriginAttributes.js --- firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_restore_getCookiesWithOriginAttributes.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_restore_getCookiesWithOriginAttributes.js 2022-04-26 05:44:41.000000000 +0000 @@ -40,7 +40,7 @@ // Test functions. // -add_task(async function setup() { +add_setup(async function() { // Make sure userContext is enabled. await SpecialPowers.pushPrefEnv({ set: [["privacy.userContext.enabled", true]], diff -Nru firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_saveLink.js firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_saveLink.js --- firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_saveLink.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_saveLink.js 2022-04-26 05:44:41.000000000 +0000 @@ -8,7 +8,7 @@ let MockFilePicker = SpecialPowers.MockFilePicker; MockFilePicker.init(window); -add_task(async function setup() { +add_setup(async function() { info("Setting the prefs."); // make sure userContext is enabled. diff -Nru firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_serviceworkers.js firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_serviceworkers.js --- firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_serviceworkers.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_serviceworkers.js 2022-04-26 05:44:41.000000000 +0000 @@ -21,7 +21,7 @@ return tab; } -add_task(async function setup() { +add_setup(async function() { // make sure userContext is enabled. await SpecialPowers.pushPrefEnv({ set: [ diff -Nru firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_usercontextid_new_window.js firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_usercontextid_new_window.js --- firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_usercontextid_new_window.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_usercontextid_new_window.js 2022-04-26 05:44:41.000000000 +0000 @@ -52,7 +52,7 @@ return windowPromise; } -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [["privacy.userContext.enabled", true]], }); diff -Nru firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_usercontext.js firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_usercontext.js --- firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_usercontext.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_usercontext.js 2022-04-26 05:44:41.000000000 +0000 @@ -20,7 +20,7 @@ return tab; } -add_task(async function setup() { +add_setup(async function() { // make sure userContext is enabled. await SpecialPowers.pushPrefEnv({ set: [ diff -Nru firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_windowName.js firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_windowName.js --- firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_windowName.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_windowName.js 2022-04-26 05:44:41.000000000 +0000 @@ -7,7 +7,7 @@ "http://mochi.test:8888/browser/browser/components/" + "contextualidentity/test/browser/empty_file.html"; -add_task(async function setup() { +add_setup(async function() { // make sure userContext is enabled. await SpecialPowers.pushPrefEnv({ set: [ diff -Nru firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_windowOpen.js firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_windowOpen.js --- firefox-99.0.1+build1/browser/components/contextualidentity/test/browser/browser_windowOpen.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/contextualidentity/test/browser/browser_windowOpen.js 2022-04-26 05:44:41.000000000 +0000 @@ -7,7 +7,7 @@ "http://mochi.test:8888/browser/browser/components/" + "contextualidentity/test/browser/empty_file.html"; -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ ["privacy.userContext.enabled", true], diff -Nru firefox-99.0.1+build1/browser/components/customizableui/CustomizableUI.jsm firefox-100.0+build1/browser/components/customizableui/CustomizableUI.jsm --- firefox-99.0.1+build1/browser/components/customizableui/CustomizableUI.jsm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/customizableui/CustomizableUI.jsm 2022-04-26 05:44:41.000000000 +0000 @@ -194,13 +194,12 @@ ); XPCOMUtils.defineLazyGetter(this, "log", () => { - let scope = {}; - ChromeUtils.import("resource://gre/modules/Console.jsm", scope); + let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm"); let consoleOptions = { maxLogLevel: gDebuggingEnabled ? "all" : "log", prefix: "CustomizableUI", }; - return new scope.ConsoleAPI(consoleOptions); + return new ConsoleAPI(consoleOptions); }); var CustomizableUIInternal = { diff -Nru firefox-99.0.1+build1/browser/components/customizableui/CustomizableWidgets.jsm firefox-100.0+build1/browser/components/customizableui/CustomizableWidgets.jsm --- firefox-99.0.1+build1/browser/components/customizableui/CustomizableWidgets.jsm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/customizableui/CustomizableWidgets.jsm 2022-04-26 05:44:41.000000000 +0000 @@ -36,14 +36,13 @@ const kPrefScreenshots = "extensions.screenshots.disabled"; XPCOMUtils.defineLazyGetter(this, "log", () => { - let scope = {}; - ChromeUtils.import("resource://gre/modules/Console.jsm", scope); + let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm"); let debug = Services.prefs.getBoolPref(kPrefCustomizationDebug, false); let consoleOptions = { maxLogLevel: debug ? "all" : "log", prefix: "CustomizableWidgets", }; - return new scope.ConsoleAPI(consoleOptions); + return new ConsoleAPI(consoleOptions); }); XPCOMUtils.defineLazyPreferenceGetter( diff -Nru firefox-99.0.1+build1/browser/components/customizableui/CustomizeMode.jsm firefox-100.0+build1/browser/components/customizableui/CustomizeMode.jsm --- firefox-99.0.1+build1/browser/components/customizableui/CustomizeMode.jsm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/customizableui/CustomizeMode.jsm 2022-04-26 05:44:41.000000000 +0000 @@ -74,14 +74,13 @@ let gDebug; XPCOMUtils.defineLazyGetter(this, "log", () => { - let scope = {}; - ChromeUtils.import("resource://gre/modules/Console.jsm", scope); + let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm"); gDebug = Services.prefs.getBoolPref(kPrefCustomizationDebug, false); let consoleOptions = { maxLogLevel: gDebug ? "all" : "log", prefix: "CustomizeMode", }; - return new scope.ConsoleAPI(consoleOptions); + return new ConsoleAPI(consoleOptions); }); var gDraggingInToolbars; diff -Nru firefox-99.0.1+build1/browser/components/customizableui/test/browser_989751_subviewbutton_class.js firefox-100.0+build1/browser/components/customizableui/test/browser_989751_subviewbutton_class.js --- firefox-99.0.1+build1/browser/components/customizableui/test/browser_989751_subviewbutton_class.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/customizableui/test/browser_989751_subviewbutton_class.js 2022-04-26 05:44:41.000000000 +0000 @@ -19,10 +19,9 @@ return async function() { // Initialize DevTools before starting the test in order to create menuitems in // menuWebDeveloperPopup. - ChromeUtils.import( - "resource://devtools/shared/loader/Loader.jsm", - {} - ).require("devtools/client/framework/devtools-browser"); + ChromeUtils.import("resource://devtools/shared/loader/Loader.jsm").require( + "devtools/client/framework/devtools-browser" + ); info( "Checking for items without the subviewbutton class in " + diff -Nru firefox-99.0.1+build1/browser/components/customizableui/test/browser_PanelMultiView_focus.js firefox-100.0+build1/browser/components/customizableui/test/browser_PanelMultiView_focus.js --- firefox-99.0.1+build1/browser/components/customizableui/test/browser_PanelMultiView_focus.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/customizableui/test/browser_PanelMultiView_focus.js 2022-04-26 05:44:41.000000000 +0000 @@ -20,7 +20,7 @@ let gSubView; let gSubButton; -add_task(async function setup() { +add_setup(async function() { let navBar = document.getElementById("nav-bar"); gAnchor = document.createXULElement("toolbarbutton"); // Must be focusable in order for key presses to work. diff -Nru firefox-99.0.1+build1/browser/components/customizableui/test/browser_PanelMultiView_keyboard.js firefox-100.0+build1/browser/components/customizableui/test/browser_PanelMultiView_keyboard.js --- firefox-99.0.1+build1/browser/components/customizableui/test/browser_PanelMultiView_keyboard.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/customizableui/test/browser_PanelMultiView_keyboard.js 2022-04-26 05:44:41.000000000 +0000 @@ -70,7 +70,7 @@ ok(true, aFocus.id + " focused after " + aKey + " pressed"); } -add_task(async function setup() { +add_setup(async function() { // This shouldn't be necessary - but it is, because we use same-process frames. // https://bugzilla.mozilla.org/show_bug.cgi?id=1565276 covers improving this. await SpecialPowers.pushPrefEnv({ diff -Nru firefox-99.0.1+build1/browser/components/customizableui/test/browser_remote_tabs_button.js firefox-100.0+build1/browser/components/customizableui/test/browser_remote_tabs_button.js --- firefox-99.0.1+build1/browser/components/customizableui/test/browser_remote_tabs_button.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/customizableui/test/browser_remote_tabs_button.js 2022-04-26 05:44:41.000000000 +0000 @@ -4,9 +4,7 @@ */ "use strict"; -let syncService = {}; -ChromeUtils.import("resource://services-sync/service.js", syncService); -const service = syncService.Service; +let { Service } = ChromeUtils.import("resource://services-sync/service.js"); const { UIState } = ChromeUtils.import("resource://services-sync/UIState.jsm"); let getState; @@ -80,7 +78,7 @@ email: "user@mozilla.com", }); - service.sync = mocked_sync; + Service.sync = mocked_sync; } function mocked_sync() { @@ -89,10 +87,10 @@ function restoreValues() { UIState.get = getState; - service.sync = originalSync; + Service.sync = originalSync; } function storeInitialValues() { getState = UIState.get; - originalSync = service.sync; + originalSync = Service.sync; } diff -Nru firefox-99.0.1+build1/browser/components/customizableui/test/browser_synced_tabs_menu.js firefox-100.0+build1/browser/components/customizableui/test/browser_synced_tabs_menu.js --- firefox-99.0.1+build1/browser/components/customizableui/test/browser_synced_tabs_menu.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/customizableui/test/browser_synced_tabs_menu.js 2022-04-26 05:44:41.000000000 +0000 @@ -55,7 +55,7 @@ hasSyncedThisSession: false, }; -add_task(async function setup() { +add_setup(async function() { const getSignedInUser = FxAccounts.config.getSignedInUser; FxAccounts.config.getSignedInUser = async () => Promise.resolve({ uid: "uid", email: "foo@bar.com" }); diff -Nru firefox-99.0.1+build1/browser/components/customizableui/test/head.js firefox-100.0+build1/browser/components/customizableui/test/head.js --- firefox-99.0.1+build1/browser/components/customizableui/test/head.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/customizableui/test/head.js 2022-04-26 05:44:41.000000000 +0000 @@ -4,15 +4,12 @@ "use strict"; -// Avoid leaks by using tmp for imports... -var tmp = {}; -ChromeUtils.import("resource:///modules/CustomizableUI.jsm", tmp); -ChromeUtils.import("resource://gre/modules/AppConstants.jsm", tmp); -ChromeUtils.import( - "resource://testing-common/CustomizableUITestUtils.jsm", - tmp -); -var { CustomizableUI, AppConstants, CustomizableUITestUtils } = tmp; +XPCOMUtils.defineLazyModuleGetters(this, { + AppConstants: "resource://gre/modules/AppConstants.jsm", + CustomizableUI: "resource:///modules/CustomizableUI.jsm", + CustomizableUITestUtils: + "resource://testing-common/CustomizableUITestUtils.jsm", +}); var EventUtils = {}; Services.scriptloader.loadSubScript( diff -Nru firefox-99.0.1+build1/browser/components/distribution.js firefox-100.0+build1/browser/components/distribution.js --- firefox-99.0.1+build1/browser/components/distribution.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/distribution.js 2022-04-26 05:44:41.000000000 +0000 @@ -359,10 +359,9 @@ return; } - let ProfileAge = ChromeUtils.import( - "resource://gre/modules/ProfileAge.jsm", - {} - ).ProfileAge; + let { ProfileAge } = ChromeUtils.import( + "resource://gre/modules/ProfileAge.jsm" + ); let profileAge = await ProfileAge(); let resetDate = await profileAge.reset; diff -Nru firefox-99.0.1+build1/browser/components/doh/test/unit/head.js firefox-100.0+build1/browser/components/doh/test/unit/head.js --- firefox-99.0.1+build1/browser/components/doh/test/unit/head.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/doh/test/unit/head.js 2022-04-26 05:44:41.000000000 +0000 @@ -57,8 +57,7 @@ // Set to allow the cert presented by our H2 server do_get_profile(); - Services.prefs.setBoolPref("network.http.spdy.enabled", true); - Services.prefs.setBoolPref("network.http.spdy.enabled.http2", true); + Services.prefs.setBoolPref("network.http.http2.enabled", true); // use the h2 server as DOH provider trrServer1 = `https://foo.example.com:${h2Port}/doh?responseIP=1.1.1.1`; @@ -98,8 +97,7 @@ Services.telemetry.canRecordExtended = true; registerCleanupFunction(() => { - Services.prefs.clearUserPref("network.http.spdy.enabled"); - Services.prefs.clearUserPref("network.http.spdy.enabled.http2"); + Services.prefs.clearUserPref("network.http.http2.enabled"); Services.prefs.clearUserPref("network.dns.native-is-localhost"); Services.telemetry.canRecordExtended = oldCanRecord; diff -Nru firefox-99.0.1+build1/browser/components/doh/TRRPerformance.jsm firefox-100.0+build1/browser/components/doh/TRRPerformance.jsm --- firefox-99.0.1+build1/browser/components/doh/TRRPerformance.jsm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/doh/TRRPerformance.jsm 2022-04-26 05:44:41.000000000 +0000 @@ -114,7 +114,7 @@ this.usedDomain, Ci.nsIDNSService.RESOLVE_TYPE_DEFAULT, Ci.nsIDNSService.RESOLVE_BYPASS_CACHE, - gDNSService.newTRRResolverInfo(this.trrServer), + gDNSService.newAdditionalInfo(this.trrServer, -1), this, Services.tm.currentThread, {} diff -Nru firefox-99.0.1+build1/browser/components/downloads/content/downloads.js firefox-100.0+build1/browser/components/downloads/content/downloads.js --- firefox-99.0.1+build1/browser/components/downloads/content/downloads.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/downloads/content/downloads.js 2022-04-26 05:44:41.000000000 +0000 @@ -1135,10 +1135,6 @@ let partFile = new FileUtils.File(this.download.target.partFilePath); return partFile.exists(); } - case "downloadsCmd_deleteFile": { - let { target } = this.download; - return target.exists || target.partFileExists; - } case "downloadsCmd_copyLocation": return !!this.download.source?.url; case "cmd_delete": diff -Nru firefox-99.0.1+build1/browser/components/downloads/DownloadsCommon.jsm firefox-100.0+build1/browser/components/downloads/DownloadsCommon.jsm --- firefox-99.0.1+build1/browser/components/downloads/DownloadsCommon.jsm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/downloads/DownloadsCommon.jsm 2022-04-26 05:44:41.000000000 +0000 @@ -360,8 +360,6 @@ * 2 - Remove the download from both session list and history. */ async deleteDownloadFiles(download, clearHistory = 0) { - let { succeeded } = download; - let { path } = download.target; if (clearHistory > 1) { try { await PlacesUtils.history.remove(download.source.url); @@ -373,18 +371,10 @@ let list = await Downloads.getList(Downloads.ALL); await list.remove(download); } - if (succeeded) { - // Temp files are made "read-only" by DownloadIntegration.downloadDone, so - // reset the permission bits to read/write. This won't be necessary after - // bug 1733587 since Downloads won't ever be temporary. - await IOUtils.setPermissions(path, 0o660); - await IOUtils.remove(path, { ignoreAbsent: true }); + await download.manuallyRemoveData(); + if (clearHistory < 2) { + DownloadHistory.updateMetaData(download).catch(Cu.reportError); } - // For clearHistory of 0 or 1, we need to remove part data before refresh. - // So we'll do it in this order rather than download.finalize(true) - await download.removePartialData(); - await download.refresh(); - await download.finalize(); }, /** diff -Nru firefox-99.0.1+build1/browser/components/downloads/DownloadsViewUI.jsm firefox-100.0+build1/browser/components/downloads/DownloadsViewUI.jsm --- firefox-99.0.1+build1/browser/components/downloads/DownloadsViewUI.jsm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/downloads/DownloadsViewUI.jsm 2022-04-26 05:44:41.000000000 +0000 @@ -230,9 +230,9 @@ : {}; // Hide the "Delete" item if there's no file data to delete. - contextMenu.querySelector(".downloadDeleteFileMenuItem").hidden = !( - download.target?.exists || download.target?.partFileExists - ); + contextMenu.querySelector(".downloadDeleteFileMenuItem").hidden = + download.deleted || + !(download.target?.exists || download.target?.partFileExists); // Hide the "Go To Download Page" item if there's no referrer. Ideally the // Downloads API will require a referrer (see bug 1723712) to create a @@ -739,7 +739,9 @@ // The download is not in progress, so we update the user interface based // on other properties. The order in which we check the properties of the // Download object is the same used by stateOfDownload. - if (this.download.succeeded) { + if (this.download.deleted) { + this.showDeletedOrMissing(); + } else if (this.download.succeeded) { DownloadsCommon.log( "_updateStateInner, target exists? ", this.download.target.path, @@ -781,10 +783,7 @@ } else { // This is a completed download, but the target file does not exist // anymore, so the main action of opening the file is unavailable. - this.element.removeAttribute("exists"); - let label = DownloadsCommon.strings.fileMovedOrMissing; - this.showStatusWithDetails(label, label); - this.hideButton(); + this.showDeletedOrMissing(); } } else if (this.download.error) { if (this.download.error.becauseBlockedByParentalControls) { @@ -939,6 +938,16 @@ ); }, + showDeletedOrMissing() { + this.element.removeAttribute("exists"); + let label = + DownloadsCommon.strings[ + this.download.deleted ? "fileDeleted" : "fileMovedOrMissing" + ]; + this.showStatusWithDetails(label, label); + this.hideButton(); + }, + /** * Shows the appropriate unblock dialog based on the verdict, and executes the * action selected by the user in the dialog, which may involve unblocking, @@ -1044,7 +1053,9 @@ case "downloadsCmd_show": case "downloadsCmd_deleteFile": let { target } = this.download; - return target.exists || target.partFileExists; + return ( + !this.download.deleted && (target.exists || target.partFileExists) + ); case "downloadsCmd_delete": case "cmd_delete": @@ -1075,7 +1086,10 @@ downloadsCmd_cancel() { // This is the correct way to avoid race conditions when cancelling. this.download.cancel().catch(() => {}); - this.download.removePartialData().catch(Cu.reportError); + this.download + .removePartialData() + .catch(Cu.reportError) + .finally(() => this.download.target.refresh()); }, downloadsCmd_confirmBlock() { diff -Nru firefox-99.0.1+build1/browser/components/downloads/test/browser/browser_download_overwrite.js firefox-100.0+build1/browser/components/downloads/test/browser/browser_download_overwrite.js --- firefox-99.0.1+build1/browser/components/downloads/test/browser/browser_download_overwrite.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/downloads/test/browser/browser_download_overwrite.js 2022-04-26 05:44:41.000000000 +0000 @@ -17,7 +17,7 @@ this ); -add_task(async function setup() { +add_setup(async function() { // head.js has helpers that write to a nice unique file we can use. await createDownloadedFile(gTestTargetFile.path, "Hello.\n"); ok(gTestTargetFile.exists(), "We created a test file."); diff -Nru firefox-99.0.1+build1/browser/components/downloads/test/browser/browser_downloads_context_menu_always_open_similar_files.js firefox-100.0+build1/browser/components/downloads/test/browser/browser_downloads_context_menu_always_open_similar_files.js --- firefox-99.0.1+build1/browser/components/downloads/test/browser/browser_downloads_context_menu_always_open_similar_files.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/downloads/test/browser/browser_downloads_context_menu_always_open_similar_files.js 2022-04-26 05:44:41.000000000 +0000 @@ -39,10 +39,7 @@ info("Setting path for download file"); // Set target for download file. Otherwise, file will default to .file instead of txt // when we prepare our downloads - particularly in task_addDownloads(). - let targetPath = PathUtils.join( - await PathUtils.getTempDir(), - "downloaded.txt" - ); + let targetPath = PathUtils.join(PathUtils.tempDir, "downloaded.txt"); let target = new FileUtils.File(targetPath); target.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE); downloads.push({ @@ -60,7 +57,7 @@ info("Download target exists? " + download.target.exists); } -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [["browser.download.improvements_to_download_panel", true]], }); diff -Nru firefox-99.0.1+build1/browser/components/downloads/test/browser/browser_downloads_context_menu_delete_file.js firefox-100.0+build1/browser/components/downloads/test/browser/browser_downloads_context_menu_delete_file.js --- firefox-99.0.1+build1/browser/components/downloads/test/browser/browser_downloads_context_menu_delete_file.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/downloads/test/browser/browser_downloads_context_menu_delete_file.js 2022-04-26 05:44:41.000000000 +0000 @@ -114,6 +114,13 @@ "There should be a download in the list" ); + ok( + !DownloadsView.richListBox.selectedItem._shell.isCommandEnabled( + "downloadsCmd_deleteFile" + ), + "Delete file command should be disabled" + ); + DownloadsPanel.hidePanel(); await hiddenPromise; }); diff -Nru firefox-99.0.1+build1/browser/components/downloads/test/browser/browser_download_spam_protection.js firefox-100.0+build1/browser/components/downloads/test/browser/browser_download_spam_protection.js --- firefox-99.0.1+build1/browser/components/downloads/test/browser/browser_download_spam_protection.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/downloads/test/browser/browser_download_spam_protection.js 2022-04-26 05:44:41.000000000 +0000 @@ -26,7 +26,7 @@ TEST_URI ); -add_task(async function setup() { +add_setup(async function() { // Create temp directory let time = new Date().getTime(); let tempDir = Services.dirsvc.get("TmpD", Ci.nsIFile); diff -Nru firefox-99.0.1+build1/browser/components/downloads/test/browser/browser_downloads_panel_context_menu.js firefox-100.0+build1/browser/components/downloads/test/browser/browser_downloads_panel_context_menu.js --- firefox-99.0.1+build1/browser/components/downloads/test/browser/browser_downloads_panel_context_menu.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/downloads/test/browser/browser_downloads_panel_context_menu.js 2022-04-26 05:44:41.000000000 +0000 @@ -282,22 +282,22 @@ const TestCasesDeletedFile = [ { - name: "Deleted PDF download with improvements pref enabled", + name: "Download with file deleted and improvements pref enabled", prefEnabled: true, - deleted: true, downloads: [ { state: DownloadsCommon.DOWNLOAD_FINISHED, - contentType: "application/pdf", + contentType: "text/plain", target: {}, source: { referrerInfo: exampleRefInfo, }, + deleted: true, }, ], expected: { menu: [ - MENU_ITEMS.alwaysOpenInSystemViewer, + MENU_ITEMS.alwaysOpenSimilarFiles, MENU_ITEMS.openReferrer, MENU_ITEMS.copyLocation, MENU_ITEMS.separator, @@ -376,6 +376,12 @@ "Created downloaded unknownExtension file at:" + TestFiles.unknownExtension.path ); + TestFiles.nonexistentFile = new FileUtils.File( + PathUtils.join(gDownloadDir, "nonexistent") + ); + info( + "Created nonexistent downloaded file at:" + TestFiles.nonexistentFile.path + ); }); // register the tests @@ -441,12 +447,26 @@ add_task(tmp[testData.name]); } +for (let testData of TestCasesMultipleFiles) { + if (testData.skip) { + info("Skipping test:" + testData.name); + continue; + } + // use the 'name' property of each test case as the test function name + // so we get useful logs + let tmp = { + async [testData.name]() { + await testDownloadContextMenu(testData); + }, + }; + add_task(tmp[testData.name]); +} + async function testDownloadContextMenu({ overrideExtension = null, downloads = [], expected, prefEnabled, - deleted, itemIndex = 0, }) { info( @@ -459,19 +479,9 @@ // prepare downloads await prepareDownloads(downloads, overrideExtension); let downloadList = await Downloads.getList(Downloads.PUBLIC); - let all = await downloadList.getAll(); - for (let dl of all) { - info("Download succeeded? " + dl.succeeded); - if (deleted) { - let { path } = dl.target; - await IOUtils.setPermissions(path, 0o660); - await IOUtils.remove(path, { ignoreAbsent: true }); - await dl.removePartialData(); - await dl.refresh(); - await dl.finalize(); - } - info("Download target exists? " + dl.target.exists); - } + let download = (await downloadList.getAll())[itemIndex]; + info("Download succeeded? " + download.succeeded); + info("Download target exists? " + download.target.exists); // open panel await task_openPanel(); @@ -564,6 +574,10 @@ if (props.state !== DownloadsCommon.DOWNLOAD_FINISHED) { continue; } + if (props.deleted) { + props.target = TestFiles.nonexistentFile; + continue; + } switch (props.contentType) { case "application/pdf": props.target = TestFiles.pdf; diff -Nru firefox-99.0.1+build1/browser/components/downloads/test/browser/browser_downloads_panel_disable_items.js firefox-100.0+build1/browser/components/downloads/test/browser/browser_downloads_panel_disable_items.js --- firefox-99.0.1+build1/browser/components/downloads/test/browser/browser_downloads_panel_disable_items.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/downloads/test/browser/browser_downloads_panel_disable_items.js 2022-04-26 05:44:41.000000000 +0000 @@ -9,7 +9,7 @@ TEST_URI ); -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [ ["browser.download.improvements_to_download_panel", true], diff -Nru firefox-99.0.1+build1/browser/components/downloads/test/browser/browser_downloads_panel_focus.js firefox-100.0+build1/browser/components/downloads/test/browser/browser_downloads_panel_focus.js --- firefox-99.0.1+build1/browser/components/downloads/test/browser/browser_downloads_panel_focus.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/downloads/test/browser/browser_downloads_panel_focus.js 2022-04-26 05:44:41.000000000 +0000 @@ -3,7 +3,7 @@ "use strict"; -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [["browser.download.improvements_to_download_panel", true]], }); diff -Nru firefox-99.0.1+build1/browser/components/downloads/test/browser/browser.ini firefox-100.0+build1/browser/components/downloads/test/browser/browser.ini --- firefox-99.0.1+build1/browser/components/downloads/test/browser/browser.ini 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/downloads/test/browser/browser.ini 2022-04-26 05:44:41.000000000 +0000 @@ -28,7 +28,8 @@ [browser_library_clearall.js] [browser_download_opens_on_click.js] [browser_download_spam_protection.js] -skip-if = os == "linux" && bits == 64 && !debug # bug 1743263 +skip-if = + os == "linux" && bits == 64 # bug 1743263 & Bug 1742678 support-files = test_spammy_page.html [browser_download_is_clickable.js] [browser_downloads_keynav.js] diff -Nru firefox-99.0.1+build1/browser/components/downloads/test/browser/browser_library_clearall.js firefox-100.0+build1/browser/components/downloads/test/browser/browser_library_clearall.js --- firefox-99.0.1+build1/browser/components/downloads/test/browser/browser_library_clearall.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/downloads/test/browser/browser_library_clearall.js 2022-04-26 05:44:41.000000000 +0000 @@ -78,7 +78,7 @@ ); } -add_task(async function setup() { +add_setup(async function() { // Ensure that state is reset in case previous tests didn't finish. await task_resetState(); diff -Nru firefox-99.0.1+build1/browser/components/downloads/test/browser/browser_library_select_all.js firefox-100.0+build1/browser/components/downloads/test/browser/browser_library_select_all.js --- firefox-99.0.1+build1/browser/components/downloads/test/browser/browser_library_select_all.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/downloads/test/browser/browser_library_select_all.js 2022-04-26 05:44:41.000000000 +0000 @@ -3,7 +3,7 @@ let gDownloadDir; -add_task(async function setup() { +add_setup(async function() { await task_resetState(); if (!gDownloadDir) { diff -Nru firefox-99.0.1+build1/browser/components/downloads/test/browser/head.js firefox-100.0+build1/browser/components/downloads/test/browser/head.js --- firefox-99.0.1+build1/browser/components/downloads/test/browser/head.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/downloads/test/browser/head.js 2022-04-26 05:44:41.000000000 +0000 @@ -235,6 +235,7 @@ canceled: item.state == DownloadsCommon.DOWNLOAD_CANCELED || item.state == DownloadsCommon.DOWNLOAD_PAUSED, + deleted: item.deleted ?? false, error: item.state == DownloadsCommon.DOWNLOAD_FAILED ? new Error("Failed.") @@ -263,9 +264,8 @@ } async function setDownloadDir() { - let tmpDir = await PathUtils.getTempDir(); - tmpDir = PathUtils.join( - tmpDir, + let tmpDir = PathUtils.join( + PathUtils.tempDir, "testsavedir" + Math.floor(Math.random() * 2 ** 32) ); // Create this dir if it doesn't exist (ignores existing dirs) diff -Nru firefox-99.0.1+build1/browser/components/downloads/test/unit/head.js firefox-100.0+build1/browser/components/downloads/test/unit/head.js --- firefox-99.0.1+build1/browser/components/downloads/test/unit/head.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/downloads/test/unit/head.js 2022-04-26 05:44:41.000000000 +0000 @@ -81,7 +81,7 @@ run_next_test(); } -add_task(async function test_common_initialize() { +add_setup(async function test_common_initialize() { gDownloadDir = await setDownloadDir(); Services.prefs.setCharPref("browser.download.loglevel", "Debug"); }); diff -Nru firefox-99.0.1+build1/browser/components/enterprisepolicies/Policies.jsm firefox-100.0+build1/browser/components/enterprisepolicies/Policies.jsm --- firefox-99.0.1+build1/browser/components/enterprisepolicies/Policies.jsm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/enterprisepolicies/Policies.jsm 2022-04-26 05:44:41.000000000 +0000 @@ -584,7 +584,6 @@ setAndLockPref("devtools.chrome.enabled", false); manager.disallowFeature("devtools"); - blockAboutPage(manager, "about:devtools"); blockAboutPage(manager, "about:debugging"); blockAboutPage(manager, "about:devtools-toolbox"); blockAboutPage(manager, "about:profiling"); diff -Nru firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_block_about.js firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_block_about.js --- firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_block_about.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_block_about.js 2022-04-26 05:44:41.000000000 +0000 @@ -33,12 +33,7 @@ policies: { DisableDeveloperTools: true, }, - urls: [ - "about:devtools", - "about:debugging", - "about:devtools-toolbox", - "about:profiling", - ], + urls: ["about:debugging", "about:devtools-toolbox", "about:profiling"], }, { policies: { diff -Nru firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_block_set_desktop_background.js firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_block_set_desktop_background.js --- firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_block_set_desktop_background.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_block_set_desktop_background.js 2022-04-26 05:44:41.000000000 +0000 @@ -2,7 +2,7 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; -add_task(async function setup() { +add_setup(async function() { await setupPolicyEngineWithJson({ policies: { DisableSetDesktopBackground: true, diff -Nru firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_popup_blocker.js firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_popup_blocker.js --- firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_popup_blocker.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_popup_blocker.js 2022-04-26 05:44:41.000000000 +0000 @@ -7,7 +7,7 @@ } let ORIGINAL_PREF_VALUE = undefined; -add_task(async function setup() { +add_setup(async function() { // It seems that this pref is given a special testing value for some reason. // Unset that value for this test, but save the old value if (Services.prefs.prefHasUserValue("dom.disable_open_during_load")) { diff -Nru firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_privatebrowsing.js firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_privatebrowsing.js --- firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_privatebrowsing.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_privatebrowsing.js 2022-04-26 05:44:41.000000000 +0000 @@ -7,7 +7,7 @@ "resource://gre/modules/PrivateBrowsingUtils.jsm" ); -add_task(async function setup() { +add_setup(async function() { await setupPolicyEngineWithJson({ policies: { DisablePrivateBrowsing: true, diff -Nru firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_profile_reset.js firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_profile_reset.js --- firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_profile_reset.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_profile_reset.js 2022-04-26 05:44:41.000000000 +0000 @@ -8,7 +8,7 @@ // For this test to work properly, this profile actually needs to be // "reset-able", which requires that it be recognized by the profile service -add_task(async function setup() { +add_setup(async function() { let profileDirectory = Services.dirsvc.get("ProfD", Ci.nsIFile); let profileName = profileDirectory.leafName; let profileService = Cc["@mozilla.org/toolkit/profile-service;1"].getService( diff -Nru firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_safemode.js firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_safemode.js --- firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_safemode.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_safemode.js 2022-04-26 05:44:41.000000000 +0000 @@ -2,7 +2,7 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; -add_task(async function setup() { +add_setup(async function() { await setupPolicyEngineWithJson({ policies: { DisableSafeMode: true, diff -Nru firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_display_bookmarks.js firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_display_bookmarks.js --- firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_display_bookmarks.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_display_bookmarks.js 2022-04-26 05:44:41.000000000 +0000 @@ -3,7 +3,7 @@ "use strict"; -add_task(async function setup() { +add_setup(async function() { await setupPolicyEngineWithJson({ policies: { DisplayBookmarksToolbar: true, diff -Nru firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_extensionsettings.js firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_extensionsettings.js --- firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_extensionsettings.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_extensionsettings.js 2022-04-26 05:44:41.000000000 +0000 @@ -33,6 +33,19 @@ }); } +add_setup(async function setupTestEnvironment() { + // Once InstallTrigger is removed, the tests targeting InstallTrigger should + // be removed or adapted to don't use InstallTrigger. + await SpecialPowers.pushPrefEnv({ + set: [ + ["extensions.InstallTrigger.enabled", true], + ["extensions.InstallTriggerImpl.enabled", true], + // Relax the user input requirements while running this test. + ["xpinstall.userActivation.required", false], + ], + }); +}); + add_task(async function test_install_source_blocked_link() { await setupPolicyEngineWithJson({ policies: { diff -Nru firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_searchbar.js firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_searchbar.js --- firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_searchbar.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_searchbar.js 2022-04-26 05:44:41.000000000 +0000 @@ -3,7 +3,7 @@ "use strict"; -add_task(async function setup() { +add_setup(async function() { await setupPolicyEngineWithJson({ policies: { SearchBar: "separate", @@ -23,15 +23,12 @@ await BrowserTestUtils.closeWindow(newWin); }); -add_task(async function setup() { +add_task(async function test_menu_shown() { await setupPolicyEngineWithJson({ policies: { SearchBar: "unified", }, }); -}); - -add_task(async function test_menu_shown() { let newWin = await BrowserTestUtils.openNewBrowserWindow(); let placement = CustomizableUI.getPlacementOfWidget("search-container"); is(placement, null, "Search bar has no placement"); diff -Nru firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_set_startpage.js firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_set_startpage.js --- firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_set_startpage.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_set_startpage.js 2022-04-26 05:44:41.000000000 +0000 @@ -2,7 +2,7 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; -add_task(async function setup() { +add_setup(async function() { // browser.startup.page is set by unittest-required/user.js, // but we need the default value await SpecialPowers.pushPrefEnv({ diff -Nru firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_support_menu.js firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_support_menu.js --- firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_support_menu.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/browser_policy_support_menu.js 2022-04-26 05:44:41.000000000 +0000 @@ -2,7 +2,7 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; -add_task(async function setup() { +add_setup(async function() { await setupPolicyEngineWithJson({ policies: { SupportMenu: { diff -Nru firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/disable_developer_tools/browser_policy_disable_developer_tools.js firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/disable_developer_tools/browser_policy_disable_developer_tools.js --- firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/disable_developer_tools/browser_policy_disable_developer_tools.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/disable_developer_tools/browser_policy_disable_developer_tools.js 2022-04-26 05:44:41.000000000 +0000 @@ -27,7 +27,6 @@ "devtools dedicated disabled pref can not be updated" ); - await expectErrorPage("about:devtools"); await expectErrorPage("about:devtools-toolbox"); await expectErrorPage("about:debugging"); diff -Nru firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/extensionsettings.html firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/extensionsettings.html --- firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/extensionsettings.html 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/extensionsettings.html 2022-04-26 05:44:41.000000000 +0000 @@ -5,7 +5,12 @@ <meta charset="UTF-8"> <script type="text/javascript"> function installTrigger(url) { - InstallTrigger.install({extension: url}); + try { + InstallTrigger.install({extension: url}); + } catch (err) { + dump(`Failed to execute InstallTrigger.install: ${err}\n`); + } + return false; } </script> </head> @@ -14,7 +19,7 @@ <a id="policytest" href="policytest_v0.1.xpi">policytest@mozilla.com</a> </p> <p> -<a id="policytest_installtrigger" onclick="installTrigger(this.href);return false;" href="policytest_v0.1.xpi">policytest@mozilla.com</a> +<a id="policytest_installtrigger" onclick="return installTrigger(this.href);" href="policytest_v0.1.xpi">policytest@mozilla.com</a> </p> <p> <a id="policytest_otherdomain" href="http://example.org:80/browser/browser/components/enterprisepolicies/tests/browser/policytest_v0.1.xpi">policytest@mozilla.com</a> diff -Nru firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/head.js firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/head.js --- firefox-99.0.1+build1/browser/components/enterprisepolicies/tests/browser/head.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/enterprisepolicies/tests/browser/head.js 2022-04-26 05:44:41.000000000 +0000 @@ -172,7 +172,7 @@ await BrowserTestUtils.removeTab(tab); } -add_task(async function policies_headjs_startWithCleanSlate() { +add_setup(async function policies_headjs_startWithCleanSlate() { if (Services.policies.status != Ci.nsIEnterprisePolicies.INACTIVE) { await setupPolicyEngineWithJson(""); } diff -Nru firefox-99.0.1+build1/browser/components/extensions/parent/ext-bookmarks.js firefox-100.0+build1/browser/components/extensions/parent/ext-bookmarks.js --- firefox-99.0.1+build1/browser/components/extensions/parent/ext-bookmarks.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/parent/ext-bookmarks.js 2022-04-26 05:44:41.000000000 +0000 @@ -156,6 +156,7 @@ index: event.index, type: BOOKMARKS_TYPES_TO_API_TYPES_MAP.get(event.itemType), url: getUrl(event.itemType, event.url), + title: event.title, }; this.emit("removed", { @@ -231,7 +232,81 @@ } }; -this.bookmarks = class extends ExtensionAPI { +this.bookmarks = class extends ExtensionAPIPersistent { + PERSISTENT_EVENTS = { + onCreated({ fire }) { + let listener = (event, bookmark) => { + fire.sync(bookmark.id, bookmark); + }; + + observer.on("created", listener); + incrementListeners(); + return { + unregister() { + observer.off("created", listener); + decrementListeners(); + }, + convert(_fire) { + fire = _fire; + }, + }; + }, + + onRemoved({ fire }) { + let listener = (event, data) => { + fire.sync(data.guid, data.info); + }; + + observer.on("removed", listener); + incrementListeners(); + return { + unregister() { + observer.off("removed", listener); + decrementListeners(); + }, + convert(_fire) { + fire = _fire; + }, + }; + }, + + onChanged({ fire }) { + let listener = (event, data) => { + fire.sync(data.guid, data.info); + }; + + observer.on("changed", listener); + incrementListeners(); + return { + unregister() { + observer.off("changed", listener); + decrementListeners(); + }, + convert(_fire) { + fire = _fire; + }, + }; + }, + + onMoved({ fire }) { + let listener = (event, data) => { + fire.sync(data.guid, data.info); + }; + + observer.on("moved", listener); + incrementListeners(); + return { + unregister() { + observer.off("moved", listener); + decrementListeners(); + }, + convert(_fire) { + fire = _fire; + }, + }; + }, + }; + getAPI(context) { return { bookmarks: { @@ -409,70 +484,30 @@ onCreated: new EventManager({ context, - name: "bookmarks.onCreated", - register: fire => { - let listener = (event, bookmark) => { - fire.sync(bookmark.id, bookmark); - }; - - observer.on("created", listener); - incrementListeners(); - return () => { - observer.off("created", listener); - decrementListeners(); - }; - }, + module: "bookmarks", + event: "onCreated", + extensionApi: this, }).api(), onRemoved: new EventManager({ context, - name: "bookmarks.onRemoved", - register: fire => { - let listener = (event, data) => { - fire.sync(data.guid, data.info); - }; - - observer.on("removed", listener); - incrementListeners(); - return () => { - observer.off("removed", listener); - decrementListeners(); - }; - }, + module: "bookmarks", + event: "onRemoved", + extensionApi: this, }).api(), onChanged: new EventManager({ context, - name: "bookmarks.onChanged", - register: fire => { - let listener = (event, data) => { - fire.sync(data.guid, data.info); - }; - - observer.on("changed", listener); - incrementListeners(); - return () => { - observer.off("changed", listener); - decrementListeners(); - }; - }, + module: "bookmarks", + event: "onChanged", + extensionApi: this, }).api(), onMoved: new EventManager({ context, - name: "bookmarks.onMoved", - register: fire => { - let listener = (event, data) => { - fire.sync(data.guid, data.info); - }; - - observer.on("moved", listener); - incrementListeners(); - return () => { - observer.off("moved", listener); - decrementListeners(); - }; - }, + module: "bookmarks", + event: "onMoved", + extensionApi: this, }).api(), }, }; diff -Nru firefox-99.0.1+build1/browser/components/extensions/parent/ext-browserAction.js firefox-100.0+build1/browser/components/extensions/parent/ext-browserAction.js --- firefox-99.0.1+build1/browser/components/extensions/parent/ext-browserAction.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/parent/ext-browserAction.js 2022-04-26 05:44:41.000000000 +0000 @@ -111,7 +111,7 @@ } } -this.browserAction = class extends ExtensionAPI { +this.browserAction = class extends ExtensionAPIPersistent { static for(extension) { return browserActionMap.get(extension); } @@ -632,9 +632,35 @@ } } + PERSISTENT_EVENTS = { + onClicked({ context, fire }) { + const { extension } = this; + const { tabManager } = extension; + async function listener(_event, tab, clickInfo) { + if (fire.wakeup) { + await fire.wakeup(); + } + // TODO: we should double-check if the tab is already being closed by the time + // the background script got started and we converted the primed listener. + context?.withPendingBrowser(tab.linkedBrowser, () => + fire.sync(tabManager.convert(tab), clickInfo) + ); + } + this.on("click", listener); + return { + unregister: () => { + this.off("click", listener); + }, + convert(newFire, extContext) { + fire = newFire; + context = extContext; + }, + }; + }, + }; + getAPI(context) { let { extension } = context; - let { tabManager } = extension; let { action } = this; let namespace = extension.manifestVersion < 3 ? "browserAction" : "action"; @@ -644,19 +670,12 @@ onClicked: new EventManager({ context, - name: `${namespace}.onClicked`, + // module name is "browserAction" because it the name used in the + // ext-browser.json, indipendently from the manifest version. + module: "browserAction", + event: "onClicked", inputHandling: true, - register: fire => { - let listener = (event, tab, clickInfo) => { - context.withPendingBrowser(tab.linkedBrowser, () => - fire.sync(tabManager.convert(tab), clickInfo) - ); - }; - this.on("click", listener); - return () => { - this.off("click", listener); - }; - }, + extensionApi: this, }).api(), openPopup: () => { diff -Nru firefox-99.0.1+build1/browser/components/extensions/parent/ext-browser.js firefox-100.0+build1/browser/components/extensions/parent/ext-browser.js --- firefox-99.0.1+build1/browser/components/extensions/parent/ext-browser.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/parent/ext-browser.js 2022-04-26 05:44:41.000000000 +0000 @@ -908,22 +908,26 @@ let entries = tabData.state ? tabData.state.entries : tabData.entries; let lastTabIndex = tabData.state ? tabData.state.index : tabData.index; - // We need to take lastTabIndex - 1 because the index in the tab data is - // 1-based rather than 0-based. - let entry = entries[lastTabIndex - 1]; - // tabData is a representation of a tab, as stored in the session data, - // and given that is not a real nativeTab, we only need to check if the extension - // has the "tabs" or host permission (because tabData represents a closed tab, - // and so we already know that it can't be the activeTab). - if ( - extension.hasPermission("tabs") || - extension.allowedOrigins.matches(entry.url) - ) { - result.url = entry.url; - result.title = entry.title; - if (tabData.image) { - result.favIconUrl = tabData.image; + // Tab may have empty history. + if (entries.length) { + // We need to take lastTabIndex - 1 because the index in the tab data is + // 1-based rather than 0-based. + let entry = entries[lastTabIndex - 1]; + + // tabData is a representation of a tab, as stored in the session data, + // and given that is not a real nativeTab, we only need to check if the extension + // has the "tabs" or host permission (because tabData represents a closed tab, + // and so we already know that it can't be the activeTab). + if ( + extension.hasPermission("tabs") || + extension.allowedOrigins.matches(entry.url) + ) { + result.url = entry.url; + result.title = entry.title; + if (tabData.image) { + result.favIconUrl = tabData.image; + } } } diff -Nru firefox-99.0.1+build1/browser/components/extensions/parent/ext-commands.js firefox-100.0+build1/browser/components/extensions/parent/ext-commands.js --- firefox-99.0.1+build1/browser/components/extensions/parent/ext-commands.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/parent/ext-commands.js 2022-04-26 05:44:41.000000000 +0000 @@ -12,7 +12,22 @@ "resource://gre/modules/ExtensionShortcuts.jsm" ); -this.commands = class extends ExtensionAPI { +this.commands = class extends ExtensionAPIPersistent { + PERSISTENT_EVENTS = { + onCommand({ fire }) { + let listener = (eventName, commandName) => { + fire.async(commandName); + }; + this.on("command", listener); + return { + unregister: () => this.off("command", listener), + convert(_fire) { + fire = _fire; + }, + }; + }, + }; + static onUninstall(extensionId) { return ExtensionShortcuts.removeCommandsFromStorage(extensionId); } @@ -39,17 +54,10 @@ reset: name => this.extension.shortcuts.resetCommand(name), onCommand: new EventManager({ context, - name: "commands.onCommand", + module: "commands", + event: "onCommand", inputHandling: true, - register: fire => { - let listener = (eventName, commandName) => { - fire.async(commandName); - }; - this.on("command", listener); - return () => { - this.off("command", listener); - }; - }, + extensionApi: this, }).api(), }, }; diff -Nru firefox-99.0.1+build1/browser/components/extensions/parent/ext-history.js firefox-100.0+build1/browser/components/extensions/parent/ext-history.js --- firefox-99.0.1+build1/browser/components/extensions/parent/ext-history.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/parent/ext-history.js 2022-04-26 05:44:41.000000000 +0000 @@ -89,7 +89,100 @@ return results; }; -this.history = class extends ExtensionAPI { +this.history = class extends ExtensionAPIPersistent { + PERSISTENT_EVENTS = { + onVisited({ fire }) { + const listener = events => { + for (const event of events) { + const visit = { + id: event.pageGuid, + url: event.url, + title: event.lastKnownTitle || "", + lastVisitTime: event.visitTime, + visitCount: event.visitCount, + typedCount: event.typedCount, + }; + fire.sync(visit); + } + }; + + PlacesUtils.observers.addListener(["page-visited"], listener); + return { + unregister() { + PlacesUtils.observers.removeListener(["page-visited"], listener); + }, + convert(_fire) { + fire = _fire; + }, + }; + }, + onVisitRemoved({ fire }) { + const listener = events => { + const removedURLs = []; + + for (const event of events) { + switch (event.type) { + case "history-cleared": { + fire.sync({ allHistory: true, urls: [] }); + break; + } + case "page-removed": { + if (!event.isPartialVisistsRemoval) { + removedURLs.push(event.url); + } + break; + } + } + } + + if (removedURLs.length) { + fire.sync({ allHistory: false, urls: removedURLs }); + } + }; + + PlacesUtils.observers.addListener( + ["history-cleared", "page-removed"], + listener + ); + return { + unregister() { + PlacesUtils.observers.removeListener( + ["history-cleared", "page-removed"], + listener + ); + }, + convert(_fire) { + fire = _fire; + }, + }; + }, + onTitleChanged({ fire }) { + const listener = events => { + for (const event of events) { + const titleChanged = { + id: event.pageGuid, + url: event.url, + title: event.title, + }; + fire.sync(titleChanged); + } + }; + + PlacesUtils.observers.addListener(["page-title-changed"], listener); + return { + unregister() { + PlacesUtils.observers.removeListener( + ["page-title-changed"], + listener + ); + }, + convert(_fire) { + fire = _fire; + }, + }; + }, + }; + getAPI(context) { return { history: { @@ -204,92 +297,23 @@ onVisited: new EventManager({ context, - name: "history.onVisited", - register: fire => { - const listener = events => { - for (const event of events) { - const visit = { - id: event.pageGuid, - url: event.url, - title: event.lastKnownTitle || "", - lastVisitTime: event.visitTime, - visitCount: event.visitCount, - typedCount: event.typedCount, - }; - fire.sync(visit); - } - }; - - PlacesUtils.observers.addListener(["page-visited"], listener); - return () => { - PlacesUtils.observers.removeListener(["page-visited"], listener); - }; - }, + module: "history", + event: "onVisited", + extensionApi: this, }).api(), onVisitRemoved: new EventManager({ context, - name: "history.onVisitRemoved", - register: fire => { - const listener = events => { - const removedURLs = []; - - for (const event of events) { - switch (event.type) { - case "history-cleared": { - fire.sync({ allHistory: true, urls: [] }); - break; - } - case "page-removed": { - if (!event.isPartialVisistsRemoval) { - removedURLs.push(event.url); - } - break; - } - } - } - - if (removedURLs.length) { - fire.sync({ allHistory: false, urls: removedURLs }); - } - }; - - PlacesUtils.observers.addListener( - ["history-cleared", "page-removed"], - listener - ); - return () => { - PlacesUtils.observers.removeListener( - ["history-cleared", "page-removed"], - listener - ); - }; - }, + module: "history", + event: "onVisitRemoved", + extensionApi: this, }).api(), onTitleChanged: new EventManager({ context, - name: "history.onTitleChanged", - register: fire => { - const listener = events => { - for (const event of events) { - const titleChanged = { - id: event.pageGuid, - url: event.url, - title: event.title, - }; - fire.sync(titleChanged); - } - }; - - PlacesUtils.observers.addListener(["page-title-changed"], listener); - return () => { - PlacesUtils.observers.removeListener( - ["page-title-changed"], - listener - ); - }; - }, + module: "history", + event: "onTitleChanged", + extensionApi: this, }).api(), }, }; diff -Nru firefox-99.0.1+build1/browser/components/extensions/parent/ext-omnibox.js firefox-100.0+build1/browser/components/extensions/parent/ext-omnibox.js --- firefox-99.0.1+build1/browser/components/extensions/parent/ext-omnibox.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/parent/ext-omnibox.js 2022-04-26 05:44:41.000000000 +0000 @@ -12,7 +12,70 @@ "resource://gre/modules/ExtensionSearchHandler.jsm" ); -this.omnibox = class extends ExtensionAPI { +this.omnibox = class extends ExtensionAPIPersistent { + PERSISTENT_EVENTS = { + onInputStarted({ fire }) { + let { extension } = this; + let listener = eventName => { + fire.sync(); + }; + extension.on(ExtensionSearchHandler.MSG_INPUT_STARTED, listener); + return { + unregister() { + extension.off(ExtensionSearchHandler.MSG_INPUT_STARTED, listener); + }, + convert(_fire) { + fire = _fire; + }, + }; + }, + onInputCancelled({ fire }) { + let { extension } = this; + let listener = eventName => { + fire.sync(); + }; + extension.on(ExtensionSearchHandler.MSG_INPUT_CANCELLED, listener); + return { + unregister() { + extension.off(ExtensionSearchHandler.MSG_INPUT_CANCELLED, listener); + }, + convert(_fire) { + fire = _fire; + }, + }; + }, + onInputEntered({ fire }) { + let { extension } = this; + let listener = (eventName, text, disposition) => { + fire.sync(text, disposition); + }; + extension.on(ExtensionSearchHandler.MSG_INPUT_ENTERED, listener); + return { + unregister() { + extension.off(ExtensionSearchHandler.MSG_INPUT_ENTERED, listener); + }, + convert(_fire) { + fire = _fire; + }, + }; + }, + onInputChanged({ fire }) { + let { extension } = this; + let listener = (eventName, text, id) => { + fire.sync(text, id); + }; + extension.on(ExtensionSearchHandler.MSG_INPUT_CHANGED, listener); + return { + unregister() { + extension.off(ExtensionSearchHandler.MSG_INPUT_CHANGED, listener); + }, + convert(_fire) { + fire = _fire; + }, + }; + }, + }; + onManifestEntry(entryName) { let { extension } = this; let { manifest } = extension; @@ -32,7 +95,6 @@ } getAPI(context) { - let { extension } = context; return { omnibox: { setDefaultSuggestion: suggestion => { @@ -49,47 +111,30 @@ onInputStarted: new EventManager({ context, - name: "omnibox.onInputStarted", - register: fire => { - let listener = eventName => { - fire.sync(); - }; - extension.on(ExtensionSearchHandler.MSG_INPUT_STARTED, listener); - return () => { - extension.off(ExtensionSearchHandler.MSG_INPUT_STARTED, listener); - }; - }, + module: "omnibox", + event: "onInputStarted", + extensionApi: this, }).api(), onInputCancelled: new EventManager({ context, - name: "omnibox.onInputCancelled", - register: fire => { - let listener = eventName => { - fire.sync(); - }; - extension.on(ExtensionSearchHandler.MSG_INPUT_CANCELLED, listener); - return () => { - extension.off( - ExtensionSearchHandler.MSG_INPUT_CANCELLED, - listener - ); - }; - }, + module: "omnibox", + event: "onInputCancelled", + extensionApi: this, }).api(), onInputEntered: new EventManager({ context, - name: "omnibox.onInputEntered", - register: fire => { - let listener = (eventName, text, disposition) => { - fire.sync(text, disposition); - }; - extension.on(ExtensionSearchHandler.MSG_INPUT_ENTERED, listener); - return () => { - extension.off(ExtensionSearchHandler.MSG_INPUT_ENTERED, listener); - }; - }, + module: "omnibox", + event: "onInputEntered", + extensionApi: this, + }).api(), + + onInputChanged: new EventManager({ + context, + module: "omnibox", + event: "onInputChanged", + extensionApi: this, }).api(), // Internal APIs. @@ -105,20 +150,6 @@ // has already invalidated the callback when asynchronously providing suggestions. } }, - - onInputChanged: new EventManager({ - context, - name: "omnibox.onInputChanged", - register: fire => { - let listener = (eventName, text, id) => { - fire.sync(text, id); - }; - extension.on(ExtensionSearchHandler.MSG_INPUT_CHANGED, listener); - return () => { - extension.off(ExtensionSearchHandler.MSG_INPUT_CHANGED, listener); - }; - }, - }).api(), }, }; } diff -Nru firefox-99.0.1+build1/browser/components/extensions/parent/ext-pageAction.js firefox-100.0+build1/browser/components/extensions/parent/ext-pageAction.js --- firefox-99.0.1+build1/browser/components/extensions/parent/ext-pageAction.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/parent/ext-pageAction.js 2022-04-26 05:44:41.000000000 +0000 @@ -62,7 +62,7 @@ } } -this.pageAction = class extends ExtensionAPI { +this.pageAction = class extends ExtensionAPIPersistent { static for(extension) { return pageActionMap.get(extension); } @@ -340,9 +340,36 @@ } } + PERSISTENT_EVENTS = { + onClicked({ context, fire }) { + const { extension } = this; + const { tabManager } = extension; + + let listener = async (_event, tab, clickInfo) => { + if (fire.wakeup) { + await fire.wakeup(); + } + // TODO: we should double-check if the tab is already being closed by the time + // the background script got started and we converted the primed listener. + context?.withPendingBrowser(tab.linkedBrowser, () => + fire.sync(tabManager.convert(tab), clickInfo) + ); + }; + + this.on("click", listener); + return { + unregister: () => { + this.off("click", listener); + }, + convert(newFire, extContext) { + fire = newFire; + context = extContext; + }, + }; + }, + }; + getAPI(context) { - const { extension } = context; - const { tabManager } = extension; const { action } = this; return { @@ -351,20 +378,10 @@ onClicked: new EventManager({ context, - name: "pageAction.onClicked", + module: "pageAction", + event: "onClicked", inputHandling: true, - register: fire => { - let listener = (evt, tab, clickInfo) => { - context.withPendingBrowser(tab.linkedBrowser, () => - fire.sync(tabManager.convert(tab), clickInfo) - ); - }; - - this.on("click", listener); - return () => { - this.off("click", listener); - }; - }, + extensionApi: this, }).api(), openPopup: () => { diff -Nru firefox-99.0.1+build1/browser/components/extensions/parent/ext-sessions.js firefox-100.0+build1/browser/components/extensions/parent/ext-sessions.js --- firefox-99.0.1+build1/browser/components/extensions/parent/ext-sessions.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/parent/ext-sessions.js 2022-04-26 05:44:41.000000000 +0000 @@ -92,7 +92,25 @@ return `extension:${extensionId}:${key}`; }; -this.sessions = class extends ExtensionAPI { +this.sessions = class extends ExtensionAPIPersistent { + PERSISTENT_EVENTS = { + onChanged({ fire }) { + let observer = () => { + fire.async(); + }; + + Services.obs.addObserver(observer, SS_ON_CLOSED_OBJECTS_CHANGED); + return { + unregister() { + Services.obs.removeObserver(observer, SS_ON_CLOSED_OBJECTS_CHANGED); + }, + convert(_fire) { + fire = _fire; + }, + }; + }, + }; + getAPI(context) { let { extension } = context; @@ -251,20 +269,9 @@ onChanged: new EventManager({ context, - name: "sessions.onChanged", - register: fire => { - let observer = () => { - fire.async(); - }; - - Services.obs.addObserver(observer, SS_ON_CLOSED_OBJECTS_CHANGED); - return () => { - Services.obs.removeObserver( - observer, - SS_ON_CLOSED_OBJECTS_CHANGED - ); - }; - }, + module: "sessions", + event: "onChanged", + extensionApi: this, }).api(), }, }; diff -Nru firefox-99.0.1+build1/browser/components/extensions/parent/ext-tabs.js firefox-100.0+build1/browser/components/extensions/parent/ext-tabs.js --- firefox-99.0.1+build1/browser/components/extensions/parent/ext-tabs.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/parent/ext-tabs.js 2022-04-26 05:44:41.000000000 +0000 @@ -189,12 +189,151 @@ ]); const restricted = new Set(["url", "favIconUrl", "title"]); -class TabsUpdateFilterEventManager extends EventManager { - constructor(context) { - let { extension } = context; +this.tabs = class extends ExtensionAPIPersistent { + static onUpdate(id, manifest) { + if (!manifest.permissions || !manifest.permissions.includes("tabHide")) { + showHiddenTabs(id); + } + } + + static onDisable(id) { + showHiddenTabs(id); + tabHidePopup.clearConfirmation(id); + } + + static onUninstall(id) { + tabHidePopup.clearConfirmation(id); + } + + tabEventRegistrar({ event, listener }) { + let { extension } = this; let { tabManager } = extension; + return ({ fire }) => { + let listener2 = (eventName, eventData, ...args) => { + if (!tabManager.canAccessTab(eventData.nativeTab)) { + return; + } + + listener(fire, eventData, ...args); + }; + + tabTracker.on(event, listener2); + return { + unregister() { + tabTracker.off(event, listener2); + }, + convert(_fire) { + fire = _fire; + }, + }; + }; + } + + PERSISTENT_EVENTS = { + onActivated: this.tabEventRegistrar({ + event: "tab-activated", + listener: (fire, event) => { + let { extension } = this; + let { tabId, windowId, previousTabId, previousTabIsPrivate } = event; + if (previousTabIsPrivate && !extension.privateBrowsingAllowed) { + previousTabId = undefined; + } + fire.async({ tabId, previousTabId, windowId }); + }, + }), + onAttached: this.tabEventRegistrar({ + event: "tab-attached", + listener: (fire, event) => { + fire.async(event.tabId, { + newWindowId: event.newWindowId, + newPosition: event.newPosition, + }); + }, + }), + onCreated: this.tabEventRegistrar({ + event: "tab-created", + listener: (fire, event) => { + let { tabManager } = this.extension; + fire.async(tabManager.convert(event.nativeTab, event.currentTabSize)); + }, + }), + onDetached: this.tabEventRegistrar({ + event: "tab-detached", + listener: (fire, event) => { + fire.async(event.tabId, { + oldWindowId: event.oldWindowId, + oldPosition: event.oldPosition, + }); + }, + }), + onRemoved: this.tabEventRegistrar({ + event: "tab-removed", + listener: (fire, event) => { + fire.async(event.tabId, { + windowId: event.windowId, + isWindowClosing: event.isWindowClosing, + }); + }, + }), + onMoved({ fire }) { + let { tabManager } = this.extension; + let moveListener = event => { + let nativeTab = event.originalTarget; + if (tabManager.canAccessTab(nativeTab)) { + fire.async(tabTracker.getId(nativeTab), { + windowId: windowTracker.getId(nativeTab.ownerGlobal), + fromIndex: event.detail, + toIndex: nativeTab._tPos, + }); + } + }; + + windowTracker.addListener("TabMove", moveListener); + return { + unregister() { + windowTracker.removeListener("TabMove", moveListener); + }, + convert(_fire) { + fire = _fire; + }, + }; + }, + + onHighlighted({ fire, context }) { + let { windowManager } = this.extension; + let highlightListener = (eventName, event) => { + // TODO see if we can avoid "context" here + let window = windowTracker.getWindow(event.windowId, context, false); + if (!window) { + return; + } + let windowWrapper = windowManager.getWrapper(window); + if (!windowWrapper) { + return; + } + let tabIds = Array.from( + windowWrapper.getHighlightedTabs(), + tab => tab.id + ); + fire.async({ tabIds: tabIds, windowId: event.windowId }); + }; + + tabTracker.on("tabs-highlighted", highlightListener); + return { + unregister() { + tabTracker.off("tabs-highlighted", highlightListener); + }, + convert(_fire, _context) { + fire = _fire; + context = _context; + }, + }; + }, - let register = (fire, filterProps) => { + onUpdated({ fire, context }, params) { + let { extension } = this; + let { tabManager } = extension; + let [filterProps] = params; let filter = { ...filterProps }; if (filter.urls) { filter.urls = new MatchPatternSet(filter.urls, { @@ -281,7 +420,7 @@ if (event.originalTarget.initializingTab) { return; } - if (!context.canAccessWindow(event.originalTarget.ownerGlobal)) { + if (!extension.canAccessWindow(event.originalTarget.ownerGlobal)) { return; } let needed = []; @@ -350,7 +489,7 @@ let { gBrowser } = browser.ownerGlobal; let tabElem = gBrowser.getTabForBrowser(browser); if (tabElem) { - if (!context.canAccessWindow(tabElem.ownerGlobal)) { + if (!extension.canAccessWindow(tabElem.ownerGlobal)) { return; } @@ -370,7 +509,7 @@ let { gBrowser } = message.target.ownerGlobal; let nativeTab = gBrowser.getTabForBrowser(message.target); - if (nativeTab && context.canAccessWindow(nativeTab.ownerGlobal)) { + if (nativeTab && extension.canAccessWindow(nativeTab.ownerGlobal)) { let tab = tabManager.getWrapper(nativeTab); fireForTab(tab, { isArticle: message.data.isArticle }, nativeTab); } @@ -404,67 +543,29 @@ tabTracker.on("tab-isarticle", isArticleChangeListener); } - return () => { - for (let [name, listener] of listeners) { - windowTracker.removeListener(name, listener); - } + return { + unregister() { + for (let [name, listener] of listeners) { + windowTracker.removeListener(name, listener); + } - if (filter.properties.has("isArticle")) { - tabTracker.off("tab-isarticle", isArticleChangeListener); - } + if (filter.properties.has("isArticle")) { + tabTracker.off("tab-isarticle", isArticleChangeListener); + } + }, + convert(_fire, _context) { + fire = _fire; + context = _context; + }, }; - }; - - super({ - context, - name: "tabs.onUpdated", - register, - }); - } -} - -function TabEventManager({ context, name, event, listener }) { - let register = fire => { - let listener2 = (eventName, eventData, ...args) => { - let { extension } = context; - let { tabManager } = extension; - - if (!tabManager.canAccessTab(eventData.nativeTab)) { - return; - } - - listener(fire, eventData, ...args); - }; - - tabTracker.on(event, listener2); - return () => { - tabTracker.off(event, listener2); - }; + }, }; - return new EventManager({ context, name, register }).api(); -} - -this.tabs = class extends ExtensionAPI { - static onUpdate(id, manifest) { - if (!manifest.permissions || !manifest.permissions.includes("tabHide")) { - showHiddenTabs(id); - } - } - - static onDisable(id) { - showHiddenTabs(id); - tabHidePopup.clearConfirmation(id); - } - - static onUninstall(id) { - tabHidePopup.clearConfirmation(id); - } - getAPI(context) { let { extension } = context; - let { tabManager, windowManager } = extension; + let extensionApi = this; + let module = "tabs"; function getTabOrActive(tabId) { let tab = @@ -510,103 +611,49 @@ return tab; } - let self = { + let tabsApi = { tabs: { - onActivated: TabEventManager({ + onActivated: new EventManager({ context, - name: "tabs.onActivated", - event: "tab-activated", - listener: (fire, event) => { - let { - tabId, - windowId, - previousTabId, - previousTabIsPrivate, - } = event; - if (previousTabIsPrivate && !context.privateBrowsingAllowed) { - previousTabId = undefined; - } - fire.async({ tabId, previousTabId, windowId }); - }, - }), + module, + event: "onActivated", + extensionApi, + }).api(), - onCreated: TabEventManager({ + onCreated: new EventManager({ context, - name: "tabs.onCreated", - event: "tab-created", - listener: (fire, event) => { - fire.async( - tabManager.convert(event.nativeTab, event.currentTabSize) - ); - }, - }), + module, + event: "onCreated", + extensionApi, + }).api(), onHighlighted: new EventManager({ context, - name: "tabs.onHighlighted", - register: fire => { - let highlightListener = (eventName, event) => { - let window = windowTracker.getWindow( - event.windowId, - context, - false - ); - if (!window) { - return; - } - let windowWrapper = windowManager.getWrapper(window); - if (!windowWrapper) { - return; - } - let tabIds = Array.from( - windowWrapper.getHighlightedTabs(), - tab => tab.id - ); - fire.async({ tabIds: tabIds, windowId: event.windowId }); - }; - - tabTracker.on("tabs-highlighted", highlightListener); - return () => { - tabTracker.off("tabs-highlighted", highlightListener); - }; - }, + module, + event: "onHighlighted", + extensionApi, }).api(), - onAttached: TabEventManager({ + onAttached: new EventManager({ context, - name: "tabs.onAttached", - event: "tab-attached", - listener: (fire, event) => { - fire.async(event.tabId, { - newWindowId: event.newWindowId, - newPosition: event.newPosition, - }); - }, - }), + module, + event: "onAttached", + extensionApi, + }).api(), - onDetached: TabEventManager({ + onDetached: new EventManager({ context, - name: "tabs.onDetached", - event: "tab-detached", - listener: (fire, event) => { - fire.async(event.tabId, { - oldWindowId: event.oldWindowId, - oldPosition: event.oldPosition, - }); - }, - }), + module, + event: "onDetached", + extensionApi, + }).api(), - onRemoved: TabEventManager({ + onRemoved: new EventManager({ context, - name: "tabs.onRemoved", - event: "tab-removed", - listener: (fire, event) => { - fire.async(event.tabId, { - windowId: event.windowId, - isWindowClosing: event.isWindowClosing, - }); - }, - }), + module, + event: "onRemoved", + extensionApi, + }).api(), onReplaced: new EventManager({ context, @@ -618,27 +665,17 @@ onMoved: new EventManager({ context, - name: "tabs.onMoved", - register: fire => { - let moveListener = event => { - let nativeTab = event.originalTarget; - if (tabManager.canAccessTab(nativeTab)) { - fire.async(tabTracker.getId(nativeTab), { - windowId: windowTracker.getId(nativeTab.ownerGlobal), - fromIndex: event.detail, - toIndex: nativeTab._tPos, - }); - } - }; - - windowTracker.addListener("TabMove", moveListener); - return () => { - windowTracker.removeListener("TabMove", moveListener); - }; - }, + module, + event: "onMoved", + extensionApi, }).api(), - onUpdated: new TabsUpdateFilterEventManager(context).api(), + onUpdated: new EventManager({ + context, + module, + event: "onUpdated", + extensionApi, + }).api(), create(createProperties) { return new Promise((resolve, reject) => { @@ -802,6 +839,10 @@ tabListener.initializingTabs.add(nativeTab); } + if (createProperties.muted) { + nativeTab.toggleMuteAudio(extension.id); + } + return tabManager.convert(nativeTab, currentTabSize); }); }, @@ -1257,7 +1298,7 @@ tabId, oldZoomFactor, newZoomFactor, - zoomSettings: self.tabs._getZoomSettings(tabId), + zoomSettings: tabsApi.tabs._getZoomSettings(tabId), }); } }; @@ -1347,7 +1388,8 @@ printSettings.isInitializedFromPrinter = true; printSettings.isInitializedFromPrefs = true; - printSettings.printToFile = true; + printSettings.outputDestination = + Ci.nsIPrintSettings.kOutputDestinationFile; printSettings.toFileName = picker.file.path; printSettings.printSilent = true; @@ -1580,6 +1622,6 @@ }, }, }; - return self; + return tabsApi; } }; diff -Nru firefox-99.0.1+build1/browser/components/extensions/parent/ext-windows.js firefox-100.0+build1/browser/components/extensions/parent/ext-windows.js --- firefox-99.0.1+build1/browser/components/extensions/parent/ext-windows.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/parent/ext-windows.js 2022-04-26 05:44:41.000000000 +0000 @@ -19,41 +19,69 @@ var { promiseObserved } = ExtensionUtils; -/** - * An event manager API provider which listens for a DOM event in any browser - * window, and calls the given listener function whenever an event is received. - * That listener function receives a `fire` object, which it can use to dispatch - * events to the extension, and a DOM event object. - * - * @param {BaseContext} context - * The extension context which the event manager belongs to. - * @param {string} name - * The API name of the event manager, e.g.,"runtime.onMessage". - * @param {string} event - * The name of the DOM event to listen for. - * @param {function} listener - * The listener function to call when a DOM event is received. - * - * @returns {object} An injectable api for the new event. - */ -function WindowEventManager(context, name, event, listener) { - let register = fire => { - let listener2 = (window, ...args) => { - if (context.canAccessWindow(window)) { - listener(fire, window, ...args); - } +this.windows = class extends ExtensionAPIPersistent { + windowEventRegistrar(event, listener) { + let { extension } = this; + return ({ fire }) => { + let listener2 = (window, ...args) => { + if (extension.canAccessWindow(window)) { + listener(fire, window, ...args); + } + }; + + windowTracker.addListener(event, listener2); + return { + unregister() { + windowTracker.removeListener(event, listener2); + }, + convert(_fire) { + fire = _fire; + }, + }; }; + } - windowTracker.addListener(event, listener2); - return () => { - windowTracker.removeListener(event, listener2); - }; + PERSISTENT_EVENTS = { + onCreated: this.windowEventRegistrar("domwindowopened", (fire, window) => { + fire.async(this.extension.windowManager.convert(window)); + }), + onRemoved: this.windowEventRegistrar("domwindowclosed", (fire, window) => { + fire.async(windowTracker.getId(window)); + }), + onFocusChanged({ fire }) { + let { extension } = this; + // Keep track of the last windowId used to fire an onFocusChanged event + let lastOnFocusChangedWindowId; + + let listener = event => { + // Wait a tick to avoid firing a superfluous WINDOW_ID_NONE + // event when switching focus between two Firefox windows. + Promise.resolve().then(() => { + let windowId = Window.WINDOW_ID_NONE; + let window = Services.focus.activeWindow; + if (window && extension.canAccessWindow(window)) { + windowId = windowTracker.getId(window); + } + if (windowId !== lastOnFocusChangedWindowId) { + fire.async(windowId); + lastOnFocusChangedWindowId = windowId; + } + }); + }; + windowTracker.addListener("focus", listener); + windowTracker.addListener("blur", listener); + return { + unregister() { + windowTracker.removeListener("focus", listener); + windowTracker.removeListener("blur", listener); + }, + convert(_fire) { + fire = _fire; + }, + }; + }, }; - return new EventManager({ context, name, register }).api(); -} - -this.windows = class extends ExtensionAPI { getAPI(context) { let { extension } = context; @@ -61,53 +89,25 @@ return { windows: { - onCreated: WindowEventManager( + onCreated: new EventManager({ context, - "windows.onCreated", - "domwindowopened", - (fire, window) => { - fire.async(windowManager.convert(window)); - } - ), + module: "windows", + event: "onCreated", + extensionApi: this, + }).api(), - onRemoved: WindowEventManager( + onRemoved: new EventManager({ context, - "windows.onRemoved", - "domwindowclosed", - (fire, window) => { - fire.async(windowTracker.getId(window)); - } - ), + module: "windows", + event: "onRemoved", + extensionApi: this, + }).api(), onFocusChanged: new EventManager({ context, - name: "windows.onFocusChanged", - register: fire => { - // Keep track of the last windowId used to fire an onFocusChanged event - let lastOnFocusChangedWindowId; - - let listener = event => { - // Wait a tick to avoid firing a superfluous WINDOW_ID_NONE - // event when switching focus between two Firefox windows. - Promise.resolve().then(() => { - let windowId = Window.WINDOW_ID_NONE; - let window = Services.focus.activeWindow; - if (window && context.canAccessWindow(window)) { - windowId = windowTracker.getId(window); - } - if (windowId !== lastOnFocusChangedWindowId) { - fire.async(windowId); - lastOnFocusChangedWindowId = windowId; - } - }); - }; - windowTracker.addListener("focus", listener); - windowTracker.addListener("blur", listener); - return () => { - windowTracker.removeListener("focus", listener); - windowTracker.removeListener("blur", listener); - }; - }, + module: "windows", + event: "onFocusChanged", + extensionApi: this, }).api(), get: function(windowId, getInfo) { diff -Nru firefox-99.0.1+build1/browser/components/extensions/schemas/tabs.json firefox-100.0+build1/browser/components/extensions/schemas/tabs.json --- firefox-99.0.1+build1/browser/components/extensions/schemas/tabs.json 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/schemas/tabs.json 2022-04-26 05:44:41.000000000 +0000 @@ -612,6 +612,11 @@ "type": "string", "optional": true, "description": "The title used for display if the tab is created in discarded mode." + }, + "muted": { + "type": "boolean", + "optional": true, + "description": "Whether the tab should be muted when created." } } }, diff -Nru firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_browserAction_click_types.js firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_browserAction_click_types.js --- firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_browserAction_click_types.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_browserAction_click_types.js 2022-04-26 05:44:41.000000000 +0000 @@ -2,21 +2,37 @@ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; -add_task(async function test_clickData() { +async function test_clickData({ manifest_version, persistent }) { + const action = manifest_version < 3 ? "browser_action" : "action"; + const background = { scripts: ["background.js"] }; + + if (persistent != null) { + background.persistent = persistent; + } + let extension = ExtensionTestUtils.loadExtension({ manifest: { - browser_action: {}, + manifest_version, + [action]: {}, + background, }, - background() { - function onClicked(tab, info) { - let button = info.button; - let modifiers = info.modifiers; - browser.test.sendMessage("onClick", { button, modifiers }); - } - - browser.browserAction.onClicked.addListener(onClicked); - browser.test.sendMessage("ready"); + files: { + "background.js": function backgroundScript() { + function onClicked(tab, info) { + let button = info.button; + let modifiers = info.modifiers; + browser.test.sendMessage("onClick", { button, modifiers }); + } + + const apiNS = + browser.runtime.getManifest().manifest_version >= 3 + ? "action" + : "browserAction"; + + browser[apiNS].onClicked.addListener(onClicked); + browser.test.sendMessage("ready"); + }, }, }); @@ -53,6 +69,20 @@ await extension.startup(); await extension.awaitMessage("ready"); + if (manifest_version >= 3 || persistent === false) { + // NOTE: even in MV3 where the API namespace is technically "action", + // the event listeners will be persisted into the startup data + // with "browserAction" as the module, because that is the name + // of the module shared by both MV2 browserAction and MV3 action APIs. + assertPersistentListeners(extension, "browserAction", "onClicked", { + primed: false, + }); + await extension.terminateBackground(); + assertPersistentListeners(extension, "browserAction", "onClicked", { + primed: true, + }); + } + for (let area of [CustomizableUI.AREA_NAVBAR, getCustomizableUIPanelID()]) { let widget = getBrowserActionWidget(extension); CustomizableUI.addWidgetToArea(widget.id, area); @@ -82,13 +112,21 @@ } } + if (manifest_version >= 3 || persistent === false) { + // The background event page is expected to have been + // spawned again to handle the action onClicked event. + await extension.awaitMessage("ready"); + } + await extension.unload(); -}); +} -add_task(async function test_clickData_reset() { +async function test_clickData_reset({ manifest_version }) { + const action = manifest_version < 3 ? "browser_action" : "action"; let extension = ExtensionTestUtils.loadExtension({ manifest: { - browser_action: {}, + manifest_version, + [action]: {}, page_action: {}, }, @@ -99,17 +137,27 @@ function onPageActionClicked(tab, info) { // openPopup requires user interaction, such as a page action click. + // NOTE: this triggers the browserAction onClicked event as a side-effect + // of triggering the browserAction popup through browserAction.openPopup. browser.browserAction.openPopup(); } - browser.browserAction.onClicked.addListener(onBrowserActionClicked); - browser.pageAction.onClicked.addListener(onPageActionClicked); + const { manifest_version } = browser.runtime.getManifest(); + const apiNS = manifest_version >= 3 ? "action" : "browserAction"; + + browser[apiNS].onClicked.addListener(onBrowserActionClicked); + + // pageAction should only be available in MV2 extensions. + if (manifest_version < 3) { + browser.pageAction.onClicked.addListener(onPageActionClicked); + + let [tab] = await browser.tabs.query({ + active: true, + currentWindow: true, + }); + await browser.pageAction.show(tab.id); + } - let [tab] = await browser.tabs.query({ - active: true, - currentWindow: true, - }); - await browser.pageAction.show(tab.id); browser.test.sendMessage("ready"); }, }); @@ -118,8 +166,8 @@ async function clickBrowserActionWithModifiers() { await clickBrowserAction(extension, window, { button: 1, shiftKey: true }); let info = await extension.awaitMessage("onClick"); - is(info.button, 1); - is(info.modifiers[0], "Shift"); + is(info.button, 1, "Got expected ClickData button details"); + is(info.modifiers[0], "Shift", "Got expected ClickData modifiers details"); } function assertInfoReset(info) { @@ -130,10 +178,33 @@ await extension.startup(); await extension.awaitMessage("ready"); + if (manifest_version >= 3) { + // NOTE: even in MV3 where the API namespace is technically "action", + // the event listeners will be persisted into the startup data + // with "browserAction" as the module, because that is the name + // of the module shared by both MV2 browserAction and MV3 action APIs. + assertPersistentListeners(extension, "browserAction", "onClicked", { + primed: false, + }); + await extension.terminateBackground(); + assertPersistentListeners(extension, "browserAction", "onClicked", { + primed: true, + }); + } + await clickBrowserActionWithModifiers(); - await clickPageAction(extension); - assertInfoReset(await extension.awaitMessage("onClick")); + if (manifest_version >= 3) { + // The background event page is expected to have been + // spawned again to handle the action onClicked event. + await extension.awaitMessage("ready"); + } else { + // pageAction should only be available in MV2 extensions. + await clickPageAction(extension); + // NOTE: the pageAction event listener then triggers browserAction.onClicked + // as a side effect of calling browserAction.openPopup. + assertInfoReset(await extension.awaitMessage("onClick")); + } await clickBrowserActionWithModifiers(); @@ -146,4 +217,39 @@ assertInfoReset(await extension.awaitMessage("onClick")); await extension.unload(); +} + +add_task(function test_clickData_MV2() { + return test_clickData({ manifest_version: 2 }); +}); + +add_task(async function test_clickData_MV2_eventpage() { + await SpecialPowers.pushPrefEnv({ + set: [["extensions.eventPages.enabled", true]], + }); + await test_clickData({ + manifest_version: 2, + persistent: false, + }); + await SpecialPowers.popPrefEnv(); +}); + +add_task(async function test_clickData_MV3() { + await SpecialPowers.pushPrefEnv({ + set: [["extensions.manifestV3.enabled", true]], + }); + await test_clickData({ manifest_version: 3 }); + await SpecialPowers.popPrefEnv(); +}); + +add_task(function test_clickData_reset_MV2() { + return test_clickData_reset({ manifest_version: 2 }); +}); + +add_task(async function test_clickData_reset_MV3() { + await SpecialPowers.pushPrefEnv({ + set: [["extensions.manifestV3.enabled", true]], + }); + await test_clickData_reset({ manifest_version: 3 }); + await SpecialPowers.popPrefEnv(); }); diff -Nru firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_browserAction_simple.js firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_browserAction_simple.js --- firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_browserAction_simple.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_browserAction_simple.js 2022-04-26 05:44:41.000000000 +0000 @@ -86,7 +86,7 @@ await waitForConsole; } -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [["extensions.manifestV3.enabled", true]], }); diff -Nru firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_commands_onCommand.js firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_commands_onCommand.js --- firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_commands_onCommand.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_commands_onCommand.js 2022-04-26 05:44:41.000000000 +0000 @@ -378,3 +378,55 @@ SimpleTest.endMonitorConsole(); await waitForConsole; }); + +add_task(async function test_commands_event_page() { + await SpecialPowers.pushPrefEnv({ + set: [["extensions.eventPages.enabled", true]], + }); + + let extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "permanent", + manifest: { + applications: { gecko: { id: "eventpage@commands" } }, + background: { persistent: false }, + commands: { + "toggle-feature": { + suggested_key: { + default: "Alt+Shift+J", + }, + }, + }, + }, + background() { + browser.commands.onCommand.addListener(name => { + browser.test.assertEq(name, "toggle-feature", "command received"); + browser.test.sendMessage("onCommand"); + }); + browser.test.sendMessage("ready"); + }, + }); + + await extension.startup(); + await extension.awaitMessage("ready"); + assertPersistentListeners(extension, "commands", "onCommand", { + primed: false, + }); + + // test events waken background + await extension.terminateBackground(); + assertPersistentListeners(extension, "commands", "onCommand", { + primed: true, + }); + + EventUtils.synthesizeKey("j", { altKey: true, shiftKey: true }); + + await extension.awaitMessage("ready"); + await extension.awaitMessage("onCommand"); + ok(true, "persistent event woke background"); + assertPersistentListeners(extension, "commands", "onCommand", { + primed: false, + }); + + await extension.unload(); + await SpecialPowers.popPrefEnv(); +}); diff -Nru firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_menus_events.js firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_menus_events.js --- firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_menus_events.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_menus_events.js 2022-04-26 05:44:41.000000000 +0000 @@ -21,7 +21,7 @@ var someOtherTab, testTab; -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [["extensions.manifestV3.enabled", true]], }); diff -Nru firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_omnibox.js firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_omnibox.js --- firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_omnibox.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_omnibox.js 2022-04-26 05:44:41.000000000 +0000 @@ -6,11 +6,11 @@ "resource://testing-common/UrlbarTestUtils.jsm" ); +const keyword = "VeryUniqueKeywordThatDoesNeverMatchAnyTestUrl"; + add_task(async function() { // This keyword needs to be unique to prevent history entries from unrelated // tests from appearing in the suggestions list. - let keyword = "VeryUniqueKeywordThatDoesNeverMatchAnyTestUrl"; - let extension = ExtensionTestUtils.loadExtension({ manifest: { omnibox: { @@ -368,3 +368,70 @@ await extension2.unload(); await extension.unload(); }); + +add_task(async function test_omnibox_event_page() { + await SpecialPowers.pushPrefEnv({ + set: [["extensions.eventPages.enabled", true]], + }); + + let extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "permanent", + manifest: { + applications: { gecko: { id: "eventpage@omnibox" } }, + omnibox: { + keyword: keyword, + }, + background: { persistent: false }, + }, + background() { + browser.omnibox.onInputStarted.addListener(() => { + browser.test.sendMessage("onInputStarted"); + }); + browser.omnibox.onInputEntered.addListener(() => {}); + browser.omnibox.onInputChanged.addListener(() => {}); + browser.omnibox.onInputCancelled.addListener(() => {}); + browser.test.sendMessage("ready"); + }, + }); + + const EVENTS = [ + "onInputStarted", + "onInputEntered", + "onInputChanged", + "onInputCancelled", + ]; + + await extension.startup(); + await extension.awaitMessage("ready"); + for (let event of EVENTS) { + assertPersistentListeners(extension, "omnibox", event, { + primed: false, + }); + } + + // test events waken background + await extension.terminateBackground(); + for (let event of EVENTS) { + assertPersistentListeners(extension, "omnibox", event, { + primed: true, + }); + } + + // Activate the keyword by typing a space. + // Expect onInputStarted to fire. + gURLBar.focus(); + gURLBar.value = keyword; + EventUtils.sendString(" "); + + await extension.awaitMessage("ready"); + await extension.awaitMessage("onInputStarted"); + ok(true, "persistent event woke background"); + for (let event of EVENTS) { + assertPersistentListeners(extension, "omnibox", event, { + primed: false, + }); + } + + await extension.unload(); + await SpecialPowers.popPrefEnv(); +}); diff -Nru firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_pageAction_click_types.js firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_pageAction_click_types.js --- firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_pageAction_click_types.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_pageAction_click_types.js 2022-04-26 05:44:41.000000000 +0000 @@ -2,7 +2,7 @@ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; -add_task(async function setup() { +add_setup(async function() { // The page action button is hidden by default. // This tests the use of pageAction when the button is visible. // @@ -17,27 +17,33 @@ }); }); -add_task(async function test_clickData() { +async function test_clickData(testAsNonPersistent = false) { let extension = ExtensionTestUtils.loadExtension({ manifest: { page_action: {}, + background: { + persistent: !testAsNonPersistent, + scripts: ["background.js"], + }, }, - async background() { - function onClicked(tab, info) { - let button = info.button; - let modifiers = info.modifiers; - browser.test.sendMessage("onClick", { button, modifiers }); - } - - browser.pageAction.onClicked.addListener(onClicked); - - let [tab] = await browser.tabs.query({ - active: true, - currentWindow: true, - }); - await browser.pageAction.show(tab.id); - browser.test.sendMessage("ready"); + files: { + "background.js": async function background() { + function onClicked(_tab, info) { + let button = info.button; + let modifiers = info.modifiers; + browser.test.sendMessage("onClick", { button, modifiers }); + } + + browser.pageAction.onClicked.addListener(onClicked); + + let [tab] = await browser.tabs.query({ + active: true, + currentWindow: true, + }); + await browser.pageAction.show(tab.id); + browser.test.sendMessage("ready"); + }, }, }); @@ -96,41 +102,67 @@ await extension.startup(); await extension.awaitMessage("ready"); + if (testAsNonPersistent) { + assertPersistentListeners(extension, "pageAction", "onClicked", { + primed: false, + }); + info("Terminating the background event page"); + await extension.terminateBackground(); + assertPersistentListeners(extension, "pageAction", "onClicked", { + primed: true, + }); + } + + info("Clicking the pageAction"); await testClickPageAction(clickPageAction, triggerPageActionWithKeyboard); + + if (testAsNonPersistent) { + await extension.awaitMessage("ready"); + assertPersistentListeners(extension, "pageAction", "onClicked", { + primed: false, + }); + } + await testClickPageAction( clickPageActionInPanel, triggerPageActionWithKeyboardInPanel ); await extension.unload(); -}); +} -add_task(async function test_clickData_reset() { +async function test_clickData_reset(testAsNonPersistent = false) { let extension = ExtensionTestUtils.loadExtension({ manifest: { browser_action: {}, page_action: {}, + background: { + persistent: !testAsNonPersistent, + scripts: ["background.js"], + }, }, - async background() { - function onBrowserActionClicked(tab, info) { - // openPopup requires user interaction, such as a browser action click. - browser.pageAction.openPopup(); - } - - function onPageActionClicked(tab, info) { - browser.test.sendMessage("onClick", info); - } - - browser.browserAction.onClicked.addListener(onBrowserActionClicked); - browser.pageAction.onClicked.addListener(onPageActionClicked); - - let [tab] = await browser.tabs.query({ - active: true, - currentWindow: true, - }); - await browser.pageAction.show(tab.id); - browser.test.sendMessage("ready"); + files: { + "background.js": async function background() { + function onBrowserActionClicked(tab, info) { + // openPopup requires user interaction, such as a browser action click. + browser.pageAction.openPopup(); + } + + function onPageActionClicked(tab, info) { + browser.test.sendMessage("onClick", info); + } + + browser.browserAction.onClicked.addListener(onBrowserActionClicked); + browser.pageAction.onClicked.addListener(onPageActionClicked); + + let [tab] = await browser.tabs.query({ + active: true, + currentWindow: true, + }); + await browser.pageAction.show(tab.id); + browser.test.sendMessage("ready"); + }, }, }); @@ -149,8 +181,27 @@ await extension.startup(); await extension.awaitMessage("ready"); + if (testAsNonPersistent) { + assertPersistentListeners(extension, "pageAction", "onClicked", { + primed: false, + }); + info("Terminating the background event page"); + await extension.terminateBackground(); + assertPersistentListeners(extension, "pageAction", "onClicked", { + primed: true, + }); + } + + info("Clicking the pageAction"); await clickPageActionWithModifiers(); + if (testAsNonPersistent) { + await extension.awaitMessage("ready"); + assertPersistentListeners(extension, "pageAction", "onClicked", { + primed: false, + }); + } + await clickBrowserAction(extension); assertInfoReset(await extension.awaitMessage("onClick")); @@ -160,4 +211,28 @@ assertInfoReset(await extension.awaitMessage("onClick")); await extension.unload(); +} + +add_task(function test_clickData_MV2() { + return test_clickData(/* testAsNonPersistent */ false); +}); + +add_task(async function test_clickData_MV2_eventPage() { + await SpecialPowers.pushPrefEnv({ + set: [["extensions.eventPages.enabled", true]], + }); + await test_clickData(/* testAsNonPersistent */ true); + await SpecialPowers.popPrefEnv(); +}); + +add_task(function test_clickData_reset_MV2() { + return test_clickData_reset(/* testAsNonPersistent */ false); +}); + +add_task(async function test_clickData_reset_MV2_eventPage() { + await SpecialPowers.pushPrefEnv({ + set: [["extensions.eventPages.enabled", true]], + }); + await test_clickData_reset(/* testAsNonPersistent */ true); + await SpecialPowers.popPrefEnv(); }); diff -Nru firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_sessions_getRecentlyClosed.js firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_sessions_getRecentlyClosed.js --- firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_sessions_getRecentlyClosed.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_sessions_getRecentlyClosed.js 2022-04-26 05:44:41.000000000 +0000 @@ -155,3 +155,62 @@ await extension.awaitFinish(); await extension.unload(); }); + +add_task( + async function test_sessions_get_recently_closed_empty_history_in_closed_window() { + function background() { + browser.sessions + .getRecentlyClosed({ maxResults: 1 }) + .then(recentlyClosed => { + let win = recentlyClosed[0].window; + browser.test.assertEq( + 3, + win.tabs.length, + "The closed window has 3 tabs." + ); + browser.test.assertEq( + "about:blank", + win.tabs[0].url, + "The first tab is about:blank." + ); + browser.test.assertFalse( + "url" in win.tabs[1], + "The second tab with empty.xpi has no url field due to empty history." + ); + browser.test.assertEq( + "http://example.com/", + win.tabs[2].url, + "The third tab is example.com." + ); + browser.test.notifyPass("getRecentlyClosed with empty history"); + }); + } + + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["sessions", "tabs"], + }, + background, + }); + + // Test with a window with empty history. + let xpi = + "http://example.com/browser/browser/components/extensions/test/browser/empty.xpi"; + let newWin = await BrowserTestUtils.openNewBrowserWindow(); + await BrowserTestUtils.openNewForegroundTab({ + gBrowser: newWin.gBrowser, + url: xpi, + // A tab with broken xpi file doesn't finish loading. + waitForLoad: false, + }); + await BrowserTestUtils.openNewForegroundTab({ + gBrowser: newWin.gBrowser, + url: "http://example.com/", + }); + await BrowserTestUtils.closeWindow(newWin); + + await extension.startup(); + await extension.awaitFinish(); + await extension.unload(); + } +); diff -Nru firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_sessions_restore.js firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_sessions_restore.js --- firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_sessions_restore.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_sessions_restore.js 2022-04-26 05:44:41.000000000 +0000 @@ -191,3 +191,44 @@ await extension.unload(); }); + +add_task(async function test_sessions_event_page() { + await SpecialPowers.pushPrefEnv({ + set: [["extensions.eventPages.enabled", true]], + }); + + let extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "permanent", + manifest: { + applications: { gecko: { id: "eventpage@sessions" } }, + permissions: ["sessions", "tabs"], + background: { persistent: false }, + }, + background() { + browser.sessions.onChanged.addListener(() => { + browser.test.sendMessage("changed"); + }); + browser.test.sendMessage("ready"); + }, + }); + + await extension.startup(); + await extension.awaitMessage("ready"); + + // test events waken background + await extension.terminateBackground(); + let win = await BrowserTestUtils.openNewBrowserWindow(); + BrowserTestUtils.loadURI(win.gBrowser.selectedBrowser, "about:config"); + await BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser); + for (let url of ["about:robots", "about:mozilla"]) { + await BrowserTestUtils.openNewForegroundTab(win.gBrowser, url); + } + await BrowserTestUtils.closeWindow(win); + + await extension.awaitMessage("ready"); + await extension.awaitMessage("changed"); + ok(true, "persistent event woke background"); + + await extension.unload(); + await SpecialPowers.popPrefEnv(); +}); diff -Nru firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_settings_overrides_default_search.js firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_settings_overrides_default_search.js --- firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_settings_overrides_default_search.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_settings_overrides_default_search.js 2022-04-26 05:44:41.000000000 +0000 @@ -33,7 +33,7 @@ await Services.search.setDefault(engine); } -add_task(async function setup() { +add_setup(async function() { let searchExtensions = getChromeDir(getResolvedURI(gTestPath)); searchExtensions.append("search-engines"); diff -Nru firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_tabs_cookieStoreId.js firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_tabs_cookieStoreId.js --- firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_tabs_cookieStoreId.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_tabs_cookieStoreId.js 2022-04-26 05:44:41.000000000 +0000 @@ -2,7 +2,7 @@ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; -add_task(async function setup() { +add_setup(async function() { // make sure userContext is enabled. return SpecialPowers.pushPrefEnv({ set: [["privacy.userContext.enabled", true]], diff -Nru firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_tabs_create.js firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_tabs_create.js --- firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_tabs_create.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_tabs_create.js 2022-04-26 05:44:41.000000000 +0000 @@ -50,6 +50,11 @@ // 'selected' is marked as unsupported in schema, so we've removed it. // For more details, see bug 1337509 selected: undefined, + mutedInfo: { + muted: false, + extensionId: undefined, + reason: undefined, + }, }; let tests = [ @@ -132,6 +137,26 @@ )}`, }, }, + { + create: { muted: true }, + result: { + mutedInfo: { + muted: true, + extensionId: browser.runtime.id, + reason: "extension", + }, + }, + }, + { + create: { muted: false }, + result: { + mutedInfo: { + muted: false, + extensionId: undefined, + reason: undefined, + }, + }, + }, ]; async function nextTest() { @@ -182,11 +207,21 @@ continue; } - browser.test.assertEq( - expected[key], - tab[key], - `Expected value for tab.${key}` - ); + if (key === "mutedInfo") { + for (let key of Object.keys(expected.mutedInfo)) { + browser.test.assertEq( + expected.mutedInfo[key], + tab.mutedInfo[key], + `Expected value for tab.mutedInfo.${key}` + ); + } + } else { + browser.test.assertEq( + expected[key], + tab[key], + `Expected value for tab.${key}` + ); + } } let updated = await updatedPromise; diff -Nru firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_tabs_events.js firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_tabs_events.js --- firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_tabs_events.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_tabs_events.js 2022-04-26 05:44:41.000000000 +0000 @@ -717,3 +717,78 @@ await extension.awaitFinish("tabs-events"); await extension.unload(); }); + +add_task(async function test_tabs_event_page() { + await SpecialPowers.pushPrefEnv({ + set: [["extensions.eventPages.enabled", true]], + }); + + let extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "permanent", + manifest: { + applications: { gecko: { id: "eventpage@tabs" } }, + permissions: ["tabs"], + background: { persistent: false }, + }, + background() { + const EVENTS = [ + "onActivated", + "onAttached", + "onDetached", + "onRemoved", + "onMoved", + "onHighlighted", + "onUpdated", + ]; + browser.tabs.onCreated.addListener(() => { + browser.test.sendMessage("onCreated"); + }); + for (let event of EVENTS) { + browser.tabs[event].addListener(() => {}); + } + browser.test.sendMessage("ready"); + }, + }); + + const EVENTS = [ + "onActivated", + "onAttached", + "onCreated", + "onDetached", + "onRemoved", + "onMoved", + "onHighlighted", + "onUpdated", + ]; + + await extension.startup(); + await extension.awaitMessage("ready"); + for (let event of EVENTS) { + assertPersistentListeners(extension, "tabs", event, { + primed: false, + }); + } + + // test events waken background + await extension.terminateBackground(); + for (let event of EVENTS) { + assertPersistentListeners(extension, "tabs", event, { + primed: true, + }); + } + + let win = await BrowserTestUtils.openNewBrowserWindow(); + + await extension.awaitMessage("ready"); + await extension.awaitMessage("onCreated"); + ok(true, "persistent event woke background"); + for (let event of EVENTS) { + assertPersistentListeners(extension, "tabs", event, { + primed: false, + }); + } + await BrowserTestUtils.closeWindow(win); + + await extension.unload(); + await SpecialPowers.popPrefEnv(); +}); diff -Nru firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_topSites.js firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_topSites.js --- firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_topSites.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_topSites.js 2022-04-26 05:44:41.000000000 +0000 @@ -61,7 +61,7 @@ return extension.awaitMessage("sites"); } -add_task(async function setup() { +add_setup(async function() { await PlacesUtils.history.clear(); await PlacesUtils.bookmarks.eraseEverything(); diff -Nru firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_webRequest.js firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_webRequest.js --- firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_webRequest.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_webRequest.js 2022-04-26 05:44:41.000000000 +0000 @@ -65,7 +65,7 @@ onCompleted: [{ urls }, ["responseHeaders"]], }; -add_task(async function setup() { +add_setup(async function() { extension = makeExtension(events); await extension.startup(); }); diff -Nru firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_webrtc.js firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_webrtc.js --- firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_webrtc.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_webrtc.js 2022-04-26 05:44:41.000000000 +0000 @@ -4,7 +4,7 @@ "resource://testing-common/PermissionTestUtils.jsm" ); -add_task(async function setup() { +add_setup(async function() { await SpecialPowers.pushPrefEnv({ set: [["media.navigator.permission.fake", true]], }); diff -Nru firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_windows_events.js firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_windows_events.js --- firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_ext_windows_events.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/test/browser/browser_ext_windows_events.js 2022-04-26 05:44:41.000000000 +0000 @@ -146,3 +146,77 @@ await extension.unload(); await monitor.unload(); }); + +add_task(async function test_windows_event_page() { + await SpecialPowers.pushPrefEnv({ + set: [["extensions.eventPages.enabled", true]], + }); + + let extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "permanent", + manifest: { + applications: { gecko: { id: "eventpage@windows" } }, + background: { persistent: false }, + }, + background() { + let removed; + browser.windows.onCreated.addListener(window => { + browser.test.sendMessage("onCreated", window.id); + }); + browser.windows.onRemoved.addListener(wid => { + removed = wid; + browser.test.sendMessage("onRemoved", wid); + }); + browser.windows.onFocusChanged.addListener(wid => { + if (wid != browser.windows.WINDOW_ID_NONE && wid != removed) { + browser.test.sendMessage("onFocusChanged", wid); + } + }); + browser.test.sendMessage("ready"); + }, + }); + + const EVENTS = ["onCreated", "onRemoved", "onFocusChanged"]; + + await extension.startup(); + await extension.awaitMessage("ready"); + for (let event of EVENTS) { + assertPersistentListeners(extension, "windows", event, { + primed: false, + }); + } + + // test events waken background + await extension.terminateBackground(); + for (let event of EVENTS) { + assertPersistentListeners(extension, "windows", event, { + primed: true, + }); + } + + let win = await BrowserTestUtils.openNewBrowserWindow(); + + await extension.awaitMessage("ready"); + let windowId = await extension.awaitMessage("onCreated"); + ok(true, "persistent event woke background"); + for (let event of EVENTS) { + assertPersistentListeners(extension, "windows", event, { + primed: false, + }); + } + // focus returns the new window + let focusedId = await extension.awaitMessage("onFocusChanged"); + Assert.equal(windowId, focusedId, "new window was focused"); + await extension.terminateBackground(); + + await BrowserTestUtils.closeWindow(win); + await extension.awaitMessage("ready"); + let removedId = await extension.awaitMessage("onRemoved"); + Assert.equal(windowId, removedId, "window was removed"); + // focus returns the window focus was passed to + focusedId = await extension.awaitMessage("onFocusChanged"); + Assert.notEqual(windowId, focusedId, "old window was focused"); + + await extension.unload(); + await SpecialPowers.popPrefEnv(); +}); diff -Nru firefox-99.0.1+build1/browser/components/extensions/test/browser/browser.ini firefox-100.0+build1/browser/components/extensions/test/browser/browser.ini --- firefox-99.0.1+build1/browser/components/extensions/test/browser/browser.ini 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/test/browser/browser.ini 2022-04-26 05:44:41.000000000 +0000 @@ -42,6 +42,7 @@ search-engines/* searchSuggestionEngine.xml searchSuggestionEngine.sjs + empty.xpi ../../../../../toolkit/components/extensions/test/mochitest/head_webrequest.js ../../../../../toolkit/components/extensions/test/mochitest/redirection.sjs ../../../../../toolkit/components/reader/test/readerModeNonArticle.html @@ -272,6 +273,7 @@ skip-if = os == "mac" && bits == 64 # Bug 1722607 win10_2004 && fission && debug # high frequency intermittent Bug 1722607 + os == "linux" && bits == 64 && debug #Bug 1722607 [browser_ext_tabs_discarded.js] https_first_disabled = true [browser_ext_tabs_duplicate.js] diff -Nru firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_toolbar_prefers_color_scheme.js firefox-100.0+build1/browser/components/extensions/test/browser/browser_toolbar_prefers_color_scheme.js --- firefox-99.0.1+build1/browser/components/extensions/test/browser/browser_toolbar_prefers_color_scheme.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/test/browser/browser_toolbar_prefers_color_scheme.js 2022-04-26 05:44:41.000000000 +0000 @@ -3,208 +3,13 @@ "use strict"; -async function testThemeDeterminesToolbarQuery(toolbarProp) { - let darkModeQuery = window.matchMedia("(-moz-system-dark-theme)"); - let darkToolbarQuery = window.matchMedia("(prefers-color-scheme: dark)"); - - let darkToolbarExtension = ExtensionTestUtils.loadExtension({ - manifest: { - applications: { - gecko: { - id: "dark@mochi.test", - }, - }, - theme: { - colors: { - [toolbarProp]: "#fff", - }, - }, - }, - }); - let lightToolbarExtension = ExtensionTestUtils.loadExtension({ - manifest: { - applications: { - gecko: { - id: "light@mochi.test", - }, - }, - theme: { - colors: { - [toolbarProp]: "#000", - }, - }, - }, - }); - - let darkToolbarLightContentExtension = ExtensionTestUtils.loadExtension({ - manifest: { - applications: { - gecko: { - id: "dark-light@mochi.test", - }, - }, - theme: { - colors: { - [toolbarProp]: "#fff", - ntp_text: "#000", - }, - }, - }, - }); - - let lightToolbarDarkContentExtension = ExtensionTestUtils.loadExtension({ - manifest: { - applications: { - gecko: { - id: "light-dark@mochi.test", - }, - }, - theme: { - colors: { - [toolbarProp]: "#000", - ntp_text: "#fff", - }, - }, - }, - }); - - let contentTab = await BrowserTestUtils.addTab( - gBrowser, - "http://example.com/browser/browser/components/extensions/test/browser/file_dummy.html" - ); - await BrowserTestUtils.browserLoaded(contentTab.linkedBrowser); - - let preferencesTab = await BrowserTestUtils.addTab( - gBrowser, - "about:preferences" - ); - await BrowserTestUtils.browserLoaded(preferencesTab.linkedBrowser); - - function queryMatchesInTab(tab) { - gBrowser.selectedTab = tab; - return SpecialPowers.spawn(tab.linkedBrowser, [], async () => { - // LookAndFeel updates are async. - await new Promise(resolve => { - content.requestAnimationFrame(() => - content.requestAnimationFrame(resolve) - ); - }); - return content.matchMedia("(prefers-color-scheme: dark)").matches; - }); - } - - function queryMatchesInContent() { - return queryMatchesInTab(contentTab); - } - - function queryMatchesInPreferences() { - return queryMatchesInTab(preferencesTab); - } - - async function testColors( - expectedToolbarDark, - expectedContentDark, - description - ) { - is( - darkToolbarQuery.matches, - expectedToolbarDark, - "toolbar query " + description - ); - - is( - await queryMatchesInContent(), - expectedContentDark, - "content query " + description - ); - is( - await queryMatchesInPreferences(), - expectedContentDark, - "about:preferences query " + description - ); - } - - let initialDarkModeMatches = darkModeQuery.matches; - await testColors(initialDarkModeMatches, initialDarkModeMatches, "initially"); - - async function testExtension( - extension, - expectedToolbarDark, - expectedContentDark - ) { - await Promise.all([ - BrowserTestUtils.waitForEvent(darkToolbarQuery, "change"), - TestUtils.topicObserved("lightweight-theme-styling-update"), - TestUtils.topicObserved("look-and-feel-changed"), - extension.startup(), - ]); - - info("testing " + extension.id); - - is(darkModeQuery.matches, initialDarkModeMatches, "OS dark mode unchanged"); - await testColors(expectedToolbarDark, expectedContentDark, extension.id); - - info("tested " + extension.id); - } - - let extensions = darkToolbarQuery.matches - ? [ - lightToolbarExtension, - darkToolbarExtension, - lightToolbarDarkContentExtension, - darkToolbarLightContentExtension, - ] - : [ - darkToolbarExtension, - lightToolbarExtension, - darkToolbarLightContentExtension, - lightToolbarDarkContentExtension, - ]; - - await testExtension( - extensions[0], - !initialDarkModeMatches, - !initialDarkModeMatches - ); - await testExtension( - extensions[1], - initialDarkModeMatches, - initialDarkModeMatches - ); - await testExtension( - extensions[2], - !initialDarkModeMatches, - initialDarkModeMatches - ); - await testExtension( - extensions[3], - initialDarkModeMatches, - !initialDarkModeMatches - ); - - for (let extension of extensions) { - await extension.unload(); - } - BrowserTestUtils.removeTab(contentTab); - BrowserTestUtils.removeTab(preferencesTab); -} - -add_task(function test_toolbar() { - return testThemeDeterminesToolbarQuery("toolbar_text"); -}); - -// Assert that we fall back to tab_background_text if toolbar is not set. -add_task(function test_frame() { - return testThemeDeterminesToolbarQuery("tab_background_text"); -}); - const kDark = 0; const kLight = 1; -const kDefault = 2; +const kSystem = 2; // The above tests should be enough to make sure that the prefs behave as // expected, the following ones test various edge cases in a simpler way. -async function testTheme(description, toolbar, content, themeData) { +async function testTheme(description, toolbar, content, themeManifestData) { info(description); let extension = ExtensionTestUtils.loadExtension({ @@ -214,7 +19,7 @@ id: "dummy@mochi.test", }, }, - theme: themeData, + ...themeManifestData, }, }); @@ -245,33 +50,217 @@ await testTheme( "Dark toolbar color, dark toolbar background", kDark, - kDefault, + kSystem, { - colors: { - toolbar: "rgb(20, 17, 26)", - toolbar_text: "rgb(251, 29, 78)", + theme: { + colors: { + toolbar: "rgb(20, 17, 26)", + toolbar_text: "rgb(251, 29, 78)", + }, }, } ); // Dark frame text is ignored as it might be overlaid with an image, // see bug 1741931. - await testTheme("Dark frame is ignored", kLight, kDefault, { - colors: { - frame: "#000000", - tab_background_text: "#000000", + await testTheme("Dark frame is ignored", kLight, kSystem, { + theme: { + colors: { + frame: "#000000", + tab_background_text: "#000000", + }, }, }); await testTheme( "Semi-transparent toolbar backgrounds are ignored.", kLight, - kDefault, + kSystem, { - colors: { - toolbar: "rgba(0, 0, 0, .2)", - toolbar_text: "#000", + theme: { + colors: { + toolbar: "rgba(0, 0, 0, .2)", + toolbar_text: "#000", + }, }, } ); }); + +add_task(async function dark_theme_presence_overrides_heuristics() { + const systemScheme = window.matchMedia("(-moz-system-dark-theme)").matches + ? kDark + : kLight; + await testTheme( + "darkTheme presence overrides heuristics", + systemScheme, + kSystem, + { + theme: { + colors: { + toolbar: "#000", + toolbar_text: "#fff", + }, + }, + dark_theme: { + colors: { + toolbar: "#000", + toolbar_text: "#fff", + }, + }, + } + ); +}); + +add_task(async function color_scheme_override() { + await testTheme( + "color_scheme overrides toolbar / toolbar_text pair (dark)", + kDark, + kDark, + { + theme: { + colors: { + toolbar: "#fff", + toolbar_text: "#000", + }, + properties: { + color_scheme: "dark", + }, + }, + } + ); + + await testTheme( + "color_scheme overrides toolbar / toolbar_text pair (light)", + kLight, + kLight, + { + theme: { + colors: { + toolbar: "#000", + toolbar_text: "#fff", + }, + properties: { + color_scheme: "light", + }, + }, + } + ); + + await testTheme( + "content_color_scheme overrides ntp_text / ntp_background (dark)", + kLight, + kDark, + { + theme: { + colors: { + toolbar: "#fff", + toolbar_text: "#000", + ntp_background: "#fff", + ntp_text: "#000", + }, + properties: { + content_color_scheme: "dark", + }, + }, + } + ); + + await testTheme( + "content_color_scheme overrides ntp_text / ntp_background (light)", + kLight, + kLight, + { + theme: { + colors: { + toolbar: "#fff", + toolbar_text: "#000", + ntp_background: "#000", + ntp_text: "#fff", + }, + properties: { + content_color_scheme: "light", + }, + }, + } + ); + + await testTheme( + "content_color_scheme overrides color_scheme only for content", + kLight, + kDark, + { + theme: { + colors: { + toolbar: "#fff", + toolbar_text: "#000", + ntp_background: "#fff", + ntp_text: "#000", + }, + properties: { + content_color_scheme: "dark", + }, + }, + } + ); + + await testTheme( + "content_color_scheme sytem overrides color_scheme only for content", + kLight, + kSystem, + { + theme: { + colors: { + toolbar: "#fff", + toolbar_text: "#000", + ntp_background: "#fff", + ntp_text: "#000", + }, + properties: { + content_color_scheme: "system", + }, + }, + } + ); + + await testTheme("color_scheme: sytem override", kSystem, kSystem, { + theme: { + colors: { + toolbar: "#fff", + toolbar_text: "#000", + ntp_background: "#fff", + ntp_text: "#000", + }, + properties: { + color_scheme: "system", + content_color_scheme: "system", + }, + }, + }); +}); + +add_task(async function unified_theme() { + await SpecialPowers.pushPrefEnv({ + set: [["browser.theme.unified-color-scheme", true]], + }); + + await testTheme("Dark toolbar color", kDark, kDark, { + theme: { + colors: { + toolbar: "rgb(20, 17, 26)", + toolbar_text: "rgb(251, 29, 78)", + }, + }, + }); + + await testTheme("Light toolbar color", kLight, kLight, { + theme: { + colors: { + toolbar: "white", + toolbar_text: "black", + }, + }, + }); + + await SpecialPowers.popPrefEnv(); +}); diff -Nru firefox-99.0.1+build1/browser/components/extensions/test/browser/head.js firefox-100.0+build1/browser/components/extensions/test/browser/head.js --- firefox-99.0.1+build1/browser/components/extensions/test/browser/head.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/test/browser/head.js 2022-04-26 05:44:41.000000000 +0000 @@ -3,7 +3,7 @@ "use strict"; /* exported CustomizableUI makeWidgetId focusWindow forceGC - * getBrowserActionWidget + * getBrowserActionWidget assertPersistentListeners * clickBrowserAction clickPageAction clickPageActionInPanel * triggerPageActionWithKeyboard triggerPageActionWithKeyboardInPanel * triggerBrowserActionWithKeyboard @@ -105,6 +105,9 @@ void LoginManagerParent.recipeParentPromise; } +// Persistent Listener test functionality +const { assertPersistentListeners } = ExtensionTestUtils.testAssertions; + // Bug 1239884: Our tests occasionally hit a long GC pause at unpredictable // times in debug builds, which results in intermittent timeouts. Until we have // a better solution, we force a GC after certain strategic tests, which tend to @@ -255,7 +258,20 @@ return new Promise(resolve => setTimeout(resolve, ms)); } -async function promiseContentDimensions(browser) { +/** + * Retrieve the content dimensions (and wait until the content gets to the. + * size of the browser element they are loaded into, optionally tollerating + * size differences to prevent intermittent failures). + * + * @param {BrowserElement} browser + * The browser element where the content has been loaded. + * @param {number} [tolleratedWidthSizeDiff] + * width size difference to tollerate in pixels (defaults to 1). + * + * @returns {Promise<object>} + * An object with the dims retrieved from the content. + */ +async function promiseContentDimensions(browser, tolleratedWidthSizeDiff = 1) { // For remote browsers, each resize operation requires an asynchronous // round-trip to resize the content window. Since there's a certain amount of // unpredictability in the timing, mainly due to the unpredictability of @@ -264,9 +280,15 @@ let dims = await promisePossiblyInaccurateContentDimensions(browser); while ( - browser.clientWidth !== Math.round(dims.window.innerWidth) || + Math.abs(browser.clientWidth - dims.window.innerWidth) > + tolleratedWidthSizeDiff || browser.clientHeight !== Math.round(dims.window.innerHeight) ) { + const diffWidth = Math.abs(browser.clientWidth - dims.window.innerWidth); + const diffHeight = Math.abs(browser.clientHeight - dims.window.innerHeight); + info( + `Content dimension did not reached the expected size yet (diff: ${diffWidth}x${diffHeight}). Wait further.` + ); await delay(50); dims = await promisePossiblyInaccurateContentDimensions(browser); } diff -Nru firefox-99.0.1+build1/browser/components/extensions/test/browser/head_pageAction.js firefox-100.0+build1/browser/components/extensions/test/browser/head_pageAction.js --- firefox-99.0.1+build1/browser/components/extensions/test/browser/head_pageAction.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/test/browser/head_pageAction.js 2022-04-26 05:44:41.000000000 +0000 @@ -119,6 +119,34 @@ let currentWindow = window; let windows = []; + async function waitForDetails(details, windowId) { + function check() { + let { document } = Services.wm.getOuterWindowWithId(windowId); + let image = document.getElementById(pageActionId); + if (details == null) { + return image == null || image.getAttribute("disabled") == "true"; + } + let title = details.title || options.manifest.name; + return ( + !!image && + getListStyleImage(image) == details.icon && + image.getAttribute("tooltiptext") == title && + image.getAttribute("aria-label") == title + ); + // TODO: Popup URL. If this is updated, modify also checkDetails. + } + + // eslint-disable-next-line no-async-promise-executor + return new Promise(async resolve => { + let maxCounter = 10; + while (!check() && --maxCounter > 0) { + info("checks left: " + maxCounter); + await promiseAnimationFrame(currentWindow); + } + resolve(); + }); + } + function checkDetails(details, windowId) { let { document } = Services.wm.getOuterWindowWithId(windowId); let image = document.getElementById(pageActionId); @@ -139,7 +167,7 @@ title, "image aria-label is correct" ); - // TODO: Popup URL. + // TODO: Popup URL. If this is updated, modify also waitForDetails. } } @@ -155,7 +183,7 @@ ); } - await promiseAnimationFrame(currentWindow); + await waitForDetails(expecting, windowId); checkDetails(expecting, windowId); diff -Nru firefox-99.0.1+build1/browser/components/extensions/test/xpcshell/head.js firefox-100.0+build1/browser/components/extensions/test/xpcshell/head.js --- firefox-99.0.1+build1/browser/components/extensions/test/xpcshell/head.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/test/xpcshell/head.js 2022-04-26 05:44:41.000000000 +0000 @@ -1,6 +1,6 @@ "use strict"; -/* exported createHttpServer, promiseConsoleOutput */ +/* exported createHttpServer, promiseConsoleOutput, assertPersistentListeners */ var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); var { XPCOMUtils } = ChromeUtils.import( @@ -22,6 +22,9 @@ ExtensionTestUtils.init(this); +// Persistent Listener test functionality +const { assertPersistentListeners } = ExtensionTestUtils.testAssertions; + /** * Creates a new HttpServer for testing, and begins listening on the * specified port. Automatically shuts down the server when the test diff -Nru firefox-99.0.1+build1/browser/components/extensions/test/xpcshell/test_ext_bookmarks.js firefox-100.0+build1/browser/components/extensions/test/xpcshell/test_ext_bookmarks.js --- firefox-99.0.1+build1/browser/components/extensions/test/xpcshell/test_ext_bookmarks.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/test/xpcshell/test_ext_bookmarks.js 2022-04-26 05:44:41.000000000 +0000 @@ -2,12 +2,25 @@ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; +const { AddonTestUtils } = ChromeUtils.import( + "resource://testing-common/AddonTestUtils.jsm" +); + ChromeUtils.defineModuleGetter( this, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm" ); +AddonTestUtils.init(this); +AddonTestUtils.overrideCertDB(); +AddonTestUtils.createAppInfo( + "xpcshell@tests.mozilla.org", + "XPCShell", + "1", + "43" +); + add_task(async function test_bookmarks() { async function background() { let unsortedId, ourId; @@ -224,7 +237,7 @@ ); } - function checkOnRemoved(id, parentId, index, url, type = "folder") { + function checkOnRemoved(id, parentId, index, title, url, type = "folder") { let removedData = collectedEvents.pop(); browser.test.assertEq( "onRemoved", @@ -269,6 +282,11 @@ "onRemoved event received the expected node url" ); browser.test.assertEq( + title, + node.title, + "onRemoved event received the expected node title" + ); + browser.test.assertEq( type, node.type, "onRemoved event received the expected node type" @@ -474,6 +492,7 @@ ourId, bookmarkGuids.unfiledGuid, 0, + "new test title", "http://example.com/", "bookmark" ); @@ -1251,7 +1270,12 @@ collectedEvents.length, "1 expected events received" ); - checkOnRemoved(createdFolderId, bookmarkGuids.unfiledGuid, 1); + checkOnRemoved( + createdFolderId, + bookmarkGuids.unfiledGuid, + 1, + "Mozilla Folder" + ); return browser.bookmarks.search({}).then(searchResults => { browser.test.assertEq( @@ -1327,6 +1351,7 @@ createdSeparatorId, createdFolderId, 0, + "", "data:", "separator" ); @@ -1339,7 +1364,12 @@ collectedEvents.length, "1 expected events received" ); - checkOnRemoved(createdFolderId, bookmarkGuids.unfiledGuid, 3); + checkOnRemoved( + createdFolderId, + bookmarkGuids.unfiledGuid, + 3, + "Empty Folder" + ); return browser.test.assertRejects( browser.bookmarks.get(createdFolderId), @@ -1435,6 +1465,7 @@ createdFolderId, bookmarkGuids.unfiledGuid, 3, + "Empty Folder", undefined, "folder" ); @@ -1615,3 +1646,82 @@ await extension.unload(); }); + +add_task( + { + pref_set: [["extensions.eventPages.enabled", true]], + }, + async function test_bookmarks_event_page() { + await AddonTestUtils.promiseStartupManager(); + let extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "permanent", + manifest: { + browser_specific_settings: { gecko: { id: "eventpage@bookmarks" } }, + permissions: ["bookmarks"], + background: { persistent: false }, + }, + background() { + browser.bookmarks.onCreated.addListener(() => { + browser.test.sendMessage("onCreated"); + }); + browser.bookmarks.onRemoved.addListener(() => { + browser.test.sendMessage("onRemoved"); + }); + browser.bookmarks.onChanged.addListener(() => {}); + browser.bookmarks.onMoved.addListener(() => {}); + browser.test.sendMessage("ready"); + }, + }); + + const EVENTS = ["onCreated", "onRemoved", "onChanged", "onMoved"]; + await PlacesUtils.bookmarks.eraseEverything(); + + await extension.startup(); + await extension.awaitMessage("ready"); + for (let event of EVENTS) { + assertPersistentListeners(extension, "bookmarks", event, { + primed: false, + }); + } + + // test events waken background + await extension.terminateBackground(); + for (let event of EVENTS) { + assertPersistentListeners(extension, "bookmarks", event, { + primed: true, + }); + } + + let bookmark = { + type: PlacesUtils.bookmarks.TYPE_BOOKMARK, + url: `http://example.com/12345`, + title: `My bookmark 12345`, + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + }; + await PlacesUtils.bookmarks.insert(bookmark); + + await extension.awaitMessage("ready"); + await extension.awaitMessage("onCreated"); + for (let event of EVENTS) { + assertPersistentListeners(extension, "bookmarks", event, { + primed: false, + }); + } + + await AddonTestUtils.promiseRestartManager(); + await extension.awaitStartup(); + + for (let event of EVENTS) { + assertPersistentListeners(extension, "bookmarks", event, { + primed: true, + }); + } + + await PlacesUtils.bookmarks.eraseEverything(); + await extension.awaitMessage("ready"); + await extension.awaitMessage("onRemoved"); + + await extension.unload(); + await AddonTestUtils.promiseShutdownManager(); + } +); diff -Nru firefox-99.0.1+build1/browser/components/extensions/test/xpcshell/test_ext_history.js firefox-100.0+build1/browser/components/extensions/test/xpcshell/test_ext_history.js --- firefox-99.0.1+build1/browser/components/extensions/test/xpcshell/test_ext_history.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/extensions/test/xpcshell/test_ext_history.js 2022-04-26 05:44:41.000000000 +0000 @@ -2,6 +2,10 @@ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; +const { AddonTestUtils } = ChromeUtils.import( + "resource://testing-common/AddonTestUtils.jsm" +); + ChromeUtils.defineModuleGetter( this, "PlacesTestUtils", @@ -18,6 +22,15 @@ "resource://gre/modules/ExtensionCommon.jsm" ); +AddonTestUtils.init(this); +AddonTestUtils.overrideCertDB(); +AddonTestUtils.createAppInfo( + "xpcshell@tests.mozilla.org", + "XPCShell", + "1", + "43" +); + add_task(async function test_delete() { function background() { let historyClearedCount = 0; @@ -218,33 +231,33 @@ await extension.unload(); }); -add_task(async function test_search() { - const SINGLE_VISIT_URL = "http://example.com/"; - const DOUBLE_VISIT_URL = "http://example.com/2/"; - const MOZILLA_VISIT_URL = "http://mozilla.com/"; - const REFERENCE_DATE = new Date(); - // pages/visits to add via History.insert - const PAGE_INFOS = [ - { - url: SINGLE_VISIT_URL, - title: `test visit for ${SINGLE_VISIT_URL}`, - visits: [{ date: new Date(Number(REFERENCE_DATE) - 1000) }], - }, - { - url: DOUBLE_VISIT_URL, - title: `test visit for ${DOUBLE_VISIT_URL}`, - visits: [ - { date: REFERENCE_DATE }, - { date: new Date(Number(REFERENCE_DATE) - 2000) }, - ], - }, - { - url: MOZILLA_VISIT_URL, - title: `test visit for ${MOZILLA_VISIT_URL}`, - visits: [{ date: new Date(Number(REFERENCE_DATE) - 3000) }], - }, - ]; +const SINGLE_VISIT_URL = "http://example.com/"; +const DOUBLE_VISIT_URL = "http://example.com/2/"; +const MOZILLA_VISIT_URL = "http://mozilla.com/"; +const REFERENCE_DATE = new Date(); +// pages/visits to add via History.insert +const PAGE_INFOS = [ + { + url: SINGLE_VISIT_URL, + title: `test visit for ${SINGLE_VISIT_URL}`, + visits: [{ date: new Date(Number(REFERENCE_DATE) - 1000) }], + }, + { + url: DOUBLE_VISIT_URL, + title: `test visit for ${DOUBLE_VISIT_URL}`, + visits: [ + { date: REFERENCE_DATE }, + { date: new Date(Number(REFERENCE_DATE) - 2000) }, + ], + }, + { + url: MOZILLA_VISIT_URL, + title: `test visit for ${MOZILLA_VISIT_URL}`, + visits: [{ date: new Date(Number(REFERENCE_DATE) - 3000) }], + }, +]; +add_task(async function test_search() { function background(BGSCRIPT_REFERENCE_DATE) { const futureTime = Date.now() + 24 * 60 * 60 * 1000; @@ -786,3 +799,76 @@ await extension.unload(); }); + +add_task( + { + pref_set: [["extensions.eventPages.enabled", true]], + }, + async function test_history_event_page() { + await AddonTestUtils.promiseStartupManager(); + let extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "permanent", + manifest: { + browser_specific_settings: { gecko: { id: "eventpage@history" } }, + permissions: ["history"], + background: { persistent: false }, + }, + background() { + browser.history.onVisited.addListener(() => { + browser.test.sendMessage("onVisited"); + }); + browser.history.onVisitRemoved.addListener(() => { + browser.test.sendMessage("onVisitRemoved"); + }); + browser.history.onTitleChanged.addListener(() => {}); + browser.test.sendMessage("ready"); + }, + }); + + const EVENTS = ["onVisited", "onVisitRemoved", "onTitleChanged"]; + await PlacesUtils.history.clear(); + + await extension.startup(); + await extension.awaitMessage("ready"); + for (let event of EVENTS) { + assertPersistentListeners(extension, "history", event, { + primed: false, + }); + } + + // test events waken background + await extension.terminateBackground(); + for (let event of EVENTS) { + assertPersistentListeners(extension, "history", event, { + primed: true, + }); + } + + await PlacesUtils.history.insertMany(PAGE_INFOS); + + await extension.awaitMessage("ready"); + await extension.awaitMessage("onVisited"); + ok(true, "persistent event woke background"); + for (let event of EVENTS) { + assertPersistentListeners(extension, "history", event, { + primed: false, + }); + } + + await AddonTestUtils.promiseRestartManager(); + await extension.awaitStartup(); + + for (let event of EVENTS) { + assertPersistentListeners(extension, "history", event, { + primed: true, + }); + } + + await PlacesUtils.history.clear(); + await extension.awaitMessage("ready"); + await extension.awaitMessage("onVisitRemoved"); + + await extension.unload(); + await AddonTestUtils.promiseShutdownManager(); + } +); diff -Nru firefox-99.0.1+build1/browser/components/migration/ESEDBReader.jsm firefox-100.0+build1/browser/components/migration/ESEDBReader.jsm --- firefox-99.0.1+build1/browser/components/migration/ESEDBReader.jsm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/migration/ESEDBReader.jsm 2022-04-26 05:44:41.000000000 +0000 @@ -4,7 +4,16 @@ "use strict"; -var EXPORTED_SYMBOLS = ["ESEDBReader"]; /* exported ESEDBReader */ +var EXPORTED_SYMBOLS = [ + "ESEDBReader", + // The items below are exported for test purposes. + "ESE", + "KERNEL", + "gLibs", + "COLUMN_TYPES", + "declareESEFunction", + "loadLibraries", +]; const { ctypes } = ChromeUtils.import("resource://gre/modules/ctypes.jsm"); const { XPCOMUtils } = ChromeUtils.import( @@ -12,8 +21,7 @@ ); const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); XPCOMUtils.defineLazyGetter(this, "log", () => { - let ConsoleAPI = ChromeUtils.import("resource://gre/modules/Console.jsm", {}) - .ConsoleAPI; + let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm"); let consoleOptions = { maxLogLevelPref: "browser.esedbreader.loglevel", prefix: "ESEDBReader", diff -Nru firefox-99.0.1+build1/browser/components/migration/tests/unit/test_Edge_db_migration.js firefox-100.0+build1/browser/components/migration/tests/unit/test_Edge_db_migration.js --- firefox-99.0.1+build1/browser/components/migration/tests/unit/test_Edge_db_migration.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/migration/tests/unit/test_Edge_db_migration.js 2022-04-26 05:44:41.000000000 +0000 @@ -1,16 +1,14 @@ "use strict"; const { ctypes } = ChromeUtils.import("resource://gre/modules/ctypes.jsm"); -let eseBackStage = ChromeUtils.import( - "resource:///modules/ESEDBReader.jsm", - null -); -let ESE = eseBackStage.ESE; -let KERNEL = eseBackStage.KERNEL; -let gLibs = eseBackStage.gLibs; -let COLUMN_TYPES = eseBackStage.COLUMN_TYPES; -let declareESEFunction = eseBackStage.declareESEFunction; -let loadLibraries = eseBackStage.loadLibraries; +const { + ESE, + KERNEL, + gLibs, + COLUMN_TYPES, + declareESEFunction, + loadLibraries, +} = ChromeUtils.import("resource:///modules/ESEDBReader.jsm"); let gESEInstanceCounter = 1; diff -Nru firefox-99.0.1+build1/browser/components/moz.build firefox-100.0+build1/browser/components/moz.build --- firefox-99.0.1+build1/browser/components/moz.build 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/moz.build 2022-04-26 05:44:41.000000000 +0000 @@ -62,6 +62,13 @@ DIRS += ["build"] +if CONFIG["NIGHTLY_BUILD"]: + DIRS += [ + "colorways", + "myfirefox", + ] + + if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa": DIRS += ["touchbar"] elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows": diff -Nru firefox-99.0.1+build1/browser/components/myfirefox/jar.mn firefox-100.0+build1/browser/components/myfirefox/jar.mn --- firefox-99.0.1+build1/browser/components/myfirefox/jar.mn 1970-01-01 00:00:00.000000000 +0000 +++ firefox-100.0+build1/browser/components/myfirefox/jar.mn 2022-04-26 05:44:41.000000000 +0000 @@ -0,0 +1,8 @@ +# 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/. + +browser.jar: + content/browser/myfirefox.html + content/browser/myfirefox.js + diff -Nru firefox-99.0.1+build1/browser/components/myfirefox/moz.build firefox-100.0+build1/browser/components/myfirefox/moz.build --- firefox-99.0.1+build1/browser/components/myfirefox/moz.build 1970-01-01 00:00:00.000000000 +0000 +++ firefox-100.0+build1/browser/components/myfirefox/moz.build 2022-04-26 05:44:41.000000000 +0000 @@ -0,0 +1,10 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +JAR_MANIFESTS += ["jar.mn"] + +with Files("**"): + BUG_COMPONENT = ("Firefox", "General") diff -Nru firefox-99.0.1+build1/browser/components/myfirefox/myfirefox.html firefox-100.0+build1/browser/components/myfirefox/myfirefox.html --- firefox-99.0.1+build1/browser/components/myfirefox/myfirefox.html 1970-01-01 00:00:00.000000000 +0000 +++ firefox-100.0+build1/browser/components/myfirefox/myfirefox.html 2022-04-26 05:44:41.000000000 +0000 @@ -0,0 +1,18 @@ +<!-- 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/. --> + +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <meta http-equiv="Content-Security-Policy" content="default-src chrome:; object-src 'none'"> + <meta name="color-scheme" content="light dark"> + <title>This space intentionally left blank + + + + + + + diff -Nru firefox-99.0.1+build1/browser/components/myfirefox/myfirefox.js firefox-100.0+build1/browser/components/myfirefox/myfirefox.js --- firefox-99.0.1+build1/browser/components/myfirefox/myfirefox.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-100.0+build1/browser/components/myfirefox/myfirefox.js 2022-04-26 05:44:41.000000000 +0000 @@ -0,0 +1,21 @@ +/* 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/. */ + +"use strict"; + +const { XPCOMUtils } = ChromeUtils.import( + "resource://gre/modules/XPCOMUtils.jsm" +); + +XPCOMUtils.defineLazyModuleGetters(this, { + ColorwayClosetOpener: "resource:///modules/ColorwayClosetOpener.jsm", +}); + +window.addEventListener("load", () => { + document + .getElementById("colorway-closet-button") + .addEventListener("click", () => { + ColorwayClosetOpener.openModal(); + }); +}); diff -Nru firefox-99.0.1+build1/browser/components/newtab/aboutwelcome/AboutWelcomeChild.jsm firefox-100.0+build1/browser/components/newtab/aboutwelcome/AboutWelcomeChild.jsm --- firefox-99.0.1+build1/browser/components/newtab/aboutwelcome/AboutWelcomeChild.jsm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/newtab/aboutwelcome/AboutWelcomeChild.jsm 2022-04-26 05:44:41.000000000 +0000 @@ -178,6 +178,10 @@ Cu.exportFunction(this.AWSetRequestedLocales.bind(this), window, { defineAs: "AWSetRequestedLocales", }); + + Cu.exportFunction(this.AWSendToDeviceEmailsSupported.bind(this), window, { + defineAs: "AWSendToDeviceEmailsSupported", + }); } /** @@ -333,6 +337,12 @@ ); } + AWSendToDeviceEmailsSupported() { + return this.wrapPromise( + this.sendQuery("AWPage:SEND_TO_DEVICE_EMAILS_SUPPORTED") + ); + } + /** * @param {{type: string, detail?: any}} event * @override diff -Nru firefox-99.0.1+build1/browser/components/newtab/aboutwelcome/AboutWelcomeParent.jsm firefox-100.0+build1/browser/components/newtab/aboutwelcome/AboutWelcomeParent.jsm --- firefox-99.0.1+build1/browser/components/newtab/aboutwelcome/AboutWelcomeParent.jsm 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/newtab/aboutwelcome/AboutWelcomeParent.jsm 2022-04-26 05:44:41.000000000 +0000 @@ -13,6 +13,7 @@ XPCOMUtils.defineLazyModuleGetters(this, { AddonManager: "resource://gre/modules/AddonManager.jsm", + BrowserUtils: "resource://gre/modules/BrowserUtils.jsm", BuiltInThemes: "resource:///modules/BuiltInThemes.jsm", FxAccounts: "resource://gre/modules/FxAccounts.jsm", MigrationUtils: "resource:///modules/MigrationUtils.jsm", @@ -312,6 +313,9 @@ return LangPackMatcher.ensureLangPackInstalled(data); case "AWPage:SET_REQUESTED_LOCALES": return LangPackMatcher.setRequestedAppLocales(data); + case "AWPage:SEND_TO_DEVICE_EMAILS_SUPPORTED": { + return BrowserUtils.sendToDeviceEmailsSupported(); + } default: log.debug(`Unexpected event ${type} was not handled.`); } diff -Nru firefox-99.0.1+build1/browser/components/newtab/aboutwelcome/content/aboutwelcome.bundle.js firefox-100.0+build1/browser/components/newtab/aboutwelcome/content/aboutwelcome.bundle.js --- firefox-99.0.1+build1/browser/components/newtab/aboutwelcome/content/aboutwelcome.bundle.js 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/newtab/aboutwelcome/content/aboutwelcome.bundle.js 2022-04-26 05:44:41.000000000 +0000 @@ -2,283 +2,41 @@ * * NOTE: This file is generated by webpack from aboutwelcome.jsx * using the npm bundle task. - * + * */ -/******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { -/******/ return installedModules[moduleId].exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ i: moduleId, -/******/ l: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.l = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { -/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); -/******/ } -/******/ }; -/******/ -/******/ // define __esModule on exports -/******/ __webpack_require__.r = function(exports) { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ -/******/ // create a fake namespace object -/******/ // mode & 1: value is a module id, require it -/******/ // mode & 2: merge all properties of value into the ns -/******/ // mode & 4: return value when already ns object -/******/ // mode & 8|1: behave like require -/******/ __webpack_require__.t = function(value, mode) { -/******/ if(mode & 1) value = __webpack_require__(value); -/******/ if(mode & 8) return value; -/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; -/******/ var ns = Object.create(null); -/******/ __webpack_require__.r(ns); -/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); -/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); -/******/ return ns; -/******/ }; -/******/ -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function getDefault() { return module['default']; } : -/******/ function getModuleExports() { return module; }; -/******/ __webpack_require__.d(getter, 'a', getter); -/******/ return getter; -/******/ }; -/******/ -/******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; -/******/ -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = 0); -/******/ }) -/************************************************************************/ -/******/ ([ -/* 0 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); -/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2); -/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var _components_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3); -/* harmony import */ var _components_ReturnToAMO__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(11); -function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } - -/* 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/. */ - - - - - -class AboutWelcome extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent { - constructor(props) { - super(props); - this.state = { - metricsFlowUri: null - }; - this.fetchFxAFlowUri = this.fetchFxAFlowUri.bind(this); - } - - async fetchFxAFlowUri() { - this.setState({ - metricsFlowUri: await window.AWGetFxAMetricsFlowURI() - }); - } - - componentDidMount() { - if (!this.props.skipFxA) { - this.fetchFxAFlowUri(); - } // Record impression with performance data after allowing the page to load - - - const recordImpression = domState => { - const { - domComplete, - domInteractive - } = performance.getEntriesByType("navigation").pop(); - window.AWSendEventTelemetry({ - event: "IMPRESSION", - event_context: { - domComplete, - domInteractive, - mountStart: performance.getEntriesByName("mount").pop().startTime, - domState, - source: this.props.UTMTerm, - page: "about:welcome" - }, - message_id: this.props.messageId - }); - }; - - if (document.readyState === "complete") { - // Page might have already triggered a load event because it waited for async data, - // e.g., attribution, so the dom load timing could be of a empty content - // with domState in telemetry captured as 'complete' - recordImpression(document.readyState); - } else { - window.addEventListener("load", () => recordImpression("load"), { - once: true - }); - } // Captures user has seen about:welcome by setting - // firstrun.didSeeAboutWelcome pref to true and capturing welcome UI unique messageId - - - window.AWSendToParent("SET_WELCOME_MESSAGE_SEEN", this.props.messageId); - } - - render() { - const { - props - } = this; - - if (props.template === "return_to_amo") { - return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_components_ReturnToAMO__WEBPACK_IMPORTED_MODULE_3__["ReturnToAMO"], { - message_id: props.messageId, - type: props.type, - name: props.name, - url: props.url, - iconURL: props.iconURL, - themeScreenshots: props.screenshots, - metricsFlowUri: this.state.metricsFlowUri - }); - } - - return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_components_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_2__["MultiStageAboutWelcome"], { - message_id: props.messageId, - screens: props.screens, - metricsFlowUri: this.state.metricsFlowUri, - utm_term: props.UTMTerm, - transitions: props.transitions, - backdrop: props.backdrop, - appAndSystemLocaleInfo: props.appAndSystemLocaleInfo - }); - } - -} // Computes messageId and UTMTerm info used in telemetry - - -function ComputeTelemetryInfo(welcomeContent, experimentId, branchId) { - let messageId = welcomeContent.template === "return_to_amo" ? `RTAMO_DEFAULT_WELCOME_${welcomeContent.type.toUpperCase()}` : "DEFAULT_ID"; - let UTMTerm = "default"; - - if (welcomeContent.id) { - messageId = welcomeContent.id.toUpperCase(); - } - - if (experimentId && branchId) { - UTMTerm = `${experimentId}-${branchId}`.toLowerCase(); - } - - return { - messageId, - UTMTerm - }; -} - -async function retrieveRenderContent() { - // Feature config includes RTAMO attribution data if exists - // else below data in order specified - // user prefs - // experiment data - // defaults - let featureConfig = await window.AWGetFeatureConfig(); - let { - messageId, - UTMTerm - } = ComputeTelemetryInfo(featureConfig, featureConfig.slug, featureConfig.branch && featureConfig.branch.slug); - return { - featureConfig, - messageId, - UTMTerm - }; -} - -async function mount() { - let { - featureConfig: aboutWelcomeProps, - messageId, - UTMTerm - } = await retrieveRenderContent(); - react_dom__WEBPACK_IMPORTED_MODULE_1___default.a.render( /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(AboutWelcome, _extends({ - messageId: messageId, - UTMTerm: UTMTerm - }, aboutWelcomeProps)), document.getElementById("root")); -} - -performance.mark("mount"); -mount(); - -/***/ }), +/******/ (() => { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ([ +/* 0 */, /* 1 */ -/***/ (function(module, exports) { +/***/ ((module) => { module.exports = React; /***/ }), /* 2 */ -/***/ (function(module, exports) { +/***/ ((module) => { module.exports = ReactDOM; /***/ }), /* 3 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MultiStageAboutWelcome", function() { return MultiStageAboutWelcome; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SecondaryCTA", function() { return SecondaryCTA; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StepsIndicator", function() { return StepsIndicator; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "WelcomeScreen", function() { return WelcomeScreen; }); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "MultiStageAboutWelcome": () => (/* binding */ MultiStageAboutWelcome), +/* harmony export */ "SecondaryCTA": () => (/* binding */ SecondaryCTA), +/* harmony export */ "StepsIndicator": () => (/* binding */ StepsIndicator), +/* harmony export */ "WelcomeScreen": () => (/* binding */ WelcomeScreen) +/* harmony export */ }); /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); /* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5); /* harmony import */ var _MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6); -/* harmony import */ var _LanguageSwitcher__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(9); -/* harmony import */ var _asrouter_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(10); +/* harmony import */ var _LanguageSwitcher__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(10); +/* harmony import */ var _asrouter_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(11); /* 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/. */ @@ -294,12 +52,12 @@ let { screens } = props; - const [index, setScreenIndex] = Object(react__WEBPACK_IMPORTED_MODULE_0__["useState"])(0); - Object(react__WEBPACK_IMPORTED_MODULE_0__["useEffect"])(() => { + const [index, setScreenIndex] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(0); + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { // Send impression ping when respective screen first renders screens.forEach((screen, order) => { if (index === order) { - _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__["AboutWelcomeUtils"].sendImpressionTelemetry(`${props.message_id}_${order}_${screen.id}`); + _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendImpressionTelemetry(`${props.message_id}_${order}_${screen.id}`); } }); // Remember that a new screen has loaded for browser navigation @@ -307,7 +65,7 @@ window.history.pushState(index, ""); } }, [index]); - Object(react__WEBPACK_IMPORTED_MODULE_0__["useEffect"])(() => { + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { // Switch to the screen tracked in state (null for initial state) // or last screen index if a user navigates by pressing back // button from about:home @@ -321,21 +79,21 @@ window.addEventListener("popstate", handler); return () => window.removeEventListener("popstate", handler); }, []); - const [flowParams, setFlowParams] = Object(react__WEBPACK_IMPORTED_MODULE_0__["useState"])(null); + const [flowParams, setFlowParams] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null); const { metricsFlowUri } = props; - Object(react__WEBPACK_IMPORTED_MODULE_0__["useEffect"])(() => { + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { (async () => { if (metricsFlowUri) { - setFlowParams(await _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__["AboutWelcomeUtils"].fetchFlowParams(metricsFlowUri)); + setFlowParams(await _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.fetchFlowParams(metricsFlowUri)); } })(); }, [metricsFlowUri]); // Allow "in" style to render to actually transition towards regular state, // which also makes using browser back/forward navigation skip transitions. - const [transition, setTransition] = Object(react__WEBPACK_IMPORTED_MODULE_0__["useState"])(props.transitions ? "in" : ""); - Object(react__WEBPACK_IMPORTED_MODULE_0__["useEffect"])(() => { + const [transition, setTransition] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(props.transitions ? "in" : ""); + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { if (transition === "in") { requestAnimationFrame(() => requestAnimationFrame(() => setTransition(""))); } @@ -361,17 +119,17 @@ }; // Update top sites with default sites by region when region is available - const [region, setRegion] = Object(react__WEBPACK_IMPORTED_MODULE_0__["useState"])(null); - Object(react__WEBPACK_IMPORTED_MODULE_0__["useEffect"])(() => { + const [region, setRegion] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null); + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { (async () => { setRegion(await window.AWGetRegion()); })(); }, []); // Get the active theme so the rendering code can make it selected // by default. - const [activeTheme, setActiveTheme] = Object(react__WEBPACK_IMPORTED_MODULE_0__["useState"])(null); - const [initialTheme, setInitialTheme] = Object(react__WEBPACK_IMPORTED_MODULE_0__["useState"])(null); - Object(react__WEBPACK_IMPORTED_MODULE_0__["useEffect"])(() => { + const [activeTheme, setActiveTheme] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null); + const [initialTheme, setInitialTheme] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null); + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { (async () => { let theme = await window.AWGetSelectedTheme(); setInitialTheme(theme); @@ -380,16 +138,16 @@ }, []); const useImportable = props.message_id.includes("IMPORTABLE"); // Track whether we have already sent the importable sites impression telemetry - const importTelemetrySent = Object(react__WEBPACK_IMPORTED_MODULE_0__["useRef"])(false); - const [topSites, setTopSites] = Object(react__WEBPACK_IMPORTED_MODULE_0__["useState"])([]); - Object(react__WEBPACK_IMPORTED_MODULE_0__["useEffect"])(() => { + const importTelemetrySent = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(false); + const [topSites, setTopSites] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)([]); + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { (async () => { let DEFAULT_SITES = await window.AWGetDefaultSites(); const importable = JSON.parse(await window.AWGetImportableSites()); const showImportable = useImportable && importable.length >= 5; if (!importTelemetrySent.current) { - _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__["AboutWelcomeUtils"].sendImpressionTelemetry(`${props.message_id}_SITES`, { + _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendImpressionTelemetry(`${props.message_id}_SITES`, { display: showImportable ? "importable" : "static", importable: importable.length }); @@ -410,9 +168,9 @@ negotiatedLanguage, langPackInstallPhase, languageFilteredScreens - } = Object(_LanguageSwitcher__WEBPACK_IMPORTED_MODULE_4__["useLanguageSwitcher"])(props.appAndSystemLocaleInfo, screens, index, setScreenIndex); + } = (0,_LanguageSwitcher__WEBPACK_IMPORTED_MODULE_4__.useLanguageSwitcher)(props.appAndSystemLocaleInfo, screens, index, setScreenIndex); screens = languageFilteredScreens; - return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react__WEBPACK_IMPORTED_MODULE_0___default.a.Fragment, null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: `outer-wrapper onboardingContainer proton transition-${transition}`, style: props.backdrop ? { background: props.backdrop @@ -420,12 +178,16 @@ }, screens.map((screen, order) => { const isFirstCenteredScreen = screen.content.position !== "corner" && screen.order === centeredScreens[0].order; const isLastCenteredScreen = screen.content.position !== "corner" && screen.order === centeredScreens[centeredScreens.length - 1].order; - return index === order ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(WelcomeScreen, { + /* If first screen is corner positioned, don't include it in the count for the steps indicator. This assumes corner positioning will only be used on the first screen. */ + + const totalNumberOfScreens = screens[0].content.position === "corner" ? screens.length - 1 : screens.length; + return index === order ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(WelcomeScreen, { key: screen.id + order, id: screen.id, - totalNumberOfScreens: screens.length, + totalNumberOfScreens: totalNumberOfScreens, isFirstCenteredScreen: isFirstCenteredScreen, isLastCenteredScreen: isLastCenteredScreen, + startsWithCorner: screens[0].content.position === "corner", order: order, content: screen.content, navigate: handleTransition, @@ -444,13 +206,13 @@ }; const SecondaryCTA = props => { let targetElement = props.position ? `secondary_button_${props.position}` : `secondary_button`; - return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: props.position ? `secondary-cta ${props.position}` : "secondary-cta" - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { text: props.content[targetElement].text - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("span", null)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { text: props.content[targetElement].label - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("button", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "secondary text-link", value: targetElement, onClick: props.handleAction @@ -461,7 +223,7 @@ for (let i = 0; i < props.totalNumberOfScreens; i++) { let className = i === props.order ? "current" : ""; - steps.push( /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + steps.push( /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { key: i, className: `indicator ${className}` })); @@ -469,7 +231,7 @@ return steps; }; -class WelcomeScreen extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent { +class WelcomeScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) { constructor(props) { super(props); this.handleAction = this.handleAction.bind(this); @@ -482,7 +244,7 @@ } = action; if (type === "SHOW_FIREFOX_ACCOUNTS") { - let params = { ..._asrouter_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_5__["BASE_PARAMS"], + let params = { ..._asrouter_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_5__.BASE_PARAMS, utm_term: `aboutwelcome-${UTMTerm}-screen` }; @@ -497,7 +259,7 @@ }; } else if (type === "OPEN_URL") { let url = new URL(data.args); - Object(_asrouter_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_5__["addUtmParams"])(url, `aboutwelcome-${UTMTerm}-screen`); + (0,_asrouter_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_5__.addUtmParams)(url, `aboutwelcome-${UTMTerm}-screen`); if (action.addFlowParams && flowParams) { url.searchParams.append("device_id", flowParams.deviceId); @@ -510,7 +272,7 @@ }; } - _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__["AboutWelcomeUtils"].handleUserAction({ + _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.handleUserAction({ type, data }); @@ -530,7 +292,7 @@ } // Send telemetry before waiting on actions - _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__["AboutWelcomeUtils"].sendActionTelemetry(props.messageId, event.currentTarget.value); + _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, event.currentTarget.value, event.name); let { action } = targetContent; @@ -538,11 +300,11 @@ if (["OPEN_URL", "SHOW_FIREFOX_ACCOUNTS"].includes(action.type)) { this.handleOpenURL(action, props.flowParams, props.UTMTerm); } else if (action.type) { - _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__["AboutWelcomeUtils"].handleUserAction(action); // Wait until migration closes to complete the action + _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.handleUserAction(action); // Wait until migration closes to complete the action if (action.type === "SHOW_MIGRATION_WIZARD") { await window.AWWaitForMigrationClose(); - _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__["AboutWelcomeUtils"].sendActionTelemetry(props.messageId, "migrate_close"); + _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, "migrate_close"); } } // A special tiles.action.theme value indicates we should use the event's value vs provided value. @@ -559,12 +321,12 @@ } render() { - return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_3__["MultiStageProtonScreen"], { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_3__.MultiStageProtonScreen, { content: this.props.content, id: this.props.id, order: this.props.order, activeTheme: this.props.activeTheme, - totalNumberOfScreens: this.props.totalNumberOfScreens - 1, + totalNumberOfScreens: this.props.totalNumberOfScreens, appAndSystemLocaleInfo: this.props.appAndSystemLocaleInfo, negotiatedLanguage: this.props.negotiatedLanguage, langPackInstallPhase: this.props.langPackInstallPhase, @@ -572,6 +334,7 @@ messageId: this.props.messageId, isFirstCenteredScreen: this.props.isFirstCenteredScreen, isLastCenteredScreen: this.props.isLastCenteredScreen, + startsWithCorner: this.props.startsWithCorner, autoAdvance: this.props.autoAdvance }); } @@ -580,21 +343,24 @@ /***/ }), /* 4 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Localized", function() { return Localized; }); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Localized": () => (/* binding */ Localized) +/* harmony export */ }); /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); /* 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/. */ -const MS_STRING_PROP = "string_id"; +const CONFIGURABLE_STYLES = ["color", "fontSize"]; +const ZAP_SIZE_THRESHOLD = 160; /** * Based on the .text prop, localizes an inner element if a string_id * is provided, OR renders plain text, OR hides it if nothing is provided. + * Allows configuring of some styles including zap underline and color. * * Examples: * @@ -609,6 +375,7 @@ * Unlocalized text * jsx: *

+ *

* output: *

Welcome

*/ @@ -617,39 +384,66 @@ text, children }) => { + // Dynamically determine the size of the zap style. + const zapRef = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createRef(); + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { + const { + current + } = zapRef; + if (current) requestAnimationFrame(() => current === null || current === void 0 ? void 0 : current.classList.replace("short", current.getBoundingClientRect().width > ZAP_SIZE_THRESHOLD ? "long" : "short")); + }); // Skip rendering of children with no text. + if (!text) { return null; - } + } // Allow augmenting existing child container properties. - let props = children ? children.props : {}; - let textNode; - if (typeof text === "object" && text[MS_STRING_PROP]) { - props = { ...props - }; - props["data-l10n-id"] = text[MS_STRING_PROP]; + const props = { + children: [], + className: "", + style: {}, + ...(children === null || children === void 0 ? void 0 : children.props) + }; // Support nested Localized by starting with their children. + + const textNodes = props.children; // Pick desired fluent or raw/plain text to render. + + if (text.string_id) { + props["data-l10n-id"] = text.string_id; if (text.args) props["data-l10n-args"] = JSON.stringify(text.args); + } else if (text.raw) { + textNodes.push(text.raw); } else if (typeof text === "string") { - textNode = text; - } + textNodes.push(text); + } // Add zap style and content in a way that allows fluent to insert too. - if (!children) { - return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("span", props, textNode); - } else if (textNode) { - return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.cloneElement(children, props, textNode); - } - return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.cloneElement(children, props); + if (text.zap) { + props.className += " welcomeZap"; + textNodes.push( /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { + className: "short zap", + "data-l10n-name": "zap", + ref: zapRef + }, text.zap)); + } // Apply certain configurable styles. + + + CONFIGURABLE_STYLES.forEach(style => { + if (text[style]) props.style[style] = text[style]; + }); + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().cloneElement( // Provide a default container for the text if necessary. + children ?? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null), props, // Conditionally pass in as void elements can't accept empty array. + textNodes.length ? textNodes : null); }; /***/ }), /* 5 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AboutWelcomeUtils", function() { return AboutWelcomeUtils; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DEFAULT_RTAMO_CONTENT", function() { return DEFAULT_RTAMO_CONTENT; }); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "AboutWelcomeUtils": () => (/* binding */ AboutWelcomeUtils), +/* harmony export */ "DEFAULT_RTAMO_CONTENT": () => (/* binding */ DEFAULT_RTAMO_CONTENT) +/* harmony export */ }); /* 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/. */ @@ -666,9 +460,9 @@ }); }, - sendActionTelemetry(messageId, elementId) { + sendActionTelemetry(messageId, elementId, eventName = "CLICK_BUTTON") { const ping = { - event: "CLICK_BUTTON", + event: eventName, event_context: { source: elementId, page: "about:welcome" @@ -732,9 +526,7 @@ string_id: "return-to-amo-addon-title" }, help_text: { - text: { - string_id: "mr1-onboarding-welcome-image-caption" - } + string_id: "mr1-onboarding-welcome-image-caption" }, backdrop: "#212121 url(chrome://activity-stream/content/data/content/assets/proton-bkg.avif) center/cover no-repeat fixed", primary_button: { @@ -777,19 +569,21 @@ /***/ }), /* 6 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MultiStageProtonScreen", function() { return MultiStageProtonScreen; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ProtonScreen", function() { return ProtonScreen; }); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "MultiStageProtonScreen": () => (/* binding */ MultiStageProtonScreen), +/* harmony export */ "ProtonScreen": () => (/* binding */ ProtonScreen) +/* harmony export */ }); /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); /* harmony import */ var _Colorways__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7); -/* harmony import */ var _Themes__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8); -/* harmony import */ var _MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(3); -/* harmony import */ var _LanguageSwitcher__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(9); +/* harmony import */ var _MobileDownloads__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8); +/* harmony import */ var _Themes__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(9); +/* harmony import */ var _MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(3); +/* harmony import */ var _LanguageSwitcher__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(10); /* 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/. */ @@ -799,19 +593,21 @@ + const MultiStageProtonScreen = props => { const { autoAdvance, handleAction, order } = props; - Object(react__WEBPACK_IMPORTED_MODULE_0__["useEffect"])(() => { + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { if (autoAdvance) { const timer = setTimeout(() => { handleAction({ currentTarget: { value: autoAdvance - } + }, + name: "AUTO_ADVANCE" }); }, 20000); return () => clearTimeout(timer); @@ -819,7 +615,7 @@ return () => {}; }, [autoAdvance, handleAction, order]); - return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(ProtonScreen, { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(ProtonScreen, { content: props.content, id: props.id, order: props.order, @@ -828,6 +624,7 @@ handleAction: props.handleAction, isFirstCenteredScreen: props.isFirstCenteredScreen, isLastCenteredScreen: props.isLastCenteredScreen, + startsWithCorner: props.startsWithCorner, autoAdvance: props.autoAdvance, isRtamo: props.isRtamo, addonName: props.addonName, @@ -838,7 +635,7 @@ langPackInstallPhase: props.langPackInstallPhase }); }; -class ProtonScreen extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent { +class ProtonScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) { componentDidMount() { this.mainContentHeader.focus(); } @@ -862,33 +659,36 @@ const { content } = this.props; - return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react__WEBPACK_IMPORTED_MODULE_0___default.a.Fragment, null, content.tiles && content.tiles.type === "colorway" && content.tiles.colorways ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_Colorways__WEBPACK_IMPORTED_MODULE_2__["Colorways"], { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, content.tiles && content.tiles.type === "colorway" && content.tiles.colorways ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_Colorways__WEBPACK_IMPORTED_MODULE_2__.Colorways, { content: content, activeTheme: this.props.activeTheme, handleAction: this.props.handleAction - }) : null, content.tiles && content.tiles.type === "theme" && content.tiles.data ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_Themes__WEBPACK_IMPORTED_MODULE_3__["Themes"], { + }) : null, content.tiles && content.tiles.type === "theme" && content.tiles.data ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_Themes__WEBPACK_IMPORTED_MODULE_4__.Themes, { content: content, activeTheme: this.props.activeTheme, handleAction: this.props.handleAction + }) : null, content.tiles && content.tiles.type === "mobile_downloads" && content.tiles.data ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MobileDownloads__WEBPACK_IMPORTED_MODULE_3__.MobileDownloads, { + data: content.tiles.data, + handleAction: this.props.handleAction }) : null); } renderNoodles(includeNoodles, isCornerPosition) { - return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react__WEBPACK_IMPORTED_MODULE_0___default.a.Fragment, null, includeNoodles ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, includeNoodles ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: `noodle orange-L` - }) : null, includeNoodles ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + }) : null, includeNoodles ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: `noodle purple-C` - }) : null, isCornerPosition ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + }) : null, isCornerPosition ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: `noodle solid-L` - }) : null, includeNoodles ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + }) : null, includeNoodles ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: `noodle outline-L` - }) : null, includeNoodles ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + }) : null, includeNoodles ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: `noodle yellow-circle` }) : null); } renderLanguageSwitcher() { - return this.props.content.languageSwitcher ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_LanguageSwitcher__WEBPACK_IMPORTED_MODULE_5__["LanguageSwitcher"], { + return this.props.content.languageSwitcher ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_LanguageSwitcher__WEBPACK_IMPORTED_MODULE_6__.LanguageSwitcher, { content: this.props.content, handleAction: this.props.handleAction, negotiatedLanguage: this.props.negotiatedLanguage, @@ -897,8 +697,17 @@ }) : null; } + renderDismissButton() { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { + className: "dismiss-button", + onClick: this.props.handleAction, + value: "dismiss_button", + "data-l10n-id": "spotlight-dialog-close-button" + }); + } + render() { - var _this$props$appAndSys, _content$primary_butt; + var _this$props$appAndSys, _content$primary_butt, _content$primary_butt2; const { autoAdvance, @@ -911,12 +720,12 @@ } = this.props; const includeNoodles = content.has_noodles; const isCornerPosition = content.position === "corner"; - const hideStepsIndicator = autoAdvance || isCornerPosition; + const hideStepsIndicator = autoAdvance || isCornerPosition || isFirstCenteredScreen && isLastCenteredScreen; const textColorClass = content.text_color ? `${content.text_color}-text` : ""; // Assign proton screen style 'screen-1' or 'screen-2' by checking // if screen order is even or odd. const screenClassName = this.getScreenClassName(isCornerPosition, isFirstCenteredScreen, isLastCenteredScreen, includeNoodles); - return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("main", { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("main", { className: `screen ${this.props.id || ""} ${screenClassName} ${textColorClass}`, role: "dialog", pos: content.position || "center", @@ -925,75 +734,75 @@ ref: input => { this.mainContentHeader = input; } - }, isCornerPosition ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + }, isCornerPosition ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "section-left" - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "message-text" - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "spacer-top" - }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { + }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { text: content.hero_text - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("h1", null)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h1", null)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "spacer-bottom" - })), content.help_text && content.help_text.text ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { - text: content.help_text.text - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("span", { + })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: content.help_text + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "attrib-text" - })) : null) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + }))) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "section-main" - }, content.secondary_button_top ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_4__["SecondaryCTA"], { + }, content.secondary_button_top ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_5__.SecondaryCTA, { content: content, handleAction: this.props.handleAction, position: "top" - }) : null, this.renderNoodles(includeNoodles, isCornerPosition), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + }) : null, this.renderNoodles(includeNoodles, isCornerPosition), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: `main-content ${hideStepsIndicator ? "no-steps" : ""}`, style: content.background ? { background: content.background } : {} - }, content.logo ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + }, content.dismiss_button ? this.renderDismissButton() : null, content.logo ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: `brand-logo`, style: this.getLogoStyle(content.logo) - }) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + }) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: `${isRtamo ? "rtamo-icon" : "hide-rtamo-icon"}` - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("img", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", { className: `${isTheme ? "rtamo-theme-icon" : ""}`, src: this.props.iconURL, role: "presentation", alt: "" - })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "main-content-inner" - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: `welcome-text ${content.title_style || ""}` - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { text: content.title - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("h1", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h1", { id: "mainContentHeader" - })), content.subtitle ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { + })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { text: content.subtitle - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("h2", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h2", { "data-l10n-args": JSON.stringify({ "addon-name": this.props.addonName, ...((_this$props$appAndSys = this.props.appAndSystemLocaleInfo) === null || _this$props$appAndSys === void 0 ? void 0 : _this$props$appAndSys.displayNames) }) - })) : null), this.renderContentTiles(), this.renderLanguageSwitcher(), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { - text: content.primary_button ? content.primary_button.label : null - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("button", { + }))), this.renderContentTiles(), this.renderLanguageSwitcher(), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: (_content$primary_butt = content.primary_button) === null || _content$primary_butt === void 0 ? void 0 : _content$primary_butt.label + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "primary", value: "primary_button", - disabled: ((_content$primary_butt = content.primary_button) === null || _content$primary_butt === void 0 ? void 0 : _content$primary_butt.disabled) === true, + disabled: ((_content$primary_butt2 = content.primary_button) === null || _content$primary_butt2 === void 0 ? void 0 : _content$primary_butt2.disabled) === true, onClick: this.props.handleAction - })), content.secondary_button ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_4__["SecondaryCTA"], { + })), content.secondary_button ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_5__.SecondaryCTA, { content: content, handleAction: this.props.handleAction - }) : null)), hideStepsIndicator ? null : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("nav", { + }) : null)), hideStepsIndicator ? null : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("nav", { className: "steps", "data-l10n-id": "onboarding-welcome-steps-indicator", "data-l10n-args": JSON.stringify({ current: this.props.order, total }) - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("br", null), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("p", null), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_4__["StepsIndicator"], { - order: this.props.order - 1, + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("br", null), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("p", null), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_5__.StepsIndicator, { + order: this.props.startsWithCorner ? this.props.order - 1 : this.props.order, totalNumberOfScreens: total }))))); } @@ -1002,13 +811,14 @@ /***/ }), /* 7 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "VariationsCircle", function() { return VariationsCircle; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "computeColorWay", function() { return computeColorWay; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Colorways", function() { return Colorways; }); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "VariationsCircle": () => (/* binding */ VariationsCircle), +/* harmony export */ "computeColorWay": () => (/* binding */ computeColorWay), +/* harmony export */ "Colorways": () => (/* binding */ Colorways) +/* harmony export */ }); /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); @@ -1031,39 +841,38 @@ transition, variations } = props; - return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: `colorway-variations ${colorway} ${transition}`, next: nextColor - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "variations-disc" - }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { + }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { text: colorwayText - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "colorway-text" })), variations === null || variations === void 0 ? void 0 : variations.map(({ id, label, tooltip, description - }) => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { + }) => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { key: id, text: typeof tooltip === "object" ? tooltip : {} - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("label", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "theme colorway", title: label - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { text: typeof description === "object" ? description : {} - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("input", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "radio", value: colorway === "default" ? id : `${colorway}-${id}`, checked: activeTheme === null || activeTheme === void 0 ? void 0 : activeTheme.includes(id), name: "variationSelect", className: "sr-only input", - onClick: setVariation, - "data-l10n-attrs": "aria-description" - })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { + onClick: setVariation + })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { text: label - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: `text variation-button ${activeTheme !== null && activeTheme !== void 0 && activeTheme.includes(id) ? " selected" : ""}` })))))); }; // Return colorway as "default" for default theme variations Automatic, Light, Dark @@ -1085,14 +894,14 @@ } = props.content.tiles; // This sets a default value const activeId = computeColorWay(props.activeTheme, systemVariations); - const [colorwayId, setState] = Object(react__WEBPACK_IMPORTED_MODULE_0__["useState"])(activeId); // Update state any time activeTheme changes. + const [colorwayId, setState] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(activeId); // Update state any time activeTheme changes. - Object(react__WEBPACK_IMPORTED_MODULE_0__["useEffect"])(() => { + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { setState(computeColorWay(props.activeTheme, systemVariations)); }, [props.activeTheme]); // Allow "in" style to render to actually transition towards regular state. - const [transition, setTransition] = Object(react__WEBPACK_IMPORTED_MODULE_0__["useState"])(""); - Object(react__WEBPACK_IMPORTED_MODULE_0__["useEffect"])(() => { + const [transition, setTransition] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(""); + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { if (transition === "in") { // Figure out the variation to activate based on the active theme. Check // if it's a system variant then colorway variant falling back to default. @@ -1133,53 +942,52 @@ } } - return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "tiles-theme-container colorway" - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("fieldset", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("fieldset", { className: "tiles-theme-section" - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { text: props.content.subtitle - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("legend", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("legend", { className: "sr-only" })), colorways.map(({ id, label, tooltip, description - }) => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { + }) => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { key: id + label, text: typeof tooltip === "object" ? tooltip : {} - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("label", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "theme colorway", title: label, "data-l10n-args": JSON.stringify({ colorwayName: label }) - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { text: typeof description === "object" ? description : {} - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("span", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "sr-only colorway label", id: `${id}-label`, "data-l10n-args": JSON.stringify({ colorwayName: label }) - })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { + })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { text: typeof description === "object" ? description : {} - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("input", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "radio", "data-colorway": id, name: "theme", checked: colorwayId === id, className: "sr-only input", onClick: handleColorwayClick, - "data-l10n-attrs": "aria-description", "data-l10n-args": JSON.stringify({ colorwayName: label }), "aria-labelledby": `${id}-label` - })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: `icon colorway ${colorwayId === id ? "selected" : ""} ${id}` - })))))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(VariationsCircle, { + })))))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(VariationsCircle, { nextColor: colorwayId, transition: transition, variations: activeId === "default" ? systemVariations : variations, @@ -1192,11 +1000,72 @@ /***/ }), /* 8 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Themes", function() { return Themes; }); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "MarketplaceButtons": () => (/* binding */ MarketplaceButtons), +/* harmony export */ "MobileDownloads": () => (/* binding */ MobileDownloads) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); +/* 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/. */ + + +const MarketplaceButtons = props => { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("ul", { + className: "mobile-download-buttons" + }, props.buttons.includes("ios") ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("li", { + className: "ios" + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { + "data-l10n-id": "spotlight-ios-marketplace-button", + value: "ios", + onClick: props.handleAction + })) : null, props.buttons.includes("android") ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("li", { + className: "android" + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { + "data-l10n-id": "spotlight-android-marketplace-button", + value: "android", + onClick: props.handleAction + })) : null); +}; +const MobileDownloads = props => { + var _QRCode$image_overrid; + + const { + QR_code: QRCode + } = props.data; + const showEmailLink = props.data.email && window.AWSendToDeviceEmailsSupported(); + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "mobile-downloads" + }, QRCode ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", { + "data-l10n-id": QRCode.alt_text.string_id ? QRCode.alt_text.string_id : null, + className: "qr-code-image", + alt: typeof QRCode.alt_text === "string" ? QRCode.alt_text : "", + src: ((_QRCode$image_overrid = QRCode.image_overrides) === null || _QRCode$image_overrid === void 0 ? void 0 : _QRCode$image_overrid[document.documentElement.lang]) ?? QRCode.image_url + }) : null, showEmailLink ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: props.data.email.link_text + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { + className: "email-link", + value: "email_link", + onClick: props.handleAction + }))) : null, props.data.marketplace_buttons ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(MarketplaceButtons, { + buttons: props.data.marketplace_buttons, + handleAction: props.handleAction + }) : null); +}; + +/***/ }), +/* 9 */ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Themes": () => (/* binding */ Themes) +/* harmony export */ }); /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); @@ -1206,52 +1075,52 @@ const Themes = props => { - return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "tiles-theme-container" - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("fieldset", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("fieldset", { className: "tiles-theme-section" - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { text: props.content.subtitle - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("legend", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("legend", { className: "sr-only" })), props.content.tiles.data.map(({ theme, label, tooltip, description - }) => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { + }) => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { key: theme + label, text: typeof tooltip === "object" ? tooltip : {} - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("label", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "theme", title: theme + label - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { text: typeof description === "object" ? description : {} - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("input", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "radio", value: theme, name: "theme", checked: theme === props.activeTheme, className: "sr-only input", - onClick: props.handleAction, - "data-l10n-attrs": "aria-description" - })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + onClick: props.handleAction + })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: `icon ${theme === props.activeTheme ? " selected" : ""} ${theme}` - }), label && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { + }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { text: label - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "text" })))))))); }; /***/ }), -/* 9 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 10 */ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "useLanguageSwitcher", function() { return useLanguageSwitcher; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "LanguageSwitcher", function() { return LanguageSwitcher; }); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "useLanguageSwitcher": () => (/* binding */ useLanguageSwitcher), +/* harmony export */ "LanguageSwitcher": () => (/* binding */ LanguageSwitcher) +/* harmony export */ }); /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); @@ -1275,8 +1144,8 @@ const screen = screens[languageMismatchScreenIndex]; // If there is a mismatch, then Firefox can negotiate a better langpack to offer // the user. - const [negotiatedLanguage, setNegotiatedLanguage] = Object(react__WEBPACK_IMPORTED_MODULE_0__["useState"])(null); - Object(react__WEBPACK_IMPORTED_MODULE_0__["useEffect"])(function getNegotiatedLanguage() { + const [negotiatedLanguage, setNegotiatedLanguage] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null); + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function getNegotiatedLanguage() { if (!appAndSystemLocaleInfo) { return; } @@ -1287,22 +1156,23 @@ } (async () => { - const langPack = await window.AWNegotiateLangPackForLanguageMismatch(appAndSystemLocaleInfo); + const { + langPack, + langPackDisplayName + } = await window.AWNegotiateLangPackForLanguageMismatch(appAndSystemLocaleInfo); if (langPack) { - // Convert the BCP 47 identifiers into the proper display names. - // e.g. "fr-CA" -> "Canadian French". - const displayNames = new Intl.DisplayNames(appAndSystemLocaleInfo.appLocaleRaw, { - type: "language" - }); setNegotiatedLanguage({ - displayName: displayNames.of(langPack.target_locale), + langPackDisplayName, + appDisplayName: appAndSystemLocaleInfo.displayNames.appLanguage, langPack, - requestSystemLocales: [langPack.target_locale, appAndSystemLocaleInfo.appLocaleRaw] + requestSystemLocales: [langPack.target_locale, appAndSystemLocaleInfo.appLocaleRaw], + originalAppLocales: [appAndSystemLocaleInfo.appLocaleRaw] }); } else { setNegotiatedLanguage({ - displayName: null, + langPackDisplayName: null, + appDisplayName: null, langPack: null, requestSystemLocales: null }); @@ -1319,8 +1189,8 @@ * } */ - const [langPackInstallPhase, setLangPackInstallPhase] = Object(react__WEBPACK_IMPORTED_MODULE_0__["useState"])("before-installation"); - Object(react__WEBPACK_IMPORTED_MODULE_0__["useEffect"])(function ensureLangPackInstalled() { + const [langPackInstallPhase, setLangPackInstallPhase] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)("before-installation"); + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function ensureLangPackInstalled() { if (!negotiatedLanguage) { // There are no negotiated languages to download yet. return; @@ -1334,10 +1204,11 @@ setLangPackInstallPhase("installation-error"); }); }, [negotiatedLanguage]); - const shouldHideLanguageSwitcher = screen && (appAndSystemLocaleInfo === null || appAndSystemLocaleInfo === void 0 ? void 0 : appAndSystemLocaleInfo.matchType) !== "language-mismatch"; - const [languageFilteredScreens, setLanguageFilteredScreens] = Object(react__WEBPACK_IMPORTED_MODULE_0__["useState"])(screens); - Object(react__WEBPACK_IMPORTED_MODULE_0__["useEffect"])(function filterScreen() { - if (shouldHideLanguageSwitcher || (negotiatedLanguage === null || negotiatedLanguage === void 0 ? void 0 : negotiatedLanguage.langPack) === null) { + const [languageFilteredScreens, setLanguageFilteredScreens] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(screens); + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function filterScreen() { + // Remove the language screen if it exists (already removed for no live + // reload) and we either don't-need-to or can't switch. + if (screen && ((appAndSystemLocaleInfo === null || appAndSystemLocaleInfo === void 0 ? void 0 : appAndSystemLocaleInfo.matchType) !== "language-mismatch" || (negotiatedLanguage === null || negotiatedLanguage === void 0 ? void 0 : negotiatedLanguage.langPack) === null)) { if (screenIndex > languageMismatchScreenIndex) { setScreenIndex(screenIndex - 1); } @@ -1368,9 +1239,9 @@ langPackInstallPhase, messageId } = props; - const [isAwaitingLangpack, setIsAwaitingLangpack] = Object(react__WEBPACK_IMPORTED_MODULE_0__["useState"])(false); // Determine the status of the langpack installation. + const [isAwaitingLangpack, setIsAwaitingLangpack] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false); // Determine the status of the langpack installation. - Object(react__WEBPACK_IMPORTED_MODULE_0__["useEffect"])(() => { + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { if (isAwaitingLangpack && langPackInstallPhase !== "installing") { window.AWSetRequestedLocales(negotiatedLanguage.requestSystemLocales); requestAnimationFrame(() => { @@ -1385,12 +1256,12 @@ }, [isAwaitingLangpack, langPackInstallPhase]); // The message args are the localized language names. const withMessageArgs = obj => { - const displayName = negotiatedLanguage === null || negotiatedLanguage === void 0 ? void 0 : negotiatedLanguage.displayName; + const langPackDisplayName = negotiatedLanguage === null || negotiatedLanguage === void 0 ? void 0 : negotiatedLanguage.langPackDisplayName; - if (displayName) { + if (langPackDisplayName) { return { ...obj, args: { ...obj.args, - negotiatedLanguage: displayName + negotiatedLanguage: langPackDisplayName } }; } @@ -1412,50 +1283,50 @@ // the localized text elements rendering as blank, then filling in the text. - return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react__WEBPACK_IMPORTED_MODULE_0___default.a.Fragment, null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { style: { display: showPreloadingScreen ? "block" : "none" } - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("button", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "primary", value: "primary_button", disabled: true, type: "button" - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("img", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", { className: "language-loader", src: "chrome://browser/skin/tabbrowser/tab-connecting.png", alt: "" - }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { + }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { text: content.languageSwitcher.waiting - })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "secondary-cta" - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { text: content.languageSwitcher.skip - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("button", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { value: "decline_waiting", type: "button", className: "secondary text-link", onClick: handleAction - })))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + })))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { style: { display: showWaitingScreen ? "block" : "none" } - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("button", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "primary", value: "primary_button", disabled: true, type: "button" - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("img", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", { className: "language-loader", src: "chrome://browser/skin/tabbrowser/tab-connecting.png", alt: "" - }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { + }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { text: withMessageArgs(content.languageSwitcher.downloading) - })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "secondary-cta" - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { text: content.languageSwitcher.cancel - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("button", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { type: "button", className: "secondary text-link", onClick: () => { @@ -1466,39 +1337,39 @@ } }); } - })))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + })))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { style: { display: showReadyScreen ? "block" : "none" } - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { - text: withMessageArgs(content.languageSwitcher.switch) - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("button", { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "primary", value: "primary_button", onClick: () => { - _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__["AboutWelcomeUtils"].sendActionTelemetry(messageId, "download_langpack"); + _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(messageId, "download_langpack"); setIsAwaitingLangpack(true); } - }))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { - className: "secondary-cta" - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { - text: content.languageSwitcher.not_now - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("button", { + }, // This is the localized name from the Intl.DisplayNames API. + negotiatedLanguage === null || negotiatedLanguage === void 0 ? void 0 : negotiatedLanguage.langPackDisplayName)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { type: "button", - className: "secondary text-link", + className: "secondary", value: "decline", - onClick: handleAction - }))))); + onClick: event => { + window.AWSetRequestedLocales(negotiatedLanguage.originalAppLocales); + handleAction(event); + } + }, // This is the localized name from the Intl.DisplayNames API. + negotiatedLanguage === null || negotiatedLanguage === void 0 ? void 0 : negotiatedLanguage.appDisplayName)))); } /***/ }), -/* 10 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 11 */ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BASE_PARAMS", function() { return BASE_PARAMS; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "addUtmParams", function() { return addUtmParams; }); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "BASE_PARAMS": () => (/* binding */ BASE_PARAMS), +/* harmony export */ "addUtmParams": () => (/* binding */ addUtmParams) +/* harmony export */ }); /* 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/. */ @@ -1533,17 +1404,18 @@ } /***/ }), -/* 11 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 12 */ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -"use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ReturnToAMO", function() { return ReturnToAMO; }); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "ReturnToAMO": () => (/* binding */ ReturnToAMO) +/* harmony export */ }); /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); /* harmony import */ var _MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6); -/* harmony import */ var _asrouter_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(10); +/* harmony import */ var _asrouter_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(11); /* 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/. */ @@ -1551,7 +1423,7 @@ -class ReturnToAMO extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent { +class ReturnToAMO extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) { constructor(props) { super(props); this.fetchFlowParams = this.fetchFlowParams.bind(this); @@ -1561,7 +1433,7 @@ async fetchFlowParams() { if (this.props.metricsFlowUri) { this.setState({ - flowParams: await _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__["AboutWelcomeUtils"].fetchFlowParams(this.props.metricsFlowUri) + flowParams: await _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.fetchFlowParams(this.props.metricsFlowUri) }); } } @@ -1596,7 +1468,7 @@ url }; } else if (type === "SHOW_FIREFOX_ACCOUNTS") { - let params = { ..._asrouter_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_3__["BASE_PARAMS"], + let params = { ..._asrouter_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_3__.BASE_PARAMS, utm_term: `aboutwelcome-${utm_term}-screen` }; @@ -1611,11 +1483,11 @@ }; } - _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__["AboutWelcomeUtils"].handleUserAction({ + _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.handleUserAction({ type, data }); - _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__["AboutWelcomeUtils"].sendActionTelemetry(message_id, source_id); + _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.sendActionTelemetry(message_id, source_id); } render() { @@ -1636,12 +1508,12 @@ // directly inside JSON except for ReturnToAMOText which picks add-on name and icon from fluent string - return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "outer-wrapper onboardingContainer proton", style: content.backdrop ? { background: content.backdrop } : {} - }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_2__["MultiStageProtonScreen"], { + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_2__.MultiStageProtonScreen, { content: content, isRtamo: true, isTheme: type.includes("theme"), @@ -1657,7 +1529,235 @@ } } -ReturnToAMO.defaultProps = _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__["DEFAULT_RTAMO_CONTENT"]; +ReturnToAMO.defaultProps = _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__.DEFAULT_RTAMO_CONTENT; /***/ }) -/******/ ]); \ No newline at end of file +/******/ ]); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/compat get default export */ +/******/ (() => { +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = (module) => { +/******/ var getter = module && module.__esModule ? +/******/ () => (module['default']) : +/******/ () => (module); +/******/ __webpack_require__.d(getter, { a: getter }); +/******/ return getter; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. +(() => { +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2); +/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _components_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3); +/* harmony import */ var _components_ReturnToAMO__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(12); +function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } + +/* 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/. */ + + + + + +class AboutWelcome extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) { + constructor(props) { + super(props); + this.state = { + metricsFlowUri: null + }; + this.fetchFxAFlowUri = this.fetchFxAFlowUri.bind(this); + } + + async fetchFxAFlowUri() { + this.setState({ + metricsFlowUri: await window.AWGetFxAMetricsFlowURI() + }); + } + + componentDidMount() { + if (!this.props.skipFxA) { + this.fetchFxAFlowUri(); + } // Record impression with performance data after allowing the page to load + + + const recordImpression = domState => { + const { + domComplete, + domInteractive + } = performance.getEntriesByType("navigation").pop(); + window.AWSendEventTelemetry({ + event: "IMPRESSION", + event_context: { + domComplete, + domInteractive, + mountStart: performance.getEntriesByName("mount").pop().startTime, + domState, + source: this.props.UTMTerm, + page: "about:welcome" + }, + message_id: this.props.messageId + }); + }; + + if (document.readyState === "complete") { + // Page might have already triggered a load event because it waited for async data, + // e.g., attribution, so the dom load timing could be of a empty content + // with domState in telemetry captured as 'complete' + recordImpression(document.readyState); + } else { + window.addEventListener("load", () => recordImpression("load"), { + once: true + }); + } // Captures user has seen about:welcome by setting + // firstrun.didSeeAboutWelcome pref to true and capturing welcome UI unique messageId + + + window.AWSendToParent("SET_WELCOME_MESSAGE_SEEN", this.props.messageId); + } + + render() { + const { + props + } = this; + + if (props.template === "return_to_amo") { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_components_ReturnToAMO__WEBPACK_IMPORTED_MODULE_3__.ReturnToAMO, { + message_id: props.messageId, + type: props.type, + name: props.name, + url: props.url, + iconURL: props.iconURL, + themeScreenshots: props.screenshots, + metricsFlowUri: this.state.metricsFlowUri + }); + } + + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_components_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_2__.MultiStageAboutWelcome, { + message_id: props.messageId, + screens: props.screens, + metricsFlowUri: this.state.metricsFlowUri, + utm_term: props.UTMTerm, + transitions: props.transitions, + backdrop: props.backdrop, + appAndSystemLocaleInfo: props.appAndSystemLocaleInfo + }); + } + +} // Computes messageId and UTMTerm info used in telemetry + + +function ComputeTelemetryInfo(welcomeContent, experimentId, branchId) { + let messageId = welcomeContent.template === "return_to_amo" ? `RTAMO_DEFAULT_WELCOME_${welcomeContent.type.toUpperCase()}` : "DEFAULT_ID"; + let UTMTerm = "default"; + + if (welcomeContent.id) { + messageId = welcomeContent.id.toUpperCase(); + } + + if (experimentId && branchId) { + UTMTerm = `${experimentId}-${branchId}`.toLowerCase(); + } + + return { + messageId, + UTMTerm + }; +} + +async function retrieveRenderContent() { + // Feature config includes RTAMO attribution data if exists + // else below data in order specified + // user prefs + // experiment data + // defaults + let featureConfig = await window.AWGetFeatureConfig(); + let { + messageId, + UTMTerm + } = ComputeTelemetryInfo(featureConfig, featureConfig.slug, featureConfig.branch && featureConfig.branch.slug); + return { + featureConfig, + messageId, + UTMTerm + }; +} + +async function mount() { + let { + featureConfig: aboutWelcomeProps, + messageId, + UTMTerm + } = await retrieveRenderContent(); + react_dom__WEBPACK_IMPORTED_MODULE_1___default().render( /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(AboutWelcome, _extends({ + messageId: messageId, + UTMTerm: UTMTerm + }, aboutWelcomeProps)), document.getElementById("root")); +} + +performance.mark("mount"); +mount(); +})(); + +/******/ })() +; \ No newline at end of file diff -Nru firefox-99.0.1+build1/browser/components/newtab/aboutwelcome/content/aboutwelcome.css firefox-100.0+build1/browser/components/newtab/aboutwelcome/content/aboutwelcome.css --- firefox-99.0.1+build1/browser/components/newtab/aboutwelcome/content/aboutwelcome.css 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/newtab/aboutwelcome/content/aboutwelcome.css 2022-04-26 05:44:41.000000000 +0000 @@ -50,6 +50,7 @@ :root[dialogroot] { background-color: transparent; min-width: 504px; + overflow: hidden; } :root[dialogroot] body { padding: 0; @@ -57,9 +58,19 @@ :root[dialogroot] .onboardingContainer { overflow: visible; } +:root[dialogroot] .onboardingContainer:dir(rtl) { + transform: unset; +} :root[dialogroot] .onboardingContainer .screen { background-color: transparent; } +:root[dialogroot] .onboardingContainer .screen:dir(rtl) { + transform: unset; +} +:root[dialogroot] .onboardingContainer .screen.with-noodles { + min-width: 610px; + min-height: 610px; +} .onboardingContainer { text-align: center; @@ -131,6 +142,17 @@ min-height: 500px; overflow: hidden; } +.onboardingContainer .screen:is(.UPGRADE_PIN_FIREFOX, .UPGRADE_ONLY_DEFAULT, .UPGRADE_GET_STARTED) .brand-logo { + margin-top: 120px; +} +@media (prefers-reduced-motion: reduce) { + .onboardingContainer .screen:is(.UPGRADE_PIN_FIREFOX, .UPGRADE_ONLY_DEFAULT, .UPGRADE_GET_STARTED) .brand-logo { + background-image: url("chrome://activity-stream/content/data/content/assets/heart.svg") !important; + } +} +.onboardingContainer .screen:is(.UPGRADE_PIN_FIREFOX, .UPGRADE_ONLY_DEFAULT, .UPGRADE_GET_STARTED) .no-steps { + padding-bottom: 12px; +} .onboardingContainer .screen.light-text { --in-content-page-color: rgb(251, 251, 254); --in-content-primary-button-text-color: rgb(43, 42, 51); @@ -199,7 +221,7 @@ } .onboardingContainer .welcome-text h1 { font-size: 24px; - line-height: 36px; + line-height: 1.5; font-weight: 600; margin: 0 6px; letter-spacing: -0.02em; @@ -222,9 +244,7 @@ .onboardingContainer .welcome-text.fancy h1 { background-image: linear-gradient(90deg, #9059FF, #FF4AA2, #FF8C00, #FF4AA2, #9059FF); background-size: 400% auto; - color: #000; background-clip: text; - -webkit-background-clip: text; animation: shine 50s linear infinite; } @media (prefers-contrast: no-preference) { @@ -264,6 +284,7 @@ height: 0.3em; left: 0; z-index: -1; + transform: scaleY(3); } .onboardingContainer .welcomeZap .zap.short::after { background-image: url("chrome://activity-stream/content/data/content/assets/short-zap.svg"); @@ -584,6 +605,98 @@ .onboardingContainer .tiles-delayed { animation: fadein 0.4s; } +.onboardingContainer .mobile-downloads .qr-code-image { + margin: 24px 0 10px; + width: 113px; + height: 113px; +} +.onboardingContainer .mobile-downloads .email-link { + font-size: 16px; + font-weight: 400; + text-decoration: underline; + color: var(--in-content-link-color); +} +.onboardingContainer .mobile-downloads .email-link:hover { + background: none; +} +.onboardingContainer .mobile-downloads .ios button { + background-image: url("chrome://activity-stream/content/data/content/assets/app-marketplace-icons/en/ios.svg"); +} +.onboardingContainer .mobile-downloads .android button { + background-image: url("chrome://activity-stream/content/data/content/assets/app-marketplace-icons/en/android.png"); +} +:root[lang|=de] .ios button { + background-image: url("chrome://activity-stream/content/data/content/assets/app-marketplace-icons/de/ios.svg"); +} +:root[lang|=de] .android button { + background-image: url("chrome://activity-stream/content/data/content/assets/app-marketplace-icons/de/android.png"); +} +:root[lang|=es] .ios button { + background-image: url("chrome://activity-stream/content/data/content/assets/app-marketplace-icons/es/ios.svg"); +} +:root[lang|=es] .android button { + background-image: url("chrome://activity-stream/content/data/content/assets/app-marketplace-icons/es/android.png"); +} +:root[lang|=fr] .ios button { + background-image: url("chrome://activity-stream/content/data/content/assets/app-marketplace-icons/fr/ios.svg"); +} +:root[lang|=fr] .android button { + background-image: url("chrome://activity-stream/content/data/content/assets/app-marketplace-icons/fr/android.png"); +} +:root[lang|=pt] .ios button { + background-image: url("chrome://activity-stream/content/data/content/assets/app-marketplace-icons/pt/ios.svg"); +} +:root[lang|=pt] .android button { + background-image: url("chrome://activity-stream/content/data/content/assets/app-marketplace-icons/pt/android.png"); +} +:root[lang|=ru] .ios button { + background-image: url("chrome://activity-stream/content/data/content/assets/app-marketplace-icons/ru/ios.svg"); +} +:root[lang|=ru] .android button { + background-image: url("chrome://activity-stream/content/data/content/assets/app-marketplace-icons/ru/android.png"); +} +:root[lang|=zh] .ios button { + background-image: url("chrome://activity-stream/content/data/content/assets/app-marketplace-icons/zh/ios.svg"); +} +:root[lang|=zh] .android button { + background-image: url("chrome://activity-stream/content/data/content/assets/app-marketplace-icons/zh/android.png"); +} + +.onboardingContainer .mobile-download-buttons { + list-style: none; + padding: 10px 0; + margin: 0; +} +.onboardingContainer .mobile-download-buttons li { + display: inline-block; +} +.onboardingContainer .mobile-download-buttons li button { + display: inline-block; + height: 45px; + width: 152px; + background-repeat: no-repeat; + background-size: contain; + background-position: center; + box-shadow: none; +} +.onboardingContainer .mobile-download-buttons li:not(:first-child) { + margin-inline: 5px 0; +} +.onboardingContainer .dismiss-button { + padding: 0; + margin-block: 30px -45px; + margin-inline: 0 30px; + display: block; + float: inline-end; + background: url("chrome://global/skin/icons/close.svg") no-repeat center/cover; + height: 15px; + width: 15px; + align-self: end; + min-height: 15px; + min-width: 15px; + -moz-context-properties: fill; + fill: currentColor; +} @keyframes fadein { from { opacity: 0; @@ -693,15 +806,21 @@ .onboardingContainer .steps .indicator.current:last-of-type:first-of-type { opacity: 0; } -.onboardingContainer .primary { +.onboardingContainer .primary, +.onboardingContainer .secondary { font-size: 13px; line-height: 16px; padding: 11px 15px; transition: var(--transition); } -.onboardingContainer .primary.rtamo { +.onboardingContainer .primary.rtamo, +.onboardingContainer .secondary.rtamo { margin-top: 24px; } +.onboardingContainer .secondary { + background-color: var(--in-content-button-background); + color: var(--in-content-button-text-color); +} .onboardingContainer .noodle { display: block; background-repeat: no-repeat; @@ -746,7 +865,7 @@ justify-content: center; } .onboardingContainer [pos=corner] .section-left .message-text .spacer-top { - flex: 2; + flex: 1; } .onboardingContainer [pos=corner] .section-left .message-text .spacer-bottom { flex: 1; @@ -760,6 +879,7 @@ max-width: 5em; text-align: initial; white-space: pre-wrap; + text-shadow: 0 4px 8px rgba(0, 0, 0, 0.35); } .onboardingContainer [pos=corner] .section-left .attrib-text { height: 18px; @@ -777,12 +897,12 @@ .onboardingContainer [pos=corner] .section-main .main-content { background: transparent; box-shadow: none; - display: block; + display: flex; position: absolute; - height: 350px; - width: 295px; - bottom: -60px; - inset-inline-end: 80px; + height: auto; + width: 350px; + bottom: 0; + inset-inline-end: 30px; transition: var(--transition); } .onboardingContainer [pos=corner] .section-main .brand-logo { @@ -799,6 +919,9 @@ .onboardingContainer [pos=corner] .section-main .welcome-text { transition-delay: 1.2s; } +.onboardingContainer [pos=corner] .section-main .welcome-text h2 { + margin: 10px 6px; +} .onboardingContainer [pos=corner] .solid-L { width: 1300px; height: 1050px; @@ -863,6 +986,7 @@ transition-delay: 0.9s; } .onboardingContainer .dialog-initial .primary, +.onboardingContainer .dialog-initial .secondary, .onboardingContainer .dialog-initial .secondary-cta, .onboardingContainer .dialog-initial .steps { transition-delay: 1s; @@ -872,6 +996,7 @@ transition-delay: 0.2s; } .onboardingContainer .screen:not(.dialog-initial):not([pos=corner]) .primary, +.onboardingContainer .screen:not(.dialog-initial):not([pos=corner]) .secondary, .onboardingContainer .screen:not(.dialog-initial):not([pos=corner]) .secondary-cta { transition-delay: 0.4s; } @@ -914,6 +1039,7 @@ .onboardingContainer.transition-in [pos=corner] .brand-logo, .onboardingContainer.transition-in [pos=corner] .welcome-text, .onboardingContainer.transition-in [pos=corner] .primary, +.onboardingContainer.transition-in [pos=corner] .secondary, .onboardingContainer.transition-in [pos=corner] .secondary-cta:not(.top), .onboardingContainer.transition-in [pos=corner] .message-text { opacity: 0; @@ -938,6 +1064,7 @@ .onboardingContainer.transition-in .screen:not([pos=corner]) .colorway-variations, .onboardingContainer.transition-in .screen:not([pos=corner]) .tiles-theme-section, .onboardingContainer.transition-in .screen:not([pos=corner]) .primary, +.onboardingContainer.transition-in .screen:not([pos=corner]) .secondary, .onboardingContainer.transition-in .screen:not([pos=corner]) .secondary-cta:not(.top) { opacity: 0; translate: 0 calc(-1 * var(--translate)); @@ -980,6 +1107,7 @@ transition-delay: 0.2s; } .onboardingContainer.transition-out .screen:not(.dialog-last):not([pos=corner]) .primary, +.onboardingContainer.transition-out .screen:not(.dialog-last):not([pos=corner]) .secondary, .onboardingContainer.transition-out .screen:not(.dialog-last):not([pos=corner]) .secondary-cta:not(.top) { opacity: 0; translate: 0 var(--translate); @@ -998,5 +1126,5 @@ transition-delay: 0.4s; } .onboardingContainer .screen.with-noodles:not(.corner) .section-main { - height: 503px; + height: 504px; } diff -Nru firefox-99.0.1+build1/browser/components/newtab/aboutwelcome/content/aboutwelcome.html firefox-100.0+build1/browser/components/newtab/aboutwelcome/content/aboutwelcome.html --- firefox-99.0.1+build1/browser/components/newtab/aboutwelcome/content/aboutwelcome.html 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/newtab/aboutwelcome/content/aboutwelcome.html 2022-04-26 05:44:41.000000000 +0000 @@ -16,6 +16,7 @@ + + {/* Waiting to download the language screen. */}
+ {/* The typical ready screen. */}
- -
-
- -
diff -Nru firefox-99.0.1+build1/browser/components/newtab/content-src/aboutwelcome/components/MobileDownloads.jsx firefox-100.0+build1/browser/components/newtab/content-src/aboutwelcome/components/MobileDownloads.jsx --- firefox-99.0.1+build1/browser/components/newtab/content-src/aboutwelcome/components/MobileDownloads.jsx 1970-01-01 00:00:00.000000000 +0000 +++ firefox-100.0+build1/browser/components/newtab/content-src/aboutwelcome/components/MobileDownloads.jsx 2022-04-26 05:44:41.000000000 +0000 @@ -0,0 +1,74 @@ +/* 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/. */ + +import React from "react"; +import { Localized } from "./MSLocalized"; + +export const MarketplaceButtons = props => { + return ( +
    + {props.buttons.includes("ios") ? ( +
  • + +
  • + ) : null} + {props.buttons.includes("android") ? ( +
  • + +
  • + ) : null} +
+ ); +}; + +export const MobileDownloads = props => { + const { QR_code: QRCode } = props.data; + const showEmailLink = + props.data.email && window.AWSendToDeviceEmailsSupported(); + + return ( +
+ {/* Avoid use of Localized element to set alt text here as a plain string value + results in a React error due to "dangerouslySetInnerHTML" */} + {QRCode ? ( + {typeof + ) : null} + {showEmailLink ? ( +
+ +
+ ) : null} + {props.data.marketplace_buttons ? ( + + ) : null} +
+ ); +}; diff -Nru firefox-99.0.1+build1/browser/components/newtab/content-src/aboutwelcome/components/MSLocalized.jsx firefox-100.0+build1/browser/components/newtab/content-src/aboutwelcome/components/MSLocalized.jsx --- firefox-99.0.1+build1/browser/components/newtab/content-src/aboutwelcome/components/MSLocalized.jsx 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/newtab/content-src/aboutwelcome/components/MSLocalized.jsx 2022-04-26 05:44:41.000000000 +0000 @@ -2,12 +2,14 @@ * 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/. */ -import React from "react"; -const MS_STRING_PROP = "string_id"; +import React, { useEffect } from "react"; +const CONFIGURABLE_STYLES = ["color", "fontSize"]; +const ZAP_SIZE_THRESHOLD = 160; /** * Based on the .text prop, localizes an inner element if a string_id * is provided, OR renders plain text, OR hides it if nothing is provided. + * Allows configuring of some styles including zap underline and color. * * Examples: * @@ -22,30 +24,67 @@ * Unlocalized text * jsx: *

+ *

* output: *

Welcome

*/ export const Localized = ({ text, children }) => { + // Dynamically determine the size of the zap style. + const zapRef = React.createRef(); + useEffect(() => { + const { current } = zapRef; + if (current) + requestAnimationFrame(() => + current?.classList.replace( + "short", + current.getBoundingClientRect().width > ZAP_SIZE_THRESHOLD + ? "long" + : "short" + ) + ); + }); + + // Skip rendering of children with no text. if (!text) { return null; } - let props = children ? children.props : {}; - let textNode; - - if (typeof text === "object" && text[MS_STRING_PROP]) { - props = { ...props }; - props["data-l10n-id"] = text[MS_STRING_PROP]; + // Allow augmenting existing child container properties. + const props = { children: [], className: "", style: {}, ...children?.props }; + // Support nested Localized by starting with their children. + const textNodes = props.children; + + // Pick desired fluent or raw/plain text to render. + if (text.string_id) { + props["data-l10n-id"] = text.string_id; if (text.args) props["data-l10n-args"] = JSON.stringify(text.args); + } else if (text.raw) { + textNodes.push(text.raw); } else if (typeof text === "string") { - textNode = text; + textNodes.push(text); } - if (!children) { - return React.createElement("span", props, textNode); - } else if (textNode) { - return React.cloneElement(children, props, textNode); + // Add zap style and content in a way that allows fluent to insert too. + if (text.zap) { + props.className += " welcomeZap"; + textNodes.push( + + {text.zap} + + ); } - return React.cloneElement(children, props); + + // Apply certain configurable styles. + CONFIGURABLE_STYLES.forEach(style => { + if (text[style]) props.style[style] = text[style]; + }); + + return React.cloneElement( + // Provide a default container for the text if necessary. + children ?? , + props, + // Conditionally pass in as void elements can't accept empty array. + textNodes.length ? textNodes : null + ); }; diff -Nru firefox-99.0.1+build1/browser/components/newtab/content-src/aboutwelcome/components/MultiStageAboutWelcome.jsx firefox-100.0+build1/browser/components/newtab/content-src/aboutwelcome/components/MultiStageAboutWelcome.jsx --- firefox-99.0.1+build1/browser/components/newtab/content-src/aboutwelcome/components/MultiStageAboutWelcome.jsx 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/newtab/content-src/aboutwelcome/components/MultiStageAboutWelcome.jsx 2022-04-26 05:44:41.000000000 +0000 @@ -169,13 +169,19 @@ const isLastCenteredScreen = screen.content.position !== "corner" && screen.order === centeredScreens[centeredScreens.length - 1].order; + /* If first screen is corner positioned, don't include it in the count for the steps indicator. This assumes corner positioning will only be used on the first screen. */ + const totalNumberOfScreens = + screens[0].content.position === "corner" + ? screens.length - 1 + : screens.length; return index === order ? ( ); diff -Nru firefox-99.0.1+build1/browser/components/newtab/content-src/aboutwelcome/components/MultiStageProtonScreen.jsx firefox-100.0+build1/browser/components/newtab/content-src/aboutwelcome/components/MultiStageProtonScreen.jsx --- firefox-99.0.1+build1/browser/components/newtab/content-src/aboutwelcome/components/MultiStageProtonScreen.jsx 2022-04-13 11:24:05.000000000 +0000 +++ firefox-100.0+build1/browser/components/newtab/content-src/aboutwelcome/components/MultiStageProtonScreen.jsx 2022-04-26 05:44:41.000000000 +0000 @@ -5,6 +5,7 @@ import React, { useEffect } from "react"; import { Localized } from "./MSLocalized"; import { Colorways } from "./Colorways"; +import { MobileDownloads } from "./MobileDownloads"; import { Themes } from "./Themes"; import { SecondaryCTA, StepsIndicator } from "./MultiStageAboutWelcome"; import { LanguageSwitcher } from "./LanguageSwitcher"; @@ -18,6 +19,7 @@ currentTarget: { value: autoAdvance, }, + name: "AUTO_ADVANCE", }); }, 20000); return () => clearTimeout(timer); @@ -35,6 +37,7 @@ handleAction={props.handleAction} isFirstCenteredScreen={props.isFirstCenteredScreen} isLastCenteredScreen={props.isLastCenteredScreen} + startsWithCorner={props.startsWithCorner} autoAdvance={props.autoAdvance} isRtamo={props.isRtamo} addonName={props.addonName} @@ -99,6 +102,14 @@ handleAction={this.props.handleAction} /> ) : null} + {content.tiles && + content.tiles.type === "mobile_downloads" && + content.tiles.data ? ( + + ) : null} ); } @@ -127,6 +138,17 @@ ) : null; } + renderDismissButton() { + return ( + + ); + } + render() { const { autoAdvance, @@ -139,7 +161,10 @@ } = this.props; const includeNoodles = content.has_noodles; const isCornerPosition = content.position === "corner"; - const hideStepsIndicator = autoAdvance || isCornerPosition; + const hideStepsIndicator = + autoAdvance || + isCornerPosition || + (isFirstCenteredScreen && isLastCenteredScreen); const textColorClass = content.text_color ? `${content.text_color}-text` : ""; @@ -173,11 +198,9 @@
- {content.help_text && content.help_text.text ? ( - - - - ) : null} + + + ) : null}
@@ -193,6 +216,7 @@ className={`main-content ${hideStepsIndicator ? "no-steps" : ""}`} style={content.background ? { background: content.background } : {}} > + {content.dismiss_button ? this.renderDismissButton() : null} {content.logo ? (

- {content.subtitle ? ( - -

- - ) : null} + +

+

{this.renderContentTiles()} {this.renderLanguageSwitcher()}
- +