diff --git a/content/base/public/DirectionalityUtils.h b/content/base/public/DirectionalityUtils.h new file mode 100644 index 000000000000..195f7009de2f --- /dev/null +++ b/content/base/public/DirectionalityUtils.h @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef DirectionalityUtils_h___ +#define DirectionalityUtils_h___ + +class nsIContent; +class nsIDocument; +class nsINode; + +namespace mozilla { +namespace dom { +class Element; +} // namespace dom +} // namespace mozilla + +namespace mozilla { + +namespace directionality { + +enum Directionality { + eDir_NotSet = 0, + eDir_RTL = 1, + eDir_LTR = 2 +}; + +/** + * Set the directionality of an element according to the algorithm defined at + * http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#the-directionality, + * not including elements with auto direction. + * + * @return the directionality that the element was set to + */ +Directionality RecomputeDirectionality(mozilla::dom::Element* aElement, + bool aNotify = true); + +/** + * Set the directionality of any descendants of a node that do not themselves + * have a dir attribute. + * For performance reasons we walk down the descendant tree in the rare case + * of setting the dir attribute, rather than walking up the ancestor tree in + * the much more common case of getting the element's directionality. + */ +void SetDirectionalityOnDescendants(mozilla::dom::Element* aElement, + Directionality aDir, + bool aNotify = true); + +} // end namespace directionality + +} // end namespace mozilla + +#endif /* DirectionalityUtils_h___ */ diff --git a/content/base/public/Element.h b/content/base/public/Element.h index 4d1b51b1acbd..c76bfe03ca95 100644 --- a/content/base/public/Element.h +++ b/content/base/public/Element.h @@ -10,6 +10,7 @@ #include "mozilla/dom/FragmentOrElement.h" // for base class #include "nsChangeHint.h" // for enum #include "nsEventStates.h" // for member +#include "mozilla/dom/DirectionalityUtils.h" class nsEventStateManager; class nsFocusManager; @@ -216,6 +217,54 @@ public: */ virtual nsIAtom *GetClassAttributeName() const = 0; + inline mozilla::directionality::Directionality GetDirectionality() const { + if (HasFlag(NODE_HAS_DIRECTION_RTL)) { + return mozilla::directionality::eDir_RTL; + } + + if (HasFlag(NODE_HAS_DIRECTION_LTR)) { + return mozilla::directionality::eDir_LTR; + } + + return mozilla::directionality::eDir_NotSet; + } + + inline void SetDirectionality(mozilla::directionality::Directionality aDir, + bool aNotify) { + UnsetFlags(NODE_ALL_DIRECTION_FLAGS); + if (!aNotify) { + RemoveStatesSilently(DIRECTION_STATES); + } + + switch (aDir) { + case (mozilla::directionality::eDir_RTL): + SetFlags(NODE_HAS_DIRECTION_RTL); + if (!aNotify) { + AddStatesSilently(NS_EVENT_STATE_RTL); + } + break; + + case(mozilla::directionality::eDir_LTR): + SetFlags(NODE_HAS_DIRECTION_LTR); + if (!aNotify) { + AddStatesSilently(NS_EVENT_STATE_LTR); + } + break; + + default: + break; + } + + /* + * Only call UpdateState if we need to notify, because we call + * SetDirectionality for every element, and UpdateState is very very slow + * for some elements. + */ + if (aNotify) { + UpdateState(true); + } + } + protected: /** * Method to get the _intrinsic_ content state of this element. This is the diff --git a/content/base/public/Makefile.in b/content/base/public/Makefile.in index 49c521897795..5d92d5876f2b 100644 --- a/content/base/public/Makefile.in +++ b/content/base/public/Makefile.in @@ -48,6 +48,7 @@ $(NULL) EXPORTS_NAMESPACES = mozilla/dom mozilla EXPORTS_mozilla/dom = \ + DirectionalityUtils.h \ Element.h \ FragmentOrElement.h \ FromParser.h \ diff --git a/content/base/public/nsIDocument.h b/content/base/public/nsIDocument.h index 16307c8d5cf1..0845a2954312 100644 --- a/content/base/public/nsIDocument.h +++ b/content/base/public/nsIDocument.h @@ -23,6 +23,7 @@ #include "nsPIDOMWindow.h" // for use in inline functions #include "nsPropertyTable.h" // for member #include "nsTHashtable.h" // for member +#include "mozilla/dom/DirectionalityUtils.h" class imgIRequest; class nsAString; @@ -397,6 +398,10 @@ public: { mBidiOptions = aBidiOptions; } + + inline mozilla::directionality::Directionality GetDocumentDirectionality() { + return mDirectionality; + } /** * Access HTTP header data (this may also get set from other @@ -1853,6 +1858,9 @@ protected: // defined in nsBidiUtils.h PRUint32 mBidiOptions; + // The root directionality of this document. + mozilla::directionality::Directionality mDirectionality; + nsCString mContentLanguage; private: nsCString mContentType; diff --git a/content/base/public/nsINode.h b/content/base/public/nsINode.h index 0d79acf33736..189d64265507 100644 --- a/content/base/public/nsINode.h +++ b/content/base/public/nsINode.h @@ -145,8 +145,16 @@ enum { // Set if the node has had :hover selectors matched against it NODE_HAS_RELEVANT_HOVER_RULES = 0x00080000U, + // Set if the node has right-to-left directionality + NODE_HAS_DIRECTION_RTL = 0x00100000U, + + // Set if the node has left-to-right directionality + NODE_HAS_DIRECTION_LTR = 0x00200000U, + + NODE_ALL_DIRECTION_FLAGS = NODE_HAS_DIRECTION_LTR | NODE_HAS_DIRECTION_RTL, + // Remaining bits are node type specific. - NODE_TYPE_SPECIFIC_BITS_OFFSET = 20 + NODE_TYPE_SPECIFIC_BITS_OFFSET = 22 }; /** @@ -1279,6 +1287,8 @@ private: NodeIsContent, // Set if the node has animations or transitions ElementHasAnimations, + // Set if node has a dir attribute with a valid value (ltr or rtl) + NodeHasValidDirAttribute, // Guard value BooleanFlagCount }; @@ -1346,6 +1356,9 @@ public: void ClearPointerLock() { ClearBoolFlag(ElementHasPointerLock); } bool MayHaveAnimations() { return GetBoolFlag(ElementHasAnimations); } void SetMayHaveAnimations() { SetBoolFlag(ElementHasAnimations); } + void SetHasValidDir() { SetBoolFlag(NodeHasValidDirAttribute); } + void ClearHasValidDir() { ClearBoolFlag(NodeHasValidDirAttribute); } + bool HasValidDir() const { return GetBoolFlag(NodeHasValidDirAttribute); } protected: void SetParentIsContent(bool aValue) { SetBoolFlag(ParentIsContent, aValue); } void SetInDocument() { SetBoolFlag(IsInDocument); } diff --git a/content/base/src/DirectionalityUtils.cpp b/content/base/src/DirectionalityUtils.cpp new file mode 100644 index 000000000000..8523e25a333a --- /dev/null +++ b/content/base/src/DirectionalityUtils.cpp @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 sw=2 et tw=78: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/DirectionalityUtils.h" +#include "nsINode.h" +#include "nsIContent.h" +#include "nsIDocument.h" +#include "mozilla/dom/Element.h" +#include "nsIDOMNodeFilter.h" +#include "nsTreeWalker.h" +#include "nsIDOMHTMLDocument.h" + + +namespace mozilla { + +namespace directionality { + +typedef mozilla::dom::Element Element; + +Directionality +RecomputeDirectionality(Element* aElement, bool aNotify) +{ + Directionality dir = eDir_LTR; + + if (aElement->HasValidDir()) { + dir = aElement->GetDirectionality(); + } else { + Element* parent = aElement->GetElementParent(); + if (parent) { + // If the element doesn't have an explicit dir attribute with a valid + // value, the directionality is the same as the parent element (but + // don't propagate the parent directionality if it isn't set yet). + Directionality parentDir = parent->GetDirectionality(); + if (parentDir != eDir_NotSet) { + dir = parentDir; + } + } else { + // If there is no parent element, the directionality is the same as the + // document direction. + Directionality documentDir = + aElement->OwnerDoc()->GetDocumentDirectionality(); + if (documentDir != eDir_NotSet) { + dir = documentDir; + } + } + + aElement->SetDirectionality(dir, aNotify); + } + return dir; +} + +void +SetDirectionalityOnDescendants(Element* aElement, Directionality aDir, + bool aNotify) +{ + for (nsIContent* child = aElement->GetFirstChild(); child; ) { + if (!child->IsElement()) { + child = child->GetNextNode(aElement); + continue; + } + + Element* element = child->AsElement(); + if (element->HasValidDir()) { + child = child->GetNextNonChildNode(aElement); + continue; + } + element->SetDirectionality(aDir, aNotify); + child = child->GetNextNode(aElement); + } +} + +} // end namespace directionality + +} // end namespace mozilla + diff --git a/content/base/src/Makefile.in b/content/base/src/Makefile.in index fdec933a82b7..c7c1d95097a6 100644 --- a/content/base/src/Makefile.in +++ b/content/base/src/Makefile.in @@ -53,6 +53,7 @@ LOCAL_INCLUDES = \ $(NULL) CPPSRCS = \ + DirectionalityUtils.cpp \ nsAtomListUtils.cpp \ nsAttrAndChildArray.cpp \ nsAttrValue.cpp \ diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 643fb2e05b44..5a1f4c200094 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -91,6 +91,7 @@ #include "nsXMLEventsManager.h" #include "nsBidiUtils.h" +#include "mozilla/dom/DirectionalityUtils.h" #include "nsIDOMUserDataHandler.h" #include "nsIDOMXPathEvaluator.h" @@ -170,6 +171,7 @@ using namespace mozilla; using namespace mozilla::dom; +using namespace mozilla::directionality; typedef nsTArray LinkArray; @@ -1510,6 +1512,7 @@ nsIDocument::nsIDocument() mAllowDNSPrefetch(true), mIsBeingUsedAsImage(false), mHasLinksToUpdate(false), + mDirectionality(eDir_LTR), mPartID(0) { SetInDocument(); @@ -5583,6 +5586,15 @@ nsDocument::SetDir(const nsAString& aDirection) // No presentation; just set it on ourselves SetBidiOptions(options); } + Directionality dir = elt->mValue == IBMBIDI_TEXTDIRECTION_RTL ? + eDir_RTL : eDir_LTR; + SetDocumentDirectionality(dir); + // Set the directionality of the root element and its descendants, if any + Element* rootElement = GetRootElement(); + if (rootElement) { + rootElement->SetDirectionality(dir, true); + SetDirectionalityOnDescendants(rootElement, dir); + } } break; diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h index bbe012601d80..3ef75fda92b2 100644 --- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -1048,6 +1048,12 @@ protected: nsIContent* GetFirstBaseNodeWithHref(); nsresult SetFirstBaseNodeWithHref(nsIContent *node); + inline void + SetDocumentDirectionality(mozilla::directionality::Directionality aDir) + { + mDirectionality = aDir; + } + // Get the first