summaryrefslogtreecommitdiffstats
path: root/dom/svg
diff options
context:
space:
mode:
Diffstat (limited to 'dom/svg')
-rw-r--r--dom/svg/SVGAElement.cpp7
-rw-r--r--dom/svg/SVGAElement.h2
-rw-r--r--dom/svg/SVGAnimatedLength.cpp21
-rw-r--r--dom/svg/SVGAnimatedLength.h3
-rw-r--r--dom/svg/SVGContentUtils.cpp28
-rw-r--r--dom/svg/SVGContentUtils.h13
-rw-r--r--dom/svg/SVGDefsElement.h2
-rw-r--r--dom/svg/SVGGeometryElement.cpp2
-rw-r--r--dom/svg/SVGGraphicsElement.cpp6
-rw-r--r--dom/svg/SVGGraphicsElement.h2
-rw-r--r--dom/svg/SVGLength.cpp36
-rw-r--r--dom/svg/SVGSwitchElement.cpp56
-rw-r--r--dom/svg/SVGSwitchElement.h3
-rw-r--r--dom/svg/SVGSymbolElement.cpp4
-rw-r--r--dom/svg/SVGSymbolElement.h2
-rw-r--r--dom/svg/SVGTests.cpp158
-rw-r--r--dom/svg/SVGTests.h44
-rw-r--r--dom/svg/test/test_switch.xhtml107
-rw-r--r--dom/svg/test/test_tabindex.html15
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);
}