diff options
Diffstat (limited to 'dom/svg')
-rw-r--r-- | dom/svg/SVGAElement.cpp | 7 | ||||
-rw-r--r-- | dom/svg/SVGAElement.h | 2 | ||||
-rw-r--r-- | dom/svg/SVGAnimatedLength.cpp | 21 | ||||
-rw-r--r-- | dom/svg/SVGAnimatedLength.h | 3 | ||||
-rw-r--r-- | dom/svg/SVGContentUtils.cpp | 28 | ||||
-rw-r--r-- | dom/svg/SVGContentUtils.h | 13 | ||||
-rw-r--r-- | dom/svg/SVGDefsElement.h | 2 | ||||
-rw-r--r-- | dom/svg/SVGGeometryElement.cpp | 2 | ||||
-rw-r--r-- | dom/svg/SVGGraphicsElement.cpp | 6 | ||||
-rw-r--r-- | dom/svg/SVGGraphicsElement.h | 2 | ||||
-rw-r--r-- | dom/svg/SVGLength.cpp | 36 | ||||
-rw-r--r-- | dom/svg/SVGSwitchElement.cpp | 56 | ||||
-rw-r--r-- | dom/svg/SVGSwitchElement.h | 3 | ||||
-rw-r--r-- | dom/svg/SVGSymbolElement.cpp | 4 | ||||
-rw-r--r-- | dom/svg/SVGSymbolElement.h | 2 | ||||
-rw-r--r-- | dom/svg/SVGTests.cpp | 158 | ||||
-rw-r--r-- | dom/svg/SVGTests.h | 44 | ||||
-rw-r--r-- | dom/svg/test/test_switch.xhtml | 107 | ||||
-rw-r--r-- | dom/svg/test/test_tabindex.html | 15 |
19 files changed, 277 insertions, 234 deletions
diff --git a/dom/svg/SVGAElement.cpp b/dom/svg/SVGAElement.cpp index 1533cceb34..bffc3d1a98 100644 --- a/dom/svg/SVGAElement.cpp +++ b/dom/svg/SVGAElement.cpp @@ -6,11 +6,10 @@ #include "mozilla/dom/SVGAElement.h" -#include "mozilla/Attributes.h" #include "mozilla/EventDispatcher.h" -#include "mozilla/dom/BindContext.h" #include "mozilla/dom/DocumentInlines.h" #include "mozilla/dom/SVGAElementBinding.h" +#include "mozilla/FocusModel.h" #include "nsCOMPtr.h" #include "nsContentUtils.h" #include "nsGkAtoms.h" @@ -158,7 +157,7 @@ void SVGAElement::UnbindFromTree(UnbindContext& aContext) { int32_t SVGAElement::TabIndexDefault() { return 0; } -Focusable SVGAElement::IsFocusableWithoutStyle(bool aWithMouse) { +Focusable SVGAElement::IsFocusableWithoutStyle(IsFocusableFlags) { Focusable result; if (IsSVGFocusable(&result.mFocusable, &result.mTabIndex)) { return result; @@ -182,7 +181,7 @@ Focusable SVGAElement::IsFocusableWithoutStyle(bool aWithMouse) { return {}; } } - if ((sTabFocusModel & eTabFocus_linksMask) == 0) { + if (!FocusModel::IsTabFocusable(TabFocusableType::Links)) { result.mTabIndex = -1; } return result; diff --git a/dom/svg/SVGAElement.h b/dom/svg/SVGAElement.h index bbb552fce6..5e476c033c 100644 --- a/dom/svg/SVGAElement.h +++ b/dom/svg/SVGAElement.h @@ -50,7 +50,7 @@ class SVGAElement final : public SVGAElementBase, public Link { void UnbindFromTree(UnbindContext&) override; int32_t TabIndexDefault() override; - Focusable IsFocusableWithoutStyle(bool aWithMouse) override; + Focusable IsFocusableWithoutStyle(IsFocusableFlags) override; void GetLinkTarget(nsAString& aTarget) override; already_AddRefed<nsIURI> GetHrefURI() const override; diff --git a/dom/svg/SVGAnimatedLength.cpp b/dom/svg/SVGAnimatedLength.cpp index 081db5704a..156fde0e23 100644 --- a/dom/svg/SVGAnimatedLength.cpp +++ b/dom/svg/SVGAnimatedLength.cpp @@ -225,6 +225,10 @@ CSSSize SVGElementMetrics::GetCSSViewportSize() const { return GetCSSViewportSizeFromContext(context); } +float SVGElementMetrics::GetLineHeight(Type aType) const { + return SVGContentUtils::GetLineHeight(GetElementForType(aType)); +} + bool SVGElementMetrics::EnsureCtx() const { if (!mCtx && mSVGElement) { mCtx = mSVGElement->GetCtx(); @@ -298,6 +302,23 @@ CSSSize NonSVGFrameUserSpaceMetrics::GetCSSViewportSize() const { return GetCSSViewportSizeFromContext(mFrame->PresContext()); } +float NonSVGFrameUserSpaceMetrics::GetLineHeight(Type aType) const { + auto* context = mFrame->PresContext(); + switch (aType) { + case Type::This: { + const auto lineHeightAu = ReflowInput::CalcLineHeight( + *mFrame->Style(), context, mFrame->GetContent(), NS_UNCONSTRAINEDSIZE, + 1.0f); + return CSSPixel::FromAppUnits(lineHeightAu); + } + case Type::Root: + return SVGContentUtils::GetLineHeight( + context->Document()->GetRootElement()); + } + MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?"); + return 1.0f; +} + float UserSpaceMetricsWithSize::GetAxisLength(uint8_t aCtxType) const { gfx::Size size = GetSize(); float length; diff --git a/dom/svg/SVGAnimatedLength.h b/dom/svg/SVGAnimatedLength.h index 6874261cda..0e5c228667 100644 --- a/dom/svg/SVGAnimatedLength.h +++ b/dom/svg/SVGAnimatedLength.h @@ -53,6 +53,7 @@ class UserSpaceMetrics { float GetCapHeight(Type aType) const; virtual float GetAxisLength(uint8_t aCtxType) const = 0; virtual CSSSize GetCSSViewportSize() const = 0; + virtual float GetLineHeight(Type aType) const = 0; protected: virtual GeckoFontMetrics GetFontMetricsForType(Type aType) const = 0; @@ -75,6 +76,7 @@ class SVGElementMetrics : public UserSpaceMetrics { } float GetAxisLength(uint8_t aCtxType) const override; CSSSize GetCSSViewportSize() const override; + float GetLineHeight(Type aType) const override; private: bool EnsureCtx() const; @@ -93,6 +95,7 @@ class NonSVGFrameUserSpaceMetrics : public UserSpaceMetricsWithSize { float GetEmLength(Type aType) const override; gfx::Size GetSize() const override; CSSSize GetCSSViewportSize() const override; + float GetLineHeight(Type aType) const override; private: GeckoFontMetrics GetFontMetricsForType(Type aType) const override; diff --git a/dom/svg/SVGContentUtils.cpp b/dom/svg/SVGContentUtils.cpp index 72534c12e8..6a87c64acc 100644 --- a/dom/svg/SVGContentUtils.cpp +++ b/dom/svg/SVGContentUtils.cpp @@ -433,6 +433,26 @@ float SVGContentUtils::GetFontXHeight(const ComputedStyle* aComputedStyle, return nsPresContext::AppUnitsToFloatCSSPixels(xHeight) / aPresContext->TextZoom(); } + +float SVGContentUtils::GetLineHeight(const Element* aElement) { + float result = 16.0f * ReflowInput::kNormalLineHeightFactor; + if (!aElement) { + return result; + } + SVGGeometryProperty::DoForComputedStyle( + aElement, [&](const ComputedStyle* style) { + auto* context = nsContentUtils::GetContextForContent(aElement); + if (!context) { + return; + } + const auto lineHeightAu = ReflowInput::CalcLineHeight( + *style, context, aElement, NS_UNCONSTRAINEDSIZE, 1.0f); + result = CSSPixel::FromAppUnits(lineHeightAu); + }); + + return result; +} + nsresult SVGContentUtils::ReportToConsole(const Document* doc, const char* aWarning, const nsTArray<nsString>& aParams) { @@ -596,8 +616,12 @@ static gfx::Matrix GetCTMInternal(SVGElement* aElement, bool aScreenCTM, : tm; } -gfx::Matrix SVGContentUtils::GetCTM(SVGElement* aElement, bool aScreenCTM) { - return GetCTMInternal(aElement, aScreenCTM, false); +gfx::Matrix SVGContentUtils::GetCTM(SVGElement* aElement) { + return GetCTMInternal(aElement, false, false); +} + +gfx::Matrix SVGContentUtils::GetScreenCTM(SVGElement* aElement) { + return GetCTMInternal(aElement, true, false); } void SVGContentUtils::RectilinearGetStrokeBounds( diff --git a/dom/svg/SVGContentUtils.h b/dom/svg/SVGContentUtils.h index 8cd017b709..31e75cbb21 100644 --- a/dom/svg/SVGContentUtils.h +++ b/dom/svg/SVGContentUtils.h @@ -180,13 +180,24 @@ class SVGContentUtils { static float GetFontXHeight(const ComputedStyle*, nsPresContext*); /* + * Get the number of CSS px (user units) per lh (i.e. the line-height in + * user units) for an nsIContent. + * + * Requires the element be styled - if not, a default value assuming + * the font-size of 16px and line-height of 1.2 is returned. + */ + static float GetLineHeight(const mozilla::dom::Element* aElement); + + /* * Report a localized error message to the error console. */ static nsresult ReportToConsole(const dom::Document* doc, const char* aWarning, const nsTArray<nsString>& aParams); - static Matrix GetCTM(dom::SVGElement* aElement, bool aScreenCTM); + static Matrix GetCTM(dom::SVGElement* aElement); + + static Matrix GetScreenCTM(dom::SVGElement* aElement); /** * Gets the tight bounds-space stroke bounds of the non-scaling-stroked rect diff --git a/dom/svg/SVGDefsElement.h b/dom/svg/SVGDefsElement.h index 38f6d4fab9..55c2c8fb95 100644 --- a/dom/svg/SVGDefsElement.h +++ b/dom/svg/SVGDefsElement.h @@ -25,7 +25,7 @@ class SVGDefsElement final : public SVGGraphicsElement { public: // defs elements are not focusable. - Focusable IsFocusableWithoutStyle(bool aWithMouse) override { return {}; } + Focusable IsFocusableWithoutStyle(IsFocusableFlags) override { return {}; } nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override; }; diff --git a/dom/svg/SVGGeometryElement.cpp b/dom/svg/SVGGeometryElement.cpp index de5756ed7d..149dae8ba4 100644 --- a/dom/svg/SVGGeometryElement.cpp +++ b/dom/svg/SVGGeometryElement.cpp @@ -201,7 +201,7 @@ bool SVGGeometryElement::IsPointInStroke(const DOMPointInit& aPoint) { SVGGeometryProperty::DoForComputedStyle(this, [&](const ComputedStyle* s) { // Per spec, we should take vector-effect into account. if (s->StyleSVGReset()->HasNonScalingStroke()) { - auto mat = SVGContentUtils::GetCTM(this, true); + auto mat = SVGContentUtils::GetCTM(this); if (mat.HasNonTranslation()) { // We have non-scaling-stroke as well as a non-translation transform. // We should transform the path first then apply the stroke on the diff --git a/dom/svg/SVGGraphicsElement.cpp b/dom/svg/SVGGraphicsElement.cpp index 29b6b76728..52bb023e61 100644 --- a/dom/svg/SVGGraphicsElement.cpp +++ b/dom/svg/SVGGraphicsElement.cpp @@ -129,7 +129,7 @@ already_AddRefed<SVGMatrix> SVGGraphicsElement::GetCTM() { // Flush all pending notifications so that our frames are up to date currentDoc->FlushPendingNotifications(FlushType::Layout); } - gfx::Matrix m = SVGContentUtils::GetCTM(this, false); + gfx::Matrix m = SVGContentUtils::GetCTM(this); RefPtr<SVGMatrix> mat = m.IsSingular() ? nullptr : new SVGMatrix(ThebesMatrix(m)); return mat.forget(); @@ -140,7 +140,7 @@ already_AddRefed<SVGMatrix> SVGGraphicsElement::GetScreenCTM() { // Flush all pending notifications so that our frames are up to date currentDoc->FlushPendingNotifications(FlushType::Layout); } - gfx::Matrix m = SVGContentUtils::GetCTM(this, true); + gfx::Matrix m = SVGContentUtils::GetScreenCTM(this); RefPtr<SVGMatrix> mat = m.IsSingular() ? nullptr : new SVGMatrix(ThebesMatrix(m)); return mat.forget(); @@ -164,7 +164,7 @@ bool SVGGraphicsElement::IsSVGFocusable(bool* aIsFocusable, return false; } -Focusable SVGGraphicsElement::IsFocusableWithoutStyle(bool aWithMouse) { +Focusable SVGGraphicsElement::IsFocusableWithoutStyle(IsFocusableFlags) { Focusable result; IsSVGFocusable(&result.mFocusable, &result.mTabIndex); return result; diff --git a/dom/svg/SVGGraphicsElement.h b/dom/svg/SVGGraphicsElement.h index 610436d394..1c5be3e324 100644 --- a/dom/svg/SVGGraphicsElement.h +++ b/dom/svg/SVGGraphicsElement.h @@ -34,7 +34,7 @@ class SVGGraphicsElement : public SVGGraphicsElementBase, public SVGTests { already_AddRefed<SVGMatrix> GetCTM(); already_AddRefed<SVGMatrix> GetScreenCTM(); - Focusable IsFocusableWithoutStyle(bool aWithMouse) override; + Focusable IsFocusableWithoutStyle(IsFocusableFlags) override; bool IsSVGGraphicsElement() const final { return true; } using nsINode::Clone; diff --git a/dom/svg/SVGLength.cpp b/dom/svg/SVGLength.cpp index f7c383a16f..bbe01e6878 100644 --- a/dom/svg/SVGLength.cpp +++ b/dom/svg/SVGLength.cpp @@ -20,15 +20,19 @@ using namespace mozilla::dom::SVGLength_Binding; namespace mozilla { +// These types are numbered so that different length categories are in +// contiguous ranges - See `SVGLength::Is[..]Unit()`. const unsigned short SVG_LENGTHTYPE_Q = 11; const unsigned short SVG_LENGTHTYPE_CH = 12; const unsigned short SVG_LENGTHTYPE_REM = 13; const unsigned short SVG_LENGTHTYPE_IC = 14; const unsigned short SVG_LENGTHTYPE_CAP = 15; -const unsigned short SVG_LENGTHTYPE_VW = 16; -const unsigned short SVG_LENGTHTYPE_VH = 17; -const unsigned short SVG_LENGTHTYPE_VMIN = 18; -const unsigned short SVG_LENGTHTYPE_VMAX = 19; +const unsigned short SVG_LENGTHTYPE_LH = 16; +const unsigned short SVG_LENGTHTYPE_RLH = 17; +const unsigned short SVG_LENGTHTYPE_VW = 18; +const unsigned short SVG_LENGTHTYPE_VH = 19; +const unsigned short SVG_LENGTHTYPE_VMIN = 20; +const unsigned short SVG_LENGTHTYPE_VMAX = 21; void SVGLength::GetValueAsString(nsAString& aValue) const { nsTextFormatter::ssprintf(aValue, u"%g", (double)mValue); @@ -74,7 +78,7 @@ bool SVGLength::IsAbsoluteUnit(uint8_t aUnit) { /*static*/ bool SVGLength::IsFontRelativeUnit(uint8_t aUnit) { return aUnit == SVG_LENGTHTYPE_EMS || aUnit == SVG_LENGTHTYPE_EXS || - (aUnit >= SVG_LENGTHTYPE_CH && aUnit <= SVG_LENGTHTYPE_CAP); + (aUnit >= SVG_LENGTHTYPE_CH && aUnit <= SVG_LENGTHTYPE_RLH); } /** @@ -192,6 +196,10 @@ float SVGLength::GetPixelsPerUnit(const UserSpaceMetrics& aMetrics, auto sz = aMetrics.GetCSSViewportSize(); return std::max(sz.width, sz.height) / 100.f; } + case SVG_LENGTHTYPE_LH: + return aMetrics.GetLineHeight(UserSpaceMetrics::Type::This); + case SVG_LENGTHTYPE_RLH: + return aMetrics.GetLineHeight(UserSpaceMetrics::Type::Root); default: MOZ_ASSERT(IsAbsoluteUnit(aUnitType)); return GetAbsUnitsPerAbsUnit(SVG_LENGTHTYPE_PX, aUnitType); @@ -256,6 +264,12 @@ nsCSSUnit SVGLength::SpecifiedUnitTypeToCSSUnit(uint8_t aSpecifiedUnit) { case SVG_LENGTHTYPE_VMAX: return nsCSSUnit::eCSSUnit_VMax; + case SVG_LENGTHTYPE_LH: + return nsCSSUnit::eCSSUnit_LineHeight; + + case SVG_LENGTHTYPE_RLH: + return nsCSSUnit::eCSSUnit_RootLineHeight; + default: MOZ_ASSERT_UNREACHABLE("Unknown unit type"); return nsCSSUnit::eCSSUnit_Pixel; @@ -322,6 +336,12 @@ void SVGLength::GetUnitString(nsAString& aUnit, uint16_t aUnitType) { case SVG_LENGTHTYPE_VMAX: aUnit.AssignLiteral("vmax"); return; + case SVG_LENGTHTYPE_LH: + aUnit.AssignLiteral("lh"); + return; + case SVG_LENGTHTYPE_RLH: + aUnit.AssignLiteral("rlh"); + return; } MOZ_ASSERT_UNREACHABLE( "Unknown unit type! Someone's using an SVGLength " @@ -387,6 +407,12 @@ uint16_t SVGLength::GetUnitTypeForString(const nsAString& aUnit) { if (aUnit.LowerCaseEqualsLiteral("vmax")) { return SVG_LENGTHTYPE_VMAX; } + if (aUnit.LowerCaseEqualsLiteral("lh")) { + return SVG_LENGTHTYPE_LH; + } + if (aUnit.LowerCaseEqualsLiteral("rlh")) { + return SVG_LENGTHTYPE_RLH; + } return SVG_LENGTHTYPE_UNKNOWN; } diff --git a/dom/svg/SVGSwitchElement.cpp b/dom/svg/SVGSwitchElement.cpp index e049d07496..a19dfd8631 100644 --- a/dom/svg/SVGSwitchElement.cpp +++ b/dom/svg/SVGSwitchElement.cpp @@ -7,7 +7,6 @@ #include "mozilla/dom/SVGSwitchElement.h" #include "nsLayoutUtils.h" -#include "mozilla/Preferences.h" #include "mozilla/SVGUtils.h" #include "mozilla/dom/SVGSwitchElementBinding.h" @@ -46,14 +45,13 @@ void SVGSwitchElement::MaybeInvalidate() { // InvalidateAndScheduleBoundsUpdate has been called, otherwise // it will not correctly invalidate the old mActiveChild area. - nsIContent* newActiveChild = FindActiveChild(); + auto* newActiveChild = SVGTests::FindActiveSwitchChild(this); if (newActiveChild == mActiveChild) { return; } - nsIFrame* frame = GetPrimaryFrame(); - if (frame) { + if (auto* frame = GetPrimaryFrame()) { nsLayoutUtils::PostRestyleEvent(this, RestyleHint{0}, nsChangeHint_InvalidateRenderingObservers); SVGUtils::ScheduleReflowSVG(frame); @@ -86,54 +84,4 @@ void SVGSwitchElement::RemoveChildNode(nsIContent* aKid, bool aNotify) { MaybeInvalidate(); } -//---------------------------------------------------------------------- -// Implementation Helpers: - -nsIContent* SVGSwitchElement::FindActiveChild() const { - nsAutoString acceptLangs; - Preferences::GetLocalizedString("intl.accept_languages", acceptLangs); - - int32_t bestLanguagePreferenceRank = -1; - nsIContent* bestChild = nullptr; - nsIContent* defaultChild = nullptr; - for (nsIContent* child = nsINode::GetFirstChild(); child; - child = child->GetNextSibling()) { - if (!child->IsElement()) { - continue; - } - nsCOMPtr<SVGTests> tests(do_QueryInterface(child)); - if (tests) { - if (tests->PassesConditionalProcessingTestsIgnoringSystemLanguage()) { - int32_t languagePreferenceRank = - tests->GetBestLanguagePreferenceRank(acceptLangs); - switch (languagePreferenceRank) { - case 0: - // best possible match - return child; - case -1: - // no match - break; - case -2: - // no systemLanguage attribute. If there's nothing better - // we'll use the first such child. - if (!defaultChild) { - defaultChild = child; - } - break; - default: - if (bestLanguagePreferenceRank == -1 || - languagePreferenceRank < bestLanguagePreferenceRank) { - bestLanguagePreferenceRank = languagePreferenceRank; - bestChild = child; - } - break; - } - } - } else if (!bestChild) { - bestChild = child; - } - } - return bestChild ? bestChild : defaultChild; -} - } // namespace mozilla::dom diff --git a/dom/svg/SVGSwitchElement.h b/dom/svg/SVGSwitchElement.h index c244c0fe51..68c22e8f71 100644 --- a/dom/svg/SVGSwitchElement.h +++ b/dom/svg/SVGSwitchElement.h @@ -50,9 +50,6 @@ class SVGSwitchElement final : public SVGSwitchElementBase { nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override; private: - void UpdateActiveChild() { mActiveChild = FindActiveChild(); } - nsIContent* FindActiveChild() const; - // only this child will be displayed nsCOMPtr<nsIContent> mActiveChild; }; diff --git a/dom/svg/SVGSymbolElement.cpp b/dom/svg/SVGSymbolElement.cpp index eec7851de3..26d8a04ff0 100644 --- a/dom/svg/SVGSymbolElement.cpp +++ b/dom/svg/SVGSymbolElement.cpp @@ -29,11 +29,11 @@ SVGSymbolElement::SVGSymbolElement( already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) : SVGSymbolElementBase(std::move(aNodeInfo)) {} -Focusable SVGSymbolElement::IsFocusableWithoutStyle(bool aWithMouse) { +Focusable SVGSymbolElement::IsFocusableWithoutStyle(IsFocusableFlags aFlags) { if (!CouldBeRendered()) { return {}; } - return SVGSymbolElementBase::IsFocusableWithoutStyle(aWithMouse); + return SVGSymbolElementBase::IsFocusableWithoutStyle(aFlags); } bool SVGSymbolElement::CouldBeRendered() const { diff --git a/dom/svg/SVGSymbolElement.h b/dom/svg/SVGSymbolElement.h index 4f49775de2..9f5b59db41 100644 --- a/dom/svg/SVGSymbolElement.h +++ b/dom/svg/SVGSymbolElement.h @@ -26,7 +26,7 @@ class SVGSymbolElement final : public SVGSymbolElementBase { ~SVGSymbolElement() = default; JSObject* WrapNode(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override; - Focusable IsFocusableWithoutStyle(bool aWithMouse) override; + Focusable IsFocusableWithoutStyle(IsFocusableFlags) override; public: // interfaces: diff --git a/dom/svg/SVGTests.cpp b/dom/svg/SVGTests.cpp index 6603663445..058065618e 100644 --- a/dom/svg/SVGTests.cpp +++ b/dom/svg/SVGTests.cpp @@ -5,12 +5,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/dom/SVGTests.h" + #include "DOMSVGStringList.h" +#include "nsCharSeparatedTokenizer.h" #include "nsIContent.h" #include "nsIContentInlines.h" #include "mozilla/dom/SVGSwitchElement.h" -#include "nsCharSeparatedTokenizer.h" -#include "nsStyleUtil.h" +#include "mozilla/intl/oxilangtag_ffi_generated.h" #include "mozilla/Preferences.h" namespace mozilla::dom { @@ -58,39 +59,84 @@ bool SVGTests::IsConditionalProcessingAttribute( return false; } -int32_t SVGTests::GetBestLanguagePreferenceRank( - const nsAString& aAcceptLangs) const { - if (!mStringListAttributes[LANGUAGE].IsExplicitlySet()) { - return -2; +// Find the best match from aAvailLangs for the users accept-languages, +// returning the index in the aAvailLangs list, or -1 if no match. +int32_t FindBestLanguage(const nsTArray<nsCString>& aAvailLangs) { + AutoTArray<nsCString, 16> reqLangs; + nsCString acceptLangs; + Preferences::GetLocalizedCString("intl.accept_languages", acceptLangs); + nsCCharSeparatedTokenizer languageTokenizer(acceptLangs, ','); + while (languageTokenizer.hasMoreTokens()) { + reqLangs.AppendElement(languageTokenizer.nextToken()); } - int32_t lowestRank = -1; - - for (uint32_t i = 0; i < mStringListAttributes[LANGUAGE].Length(); i++) { - int32_t index = 0; - for (const nsAString& languageToken : - nsCharSeparatedTokenizer(aAcceptLangs, ',').ToRange()) { - bool exactMatch = languageToken.Equals(mStringListAttributes[LANGUAGE][i], - nsCaseInsensitiveStringComparator); - bool prefixOnlyMatch = - !exactMatch && nsStyleUtil::DashMatchCompare( - mStringListAttributes[LANGUAGE][i], languageToken, - nsCaseInsensitiveStringComparator); - if (index == 0 && exactMatch) { - // best possible match - return 0; + for (const auto& req : reqLangs) { + for (const auto& avail : aAvailLangs) { + if (avail.Length() > req.Length()) { + // Ensure that en does not match en-us, i.e. you need to have en in + // intl.accept_languages to match en in markup. + continue; } - if ((exactMatch || prefixOnlyMatch) && - (lowestRank == -1 || 2 * index + prefixOnlyMatch < lowestRank)) { - lowestRank = 2 * index + prefixOnlyMatch; + using namespace intl::ffi; + struct LangTagDelete { + void operator()(LangTag* aLangTag) const { lang_tag_destroy(aLangTag); } + }; + UniquePtr<LangTag, LangTagDelete> langTag(lang_tag_new(&avail)); + if (langTag && lang_tag_matches(langTag.get(), &req)) { + return &avail - &aAvailLangs[0]; } - ++index; } } - return lowestRank; + return -1; } -bool SVGTests::PassesConditionalProcessingTestsIgnoringSystemLanguage() const { +nsIContent* SVGTests::FindActiveSwitchChild( + const dom::SVGSwitchElement* aSwitch) { + AutoTArray<nsCString, 16> availLocales; + AutoTArray<nsIContent*, 16> children; + nsIContent* defaultChild = nullptr; + for (auto* child = aSwitch->GetFirstChild(); child; + child = child->GetNextSibling()) { + if (!child->IsElement()) { + continue; + } + nsCOMPtr<SVGTests> tests(do_QueryInterface(child)); + if (tests) { + if (!tests->mPassesConditionalProcessingTests.valueOr(true) || + !tests->PassesRequiredExtensionsTests()) { + continue; + } + const auto& languages = tests->mStringListAttributes[LANGUAGE]; + if (!languages.IsExplicitlySet()) { + if (!defaultChild) { + defaultChild = child; + } + continue; + } + for (uint32_t i = 0; i < languages.Length(); i++) { + children.AppendElement(child); + availLocales.AppendElement(NS_ConvertUTF16toUTF8(languages[i])); + } + } + } + + // For each entry in availLocales, we expect to have a corresponding entry + // in children that provides the child node associated with that locale. + MOZ_ASSERT(children.Length() == availLocales.Length()); + + if (availLocales.IsEmpty()) { + return defaultChild; + } + + int32_t index = FindBestLanguage(availLocales); + if (index >= 0) { + return children[index]; + } + + return defaultChild; +} + +bool SVGTests::PassesRequiredExtensionsTests() const { // Required Extensions // // The requiredExtensions attribute defines a list of required language @@ -98,12 +144,15 @@ bool SVGTests::PassesConditionalProcessingTestsIgnoringSystemLanguage() const { // go beyond the feature set defined in the SVG specification. // Each extension is identified by a URI reference. // For now, claim that mozilla's SVG implementation supports XHTML and MathML. - if (mStringListAttributes[EXTENSIONS].IsExplicitlySet()) { - if (mStringListAttributes[EXTENSIONS].IsEmpty()) { + const auto& extensions = mStringListAttributes[EXTENSIONS]; + if (extensions.IsExplicitlySet()) { + if (extensions.IsEmpty()) { + mPassesConditionalProcessingTests = Some(false); return false; } - for (uint32_t i = 0; i < mStringListAttributes[EXTENSIONS].Length(); i++) { - if (!HasExtension(mStringListAttributes[EXTENSIONS][i])) { + for (uint32_t i = 0; i < extensions.Length(); i++) { + if (!HasExtension(extensions[i])) { + mPassesConditionalProcessingTests = Some(false); return false; } } @@ -112,45 +161,36 @@ bool SVGTests::PassesConditionalProcessingTestsIgnoringSystemLanguage() const { } bool SVGTests::PassesConditionalProcessingTests() const { - if (!PassesConditionalProcessingTestsIgnoringSystemLanguage()) { + if (mPassesConditionalProcessingTests) { + return mPassesConditionalProcessingTests.value(); + } + if (!PassesRequiredExtensionsTests()) { return false; } // systemLanguage // - // Evaluates to "true" if one of the languages indicated by user preferences - // exactly equals one of the languages given in the value of this parameter, - // or if one of the languages indicated by user preferences exactly equals a - // prefix of one of the languages given in the value of this parameter such - // that the first tag character following the prefix is "-". - if (mStringListAttributes[LANGUAGE].IsExplicitlySet()) { - if (mStringListAttributes[LANGUAGE].IsEmpty()) { + // Evaluates to true if there's a BCP 47 match for the one of the user + // preference languages with one of the languages given in the value of + // this parameter. + const auto& languages = mStringListAttributes[LANGUAGE]; + if (languages.IsExplicitlySet()) { + if (languages.IsEmpty()) { + mPassesConditionalProcessingTests = Some(false); return false; } - // Get our language preferences - nsAutoString acceptLangs; - Preferences::GetLocalizedString("intl.accept_languages", acceptLangs); - - if (acceptLangs.IsEmpty()) { - NS_WARNING( - "no default language specified for systemLanguage conditional test"); - return false; + AutoTArray<nsCString, 4> availLocales; + for (uint32_t i = 0; i < languages.Length(); i++) { + availLocales.AppendElement(NS_ConvertUTF16toUTF8(languages[i])); } - for (uint32_t i = 0; i < mStringListAttributes[LANGUAGE].Length(); i++) { - nsCharSeparatedTokenizer languageTokenizer(acceptLangs, ','); - while (languageTokenizer.hasMoreTokens()) { - if (nsStyleUtil::DashMatchCompare(mStringListAttributes[LANGUAGE][i], - languageTokenizer.nextToken(), - nsCaseInsensitiveStringComparator)) { - return true; - } - } - } - return false; + mPassesConditionalProcessingTests = + Some(FindBestLanguage(availLocales) >= 0); + return mPassesConditionalProcessingTests.value(); } + mPassesConditionalProcessingTests = Some(true); return true; } @@ -163,6 +203,7 @@ bool SVGTests::ParseConditionalProcessingAttribute(nsAtom* aAttribute, if (NS_FAILED(rv)) { mStringListAttributes[i].Clear(); } + mPassesConditionalProcessingTests = Nothing(); MaybeInvalidate(); return true; } @@ -174,6 +215,7 @@ void SVGTests::UnsetAttr(const nsAtom* aAttribute) { for (uint32_t i = 0; i < ArrayLength(sStringListNames); i++) { if (aAttribute == sStringListNames[i]) { mStringListAttributes[i].Clear(); + mPassesConditionalProcessingTests = Nothing(); MaybeInvalidate(); return; } diff --git a/dom/svg/SVGTests.h b/dom/svg/SVGTests.h index 148c35fc5c..90991fd60d 100644 --- a/dom/svg/SVGTests.h +++ b/dom/svg/SVGTests.h @@ -13,13 +13,15 @@ class nsAttrValue; class nsAtom; +class nsIContent; class nsStaticAtom; namespace mozilla { namespace dom { class DOMSVGStringList; -} +class SVGSwitchElement; +} // namespace dom #define MOZILLA_DOMSVGTESTS_IID \ { \ @@ -42,26 +44,10 @@ class SVGTests : public nsISupports { using SVGStringList = mozilla::SVGStringList; /** - * Compare the language name(s) in a systemLanguage attribute to the - * user's language preferences, as defined in - * http://www.w3.org/TR/SVG11/struct.html#SystemLanguageAttribute - * We have a match if a language name in the users language preferences - * exactly equals one of the language names or exactly equals a prefix of - * one of the language names in the systemLanguage attribute. - * @returns 2 * the lowest index in the aAcceptLangs that matches + 1 - * if only the prefix matches, -2 if there's no systemLanguage attribute, - * or -1 if no indices match. - * XXX This algorithm is O(M*N). - */ - int32_t GetBestLanguagePreferenceRank(const nsAString& aAcceptLangs) const; - - /** - * Check whether the conditional processing attributes other than - * systemLanguage "return true" if they apply to and are specified - * on the given element. Returns true if this element should be - * rendered, false if it should not. + * Find the active switch child using BCP 47 rules. */ - bool PassesConditionalProcessingTestsIgnoringSystemLanguage() const; + static nsIContent* FindActiveSwitchChild( + const dom::SVGSwitchElement* aSwitch); /** * Check whether the conditional processing attributes requiredExtensions @@ -72,16 +58,6 @@ class SVGTests : public nsISupports { bool PassesConditionalProcessingTests() const; /** - * Check whether the conditional processing attributes requiredExtensions - * and systemLanguage both "return true" if they apply to - * and are specified on the given element. Returns true if this element - * should be rendered, false if it should not. - * - * @param aAcceptLangs The value of the intl.accept_languages preference - */ - bool PassesConditionalProcessingTests(const nsAString& aAcceptLangs) const; - - /** * Returns true if the attribute is one of the conditional processing * attributes. */ @@ -117,9 +93,17 @@ class SVGTests : public nsISupports { virtual ~SVGTests() = default; private: + /** + * Check whether the extensions processing attribute applies to and is + * specified on the given element. Returns true if this element should be + * rendered, false if it should not. + */ + bool PassesRequiredExtensionsTests() const; + enum { EXTENSIONS, LANGUAGE }; SVGStringList mStringListAttributes[2]; static nsStaticAtom* const sStringListNames[2]; + mutable Maybe<bool> mPassesConditionalProcessingTests = Some(true); }; NS_DEFINE_STATIC_IID_ACCESSOR(SVGTests, MOZILLA_DOMSVGTESTS_IID) diff --git a/dom/svg/test/test_switch.xhtml b/dom/svg/test/test_switch.xhtml index d1fa546e26..a589a934c1 100644 --- a/dom/svg/test/test_switch.xhtml +++ b/dom/svg/test/test_switch.xhtml @@ -22,17 +22,6 @@ SimpleTest.waitForExplicitFinish(); var test = 1; -function checkBounds(element, x, y, w, h) { - var bbox = element.getBBox(); - var name = element.nodeName; - - is(bbox.x, x, test + " " + name + ".getBBox().x"); - is(bbox.y, y, test + " " + name + ".getBBox().y"); - is(bbox.width, w, test + " " + name + ".getBBox().width"); - is(bbox.height, h, test + " " + name + ".getBBox().height"); - ++test; -} - function checkWidth(element, w) { var bbox = element.getBBox(); var name = element.nodeName; @@ -41,55 +30,65 @@ function checkWidth(element, w) { ++test; } -function run() { - // Set accept_languages to something we know - SpecialPowers.pushPrefEnv({"set": [["intl.accept_languages", "en-gb,en,it"]]}, run1); -} - -function run1() { +async function run() { try { - var doc = $("svg").contentDocument; - var s = doc.getElementById("s"); - var first = doc.getElementById("first"); - var second = doc.getElementById("second"); - var third = doc.getElementById("third"); - - first.setAttribute("systemLanguage", "fr"); - - /* test for an exact match */ - second.setAttribute("systemLanguage", "en-gb"); - checkWidth(s, 50); - - /* test for a close match i.e. the same language prefix */ - second.setAttribute("systemLanguage", "en-us"); - checkWidth(s, 50); - - /* test that we pick the best match */ - second.setAttribute("systemLanguage", "it"); - checkWidth(s, 50); - - /* test that we use the default if nothing matches */ - second.setAttribute("systemLanguage", "fr"); - checkWidth(s, 80); - - /* test we still ignore non-matches */ - second.removeAttribute("systemLanguage"); - third.setAttribute("systemLanguage", "fr"); - checkWidth(s, 50); - - /* check what happens if nothing matches */ - second.setAttribute("systemLanguage", "fr"); - checkWidth(s, 0); - - /* test that we pick the best match */ - first.setAttribute("systemLanguage", "en"); - second.setAttribute("systemLanguage", "en-gb"); - checkWidth(s, 50); + // Set accept_languages to something we know + await SpecialPowers.pushPrefEnv({"set": [["intl.accept_languages", "en-gb,en,it"]]}, run1); } finally { SimpleTest.finish(); } } +function run1() { + let doc = $("svg").contentDocument; + let s = doc.getElementById("s"); + let first = doc.getElementById("first"); + let second = doc.getElementById("second"); + let third = doc.getElementById("third"); + + first.setAttribute("systemLanguage", "fr"); + + /* test for an exact match */ + second.setAttribute("systemLanguage", "en-gb"); + checkWidth(s, 50); + + /* test for a close match i.e. the same language prefix */ + second.setAttribute("systemLanguage", "en"); + checkWidth(s, 50); + + /* test for a close match regardless of case */ + second.setAttribute("systemLanguage", "eN"); + checkWidth(s, 50); + + /* test that different regions don't match */ + second.setAttribute("systemLanguage", "en-us"); + checkWidth(s, 80); + + /* test that we pick the best match */ + second.setAttribute("systemLanguage", "it"); + checkWidth(s, 50); + + /* test that we use the default if nothing matches */ + second.setAttribute("systemLanguage", "fr"); + checkWidth(s, 80); + + /* test we still ignore non-matches */ + second.removeAttribute("systemLanguage"); + third.setAttribute("systemLanguage", "fr"); + checkWidth(s, 50); + + /* check what happens if nothing matches */ + second.setAttribute("systemLanguage", "fr"); + third.setAttribute("systemLanguage", "fr"); + checkWidth(s, 0); + + /* test that we pick the best match */ + first.setAttribute("systemLanguage", "en"); + second.setAttribute("systemLanguage", "en-gb"); + third.removeAttribute("systemLanguage"); + checkWidth(s, 50); +} + window.addEventListener("load", run); ]]> diff --git a/dom/svg/test/test_tabindex.html b/dom/svg/test/test_tabindex.html index 65315420bc..3d29d1070b 100644 --- a/dom/svg/test/test_tabindex.html +++ b/dom/svg/test/test_tabindex.html @@ -37,7 +37,6 @@ function main() { var t = document.getElementById("t"); var l1 = document.getElementById("l1"); var l2 = document.getElementById("l2"); - const isMac = ("nsILocalFileMac" in SpecialPowers.Ci); try { // Step 1: Checking by assigning tabIndex @@ -74,20 +73,10 @@ function main() { is(document.activeElement.tabIndex, 3, "The active element tabindex is 3"); synthesizeKey("KEY_Tab"); - // On Mac, SVG link elements should not be focused. - if (isMac) { - is(document.activeElement.tabIndex, 6, "The active element tabindex is 6"); - } else { - is(document.activeElement.tabIndex, 4, "The active element tabindex is 4"); - } + is(document.activeElement.tabIndex, 4, "The active element tabindex is 4"); synthesizeKey("KEY_Tab"); - // On Mac, SVG link elements should not be focused. - if (isMac) { - is(document.activeElement.tabIndex, 7, "The active element tabindex is 7"); - } else { - is(document.activeElement.tabIndex, 5, "The active element tabindex is 5"); - } + is(document.activeElement.tabIndex, 5, "The active element tabindex is 5"); } catch (e) { ok(false, "Got unexpected exception" + e); } |