Bug 562169. Add support for :dir(ltr/rtl) CSS selector, content part, r=bz

This commit is contained in:
Simon Montagu 2012-04-17 07:03:10 +03:00
Родитель 7b0bd7bf8b
Коммит 88f3b972df
13 изменённых файлов: 278 добавлений и 1 удалений

Просмотреть файл

@ -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___ */

Просмотреть файл

@ -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

Просмотреть файл

@ -48,6 +48,7 @@ $(NULL)
EXPORTS_NAMESPACES = mozilla/dom mozilla
EXPORTS_mozilla/dom = \
DirectionalityUtils.h \
Element.h \
FragmentOrElement.h \
FromParser.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;

Просмотреть файл

@ -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); }

Просмотреть файл

@ -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

Просмотреть файл

@ -53,6 +53,7 @@ LOCAL_INCLUDES = \
$(NULL)
CPPSRCS = \
DirectionalityUtils.cpp \
nsAtomListUtils.cpp \
nsAttrAndChildArray.cpp \
nsAttrValue.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<Link*> 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;

Просмотреть файл

@ -1048,6 +1048,12 @@ protected:
nsIContent* GetFirstBaseNodeWithHref();
nsresult SetFirstBaseNodeWithHref(nsIContent *node);
inline void
SetDocumentDirectionality(mozilla::directionality::Directionality aDir)
{
mDirectionality = aDir;
}
// Get the first <title> element with the given IsNodeOfType type, or
// return null if there isn't one
nsIContent* GetTitleContent(PRUint32 aNodeType);

Просмотреть файл

@ -51,6 +51,7 @@
#include "nsIDOMMutationEvent.h"
#include "nsMutationEvent.h"
#include "nsNodeUtils.h"
#include "mozilla/dom/DirectionalityUtils.h"
#include "nsDocument.h"
#include "nsAttrValueOrString.h"
#ifdef MOZ_XUL
@ -128,6 +129,7 @@
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::directionality;
nsEventStates
Element::IntrinsicState() const
@ -1358,6 +1360,13 @@ nsGenericElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
SetSubtreeRootPointer(aParent->SubtreeRoot());
}
// This has to be here, rather than in nsGenericHTMLElement::BindToTree,
// because it has to happen after updating the parent pointer, but before
// recursively binding the kids.
if (IsHTML()) {
RecomputeDirectionality(this, false);
}
// If NODE_FORCE_XBL_BINDINGS was set we might have anonymous children
// that also need to be told that they are moving.
nsresult rv;
@ -1543,6 +1552,13 @@ nsGenericElement::UnbindFromTree(bool aDeep, bool aNullParent)
}
}
// This has to be here, rather than in nsGenericHTMLElement::UnbindFromTree,
// because it has to happen after unsetting the parent pointer, but before
// recursively unbinding the kids.
if (IsHTML()) {
RecomputeDirectionality(this, false);
}
if (aDeep) {
// Do the kids. Don't call GetChildCount() here since that'll force
// XUL to generate template children, which there is no need for since

Просмотреть файл

@ -270,6 +270,7 @@ GK_ATOM(dialog, "dialog")
GK_ATOM(difference, "difference")
GK_ATOM(digit, "digit")
GK_ATOM(dir, "dir")
GK_ATOM(directionality, "directionality")
GK_ATOM(disableOutputEscaping, "disable-output-escaping")
GK_ATOM(disabled, "disabled")
GK_ATOM(display, "display")

Просмотреть файл

@ -53,6 +53,7 @@
#include "nsHTMLParts.h"
#include "nsContentUtils.h"
#include "mozilla/dom/DirectionalityUtils.h"
#include "nsString.h"
#include "nsUnicharUtils.h"
#include "nsGkAtoms.h"
@ -96,6 +97,7 @@
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::directionality;
class nsINodeInfo;
class nsIDOMNodeList;
@ -1687,6 +1689,24 @@ nsGenericHTMLElement::UpdateEditableState(bool aNotify)
nsStyledElement::UpdateEditableState(aNotify);
}
nsEventStates
nsGenericHTMLElement::IntrinsicState() const
{
nsEventStates state = nsGenericHTMLElementBase::IntrinsicState();
if (GetDirectionality() == eDir_RTL) {
state |= NS_EVENT_STATE_RTL;
state &= ~NS_EVENT_STATE_LTR;
} else { // at least for HTML, directionality is exclusively LTR or RTL
NS_ASSERTION(GetDirectionality() == eDir_LTR,
"HTML element's directionality must be either RTL or LTR");
state |= NS_EVENT_STATE_LTR;
state &= ~NS_EVENT_STATE_RTL;
}
return state;
}
nsresult
nsGenericHTMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
@ -1889,6 +1909,20 @@ nsGenericHTMLElement::AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
else if (aNotify && aName == nsGkAtoms::spellcheck) {
SyncEditorsOnSubtree(this);
}
else if (aName == nsGkAtoms::dir) {
Directionality dir;
if (aValue &&
(aValue->Equals(nsGkAtoms::ltr, eIgnoreCase) ||
aValue->Equals(nsGkAtoms::rtl, eIgnoreCase))) {
SetHasValidDir();
dir = aValue->Equals(nsGkAtoms::rtl, eIgnoreCase) ? eDir_RTL : eDir_LTR;
SetDirectionality(dir, aNotify);
} else {
ClearHasValidDir();
dir = RecomputeDirectionality(this, aNotify);
}
SetDirectionalityOnDescendants(this, dir, aNotify);
}
}
return nsGenericHTMLElementBase::AfterSetAttr(aNamespaceID, aName,

Просмотреть файл

@ -50,6 +50,7 @@ public:
NS_ASSERTION(mNodeInfo->NamespaceID() == kNameSpaceID_XHTML,
"Unexpected namespace");
AddStatesSilently(NS_EVENT_STATE_LTR);
SetFlags(NODE_HAS_DIRECTION_LTR);
}
/** Typesafe, non-refcounting cast from nsIContent. Cheaper than QI. **/
@ -203,6 +204,8 @@ public:
virtual void UpdateEditableState(bool aNotify);
virtual nsEventStates IntrinsicState() const;
// Helper for setting our editable flag and notifying
void DoSetEditableFlag(bool aEditable, bool aNotify) {
SetEditableFlag(aEditable);