Closed Bug 35768 Opened 25 years ago Closed 22 years ago

[review]CSS2 :lang pseudo-class won't match on LANG and xml:lang attributes [SELECT]

Categories

(Core :: CSS Parsing and Computation, defect, P2)

defect

Tracking

()

VERIFIED FIXED
mozilla1.2alpha

People

(Reporter: ess, Assigned: dbaron)

References

Details

(Keywords: css2, Whiteboard: [patch](py8ieh: need a thorough test case) (py8ieh:need test case which dynamically changes the xml:lang of a grandparent element))

Attachments

(11 files, 22 obsolete files)

155 bytes, text/html
Details
288 bytes, text/html
Details
3.14 KB, text/xml
Details
425 bytes, text/html
Details
2.75 KB, text/html
Details
2.96 KB, text/xml
Details
401 bytes, text/xml
Details
32.67 KB, patch
bzbarsky
: superreview+
Details | Diff | Splinter Review
2.54 KB, patch
Details | Diff | Splinter Review
857 bytes, patch
sicking
: review+
dbaron
: superreview+
Details | Diff | Splinter Review
591 bytes, text/xml
Details
From Bugzilla Helper:
User-Agent: Mozilla/5.0 (Windows; U; WinNT4.0; en-US; m14)
BuildID:    2000041210

HTML elements with their LANG attributes set are not styled according to
appropriate :lang() stylesheet rules.  This is probably the fault of bug 26237
(if I read correctly), but may be a fault in handling of CSS2.


Reproducible: Always
Steps to Reproduce:
1. Create style which depends upon :lang pseudo-class
2. Create HTML element with matching LANG attribute
3. View


Actual Results:  The style is not applied.

Expected Results:  The style should be applied to matching elements.

As in:

<html>
<head>
<style>
 span:lang(fr) { text-transform: uppercase; }
</style>
<body>
<p>Foo, <span lang="fr">foo,</span> foo.</p>
</body>
</html>
Style System or Parser maybe.  Trying Style System first.
Assignee: asadotzler → pierre
Component: Browser-General → Style System
QA Contact: jelwell → chrisd
*** Bug 14030 has been marked as a duplicate of this bug. ***
I STRONGLY suggest that this be marked "LATER" (i.e., fix in next version).

We have not committed to implementing CSS2, and this is not the easiest of
features to get right (it involves inheriting attributes down the DOM, and other
interesting excursions). We have plenty of CSS1 bugs to keep us busy for a 
while yet. Furthermore, since we already have the dash match ("|=") attribute 
selector implemented, eager stylesheet authors can already, to some extent, get 
this functionality.

BTW, we will need a thorough test case for this.
Status: UNCONFIRMED → NEW
Ever confirmed: true
Keywords: css2
OS: Windows NT → All
Priority: P3 → P4
Hardware: PC → All
Summary: CSS2 :lang pseudo-class won't match on LANG attributes → CSS2 :lang pseudo-class won't match on LANG and xml:lang attributes
Whiteboard: LATER? (py8ieh: need a thorough test case)
Wholeheartedly approved.
Marked LATER.
Status: NEW → RESOLVED
Closed: 25 years ago
Resolution: --- → LATER
Status: RESOLVED → VERIFIED
Whiteboard: LATER? (py8ieh: need a thorough test case) → (py8ieh: need a thorough test case)
Ok. We'll reopen this for 5.1...
Reopening and moving to Future. There probably exists duplicates of this bug.
Status: VERIFIED → REOPENED
Keywords: correctness
QA Contact: chrisd → py8ieh=bugzilla
Resolution: LATER → ---
Target Milestone: --- → Future
Summary: CSS2 :lang pseudo-class won't match on LANG and xml:lang attributes → CSS2 :lang pseudo-class won't match on LANG and xml:lang attributes [SELECT]
*** Bug 61319 has been marked as a duplicate of this bug. ***
Nominating this bug for nsbeta1 on behalf of gerardok@netscape.com.
Keywords: nsbeta1
I'll append a first simple patch toward an implementation of the lang pseudo
class.  This allows the parser to handle it correctly.  I hope this patch is
uncontroversial.  It changes the behavior of Mozilla a bit as far as to allow
the rules with the :lang() pseudo-class to be used.  It's just that the
restrictions coming from the lang attribute are not  checked.  I don't think
this situation is worse than what exists today.

Anyhow, I'd like to implement this completely and already looked at the code. 
But there are some major decisions to be made first (see also 

  http://bugzilla.mozilla.org/show_bug.cgi?id=24861

especially the comment by Daniel Glazman; I think the problem is similar).  The
problem here is that at all time it is necessary to know about the lang
attribute for each tag.  The initial language might even be defined by the HTML
reply and/or the language selected by the user.  Now it would be possible to
always walk the tree from the current node to the root and check for the
attributes but I guess this would be considered a problem by some people.  But
what is the alternative?  Forward propagation of the lang attribute is costly as
well.  And what do you do with a DOM call changing/adding/removing the attribute?

IMHO the tree-walking approach cannot be avoided without some major
reorganizations of the code.  To speed things up is is probably a good thing to
handle the lang attribute special in the nsIContent class, always add it as an
nsAtom* if set.  This will reduce the lookup to pointer comparisons.

I don't want to go ahead and do any of the work without some guidance.  What do
the owners of this code think is the right solution?  I know that you declared
this problem as "future" but to increase parallelism in the development this
kind of guidance is necessary.  Your priorities don't have to be shared by
everybody and everybody should be fine to implement the missing pieces.

I'd be happy to implement the simple, tree-walking approach now (which will have
impacts almost only on those pages using the lang pseudo-class) and modify it
later for a better solution.
Please ignore the patch is attached on 07/05/01.  It was missing and error
check.  The new patch has it and I'm using a build with the patch for 1 1/2 days
now.
I believe we already "know" the language of each element, although I'm not sure
how we do it. For example, right clicking on an element and choosing Properties
will give you the language of the element, so the data is there somewhere.

Hyatt? dbaron?
Ian Hickson wrote:
> I believe we already "know" the language of each element

I could see how.  I looked at the information available in the CSS functions and
there is no lang info anywhere.

So I went on and finished my patch.  I'll shortly attach it and in supercedes
the patches I've provided so far.  It should be a complete implementation with
one defficiency which is as far as I can say not in the code I touched here. 
You can see the defficiency in the test input file I'll also attach (it's about
changing the lang attribute of an element with the DOM functions).  Creating new
elements with DOM works as can be seen in the test case, too.  Oh, the test case
included in this bugzilla entry also works.

As for the patch, it is I think the best you can do given the current set of
information available in the nsIContent data.  The parser stores the :lang()
parameter as a string and the selector matching function tries to find an
element with a lang attribute set and then performs a string comparison.  Quite
simple but potentially slow.  There is, however, no negative effect on the
performance for input files which Mozilla managed to handle so far.  Only input
with :lang() usage in the style sheet will use the new code.  I don't know the
standards of the Mozilla team but I would say this is good enough to include the
code.

For the future, I have some ideas but would really like to have some input
first.  I think that the lang attribute is important enough to be treated
specially.  Therefore a member GetLang() should be added to nsIContent. 
Alternative a data member mLang could be used.   The language should be
represented by an nsIAtom type object.  The value will be automatically set if
an attribute named "lang" is set using the SetAttribute members.  When building
the document structure the language of the document itself should be determined
by the content-language specification in the HTTP reply.  The patch I've
provided would have to be changed only slightly: the nsAtomStringList type would
be changed to nsAtomPairList (with mString becoming a nsIAtom* object), the CSS
parser stores the language as an atom, and the selector matcher simply compares
the nsIAtom from the CSS selector with the return value of data.GetLang().  No
loops needed.

While this solution is fast at display time it'll require more work when
creating the document representation and also in the SetAttribute() functions. 
This should be acceptable especially since to solve the problem exposed in my
test case (using the DOM function setAttribute to change the lang value) also
requires the special treatment of such a call to initiated update of the display.

Any comment welcome.
Attached file more tests for :lang() handling (obsolete) —
Attached file more tests for :lang() handling (obsolete) —
Result of discussion in #mozilla about attachment 42159 [details]: the style hint for the
'quotes' property may need to be changed from REFLOW to FRAMECHANGE:

http://lxr.mozilla.org/seamonkey/source/content/shared/public/nsCSSPropList.h#188
I've compiled Mozilla with the proposed

 CSS_PROP(quotes, quotes, FRAMECHANGE)

and have seen no difference.  This doesn't fix the problem.
Hmm.  The FRAMECHANGE change may or may not be necessary.  However, what *is*
necessary (should have thought of this earlier...) is changing
CSSStyleSheetImpl::CheckRuleForAttributes at
http://lxr.mozilla.org/seamonkey/source/content/html/style/src/nsCSSStyleSheet.cpp#2037
to check for a lang selector and, if it exists, add the lang attribute (and
xml:lang attribute, although this seems not to be namespace-aware -- it's just a
performance hack) to the sheet's mRelevantAttributes.  (Are there any other
potentially relevant attributes?)
> However, what *is* necessary (should have thought of this
> earlier...) is changing CSSStyleSheetImpl::CheckRuleForAttributes at
>[...]

I've looked at this (actually implemented it) and think this still
cannot solve the problem.

This code would help if the lang attribute of the element which has a
CSS rule is changed.  But the lang attribute is transitive to all the
children (unless it's reset).  So, in a situation like

   <p lang="de">some <q>quoted</q> text.</p>

where there is a CSS rule with :lang() for <q> no the change would be
recognized.

The lang attribute is really special and might indeed need special
attention.


What makes things even stranger is that everything seems to work fine if I'm
using colors instead of quotes.  I've an updated test at

   http://www.cygnus.com/~drepper/quotes.html

With the patch applied the DOM code changing the lang attribute of the DIV
element with the ID "foo" has the effect that the color changes.
> This code would help if the lang attribute of the element which has a
> CSS rule is changed.  But the lang attribute is transitive to all the
> children (unless it's reset).

That shouldn't be a problem, since even with existing combinators we have
to re-resolve style for all descendants when an attribute that affects style
changes.
> That shouldn't be a problem, since even with existing combinators we have
> to re-resolve style for all descendants when an attribute that affects style
> changes.

OK, then maybe you want to inspect what code I added:

        if (iter->mPseudoClassList) {
          // Search for the :lang() pseudo class.  If it is there add
          // the lang attribute to the relevant attribute list.
          nsAtomStringList *runp = iter->mPseudoClassList;
          do {
            if (runp->mAtom == nsCSSAtoms::langPseudo) {
              // Found it.
              DependentAtomKey langKey(nsHTMLAtoms::lang);
              mInner->mRelevantAttributes.Put(&langKey, nsHTMLAtoms::lang);
              break;
            }
          } while ((runp = runp->mNext) != nsnull);
        }

This is additional code in the for(iter=) loop in the function you mentioned. 
I've verified that the Put() function is actually called.  Anything wrong with this?
Hmmm.  Maybe you're also running into bug 57226.  Can you tell if the style is
now actually being re-resolved, using the debugging tools in viewer?  Or you
could try another selector with "[lang]" as selector to work around the problem
for now in case that code is wrong...
> Hmmm.  Maybe you're also running into bug 57226.  Can you tell if the style is
> now actually being re-resolved, using the debugging tools in viewer?

You mean Debug|DOM Viewer?  When I select this menu entry I get

JavaScript error: 
 line 0: uncaught exception: [Exception... "Component returned failure code:
0x80004005 (NS_ERROR_FAILURE) [nsIDOMLocation.href]"  nsresult: "0x80004005
(NS_ERROR_FAILURE)"  location: "JS frame :: <unknown filename> :: oncommand ::
line 0"  data: no]

Not when I press a button but when I select the menu entry.

> Or you could try another selector with "[lang]" as selector to work around
> the problem for now in case that code is wrong...

If I replace :lang(de) with [lang="de"] nothing at all works.  And I'd expect
this since the lang attribute isn't set for the elements mentioned in the CSS.

> You mean Debug|DOM Viewer?

no, mozilla-viewer.sh or viewer.exe

> If I replace :lang(de) with [lang="de"] nothing at all works.

Not replace, but add an additional rule so that the stylesheet is noted as
having something that responds to the lang attribute.
> no, mozilla-viewer.sh or viewer.exe

OK.  I normally don't build with tests enabled.  Have to recompile first.

> Not replace, but add an additional rule so that the stylesheet is noted as
> having something that responds to the lang attribute.

Done that, no difference.
> Hmmm.  Maybe you're also running into bug 57226.  Can you tell if the style is
> now actually being re-resolved, using the debugging tools in viewer? 

I'm not at all experienced with the viewer debug tools but I think I tried every
combination without getting any result.  If you can suggest a specific procedure
I'll do that.

Please ignore the first added patch.  I really shouldn't edit the patch file
directly.  But I have so many accumulated changes in those files that it's hard
to regenerate the patch from scratch.
Attached patch *Corrected* updated patch (obsolete) — Splinter Review
Looks good (but that's not a formal review!). Do I take it you do not (yet) 
support the xml:lang attribute? 

I notice that as far as I can tell you explicitly look for the 'lang' attribute 
in the HTML namespace (right?). That seems wrong to me. You should look for the
'lang' attribute in NO namespace IF the element is an HTML element. The HTML 
spec doesn't say anything about a global 'lang' attribute in the HTML namespace.
If I misread that code then my apologies.
> Looks good (but that's not a formal review!). Do I take it you do not (yet) 
> support the xml:lang attribute? 

You're right, xml_lang isn't supported.  But it's not because of lack of trying.
 I couldn't figure out how.  If you can point me in the right direction I'm
happy to update my patch.
The xml:lang attribute SHOULD be the 'lang' attribute in the XML namespace. I'm
not sure exactly how we do that.

nisheeth/jst/heikki: could you help us here? How does one find the xml:lang
attribute?
> The xml:lang attribute SHOULD be the 'lang' attribute in the XML namespace. I'm
> not sure exactly how we do that.


Unless I'm mistaken the GetAttr function in the HTML code will not allow using
the kNameSpaceID_XML value (I think this is
content/src/nsGenericHTMLElement.cpp:1876).

And when looking through the data structures genereated  for theXML file I
couldn't find the xml:lang attribute altogether.

I think I haven't yet attached a XML test case.  Will do so after verifying it.
Have you checked if you can access xml:lang attribute in an XML document that
has no declared namespaces?

I know there is a bug regarding HTML/XHTML content: they do not support
additional namespaces. Therefore, if you try to add xml:lang to an XHTML element
it won't work. See bug 41983.
> Have you checked if you can access xml:lang attribute in an XML document that
> has no declared namespaces?

What I actually did is starting moz in gdb and looking at the code accessing the
attributes to see how the xml:lang attributes are represented.  The result: they
are not there at all.  In the XHTML test case I attached the only attributes
visible are the xmlns attribute at the toplevel and the lang attributes in the
preference test.  No sign whatsoever of the xml:lang attributes.

I might have look at the wrong place but it sure looks to me as if xml:lang
isn't recognized as a valid attribute name and therefore not added to the
internal representation.
There is a bug which makes them not appear for elements in the XHTML namespace.
Look again, but on elements that have no namespace, e.g. in the following document:

   <foo xml:lang="en"/>
I've just created bug 98929 for a patch to implement Content-Language handling
on document level.  This implementation than can be used to (almost) finish the
:lang() pseudo-class implementation (only xml:lang is missing).  The patch is
tested, see bug 98929 for an explanation how to configure Apache.
Keywords: patch
Attached patch Update after nsDocument change (obsolete) — Splinter Review
Now that nsDocument provides language information (bug 98929) I've updated the
patch once more (attachment 49051 [details] [diff] [review]).  This patch avoids converting from Unicode
and does all conversion with Unicode chars.

There is one more change: the language string returned from nsDocument can be a
list.  I think it only makes sense to use the first listed language and ignore
the others when looking for a match.
This is not a full review - I thought the first 2 points below should be 
discussed before going further.

1) I haven't looked into it in detail but wouldn't have been simpler to add the 
language directly into the nsCSSSelector instead of putting it inside the 
mPseudoClassList?

2) I think you should not a match only on the first item in the 'Content-
Language' list. You wrote in the patch that doing otherwise "might lead to not 
reproducible results".  If you consider the example at 
http://www.w3.org/TR/REC-CSS2/selector.html#x42, the first pair of rules defines 
a precedence of HTML:lang(de) over HTML:lang(fr) in the case of a document that 
defines "fr,de" simply because the "de" rule comes after the "fr" rule.  With the 
proposed patch, only the first rule applies, which is wrong - and it is even more 
wrong for documents that define "en,fr,de" since none of the 2 rules apply.

3) In nsAtomStringList::Equals() and SelectorMatches(), you should use 
EqualsIgnoreCase() to check the two strings.

4) I saw some NS_RELEASE where nsCOMPtr would be more appropriate.

Regarding point one, I'm not entirely sure I understand it, but :lang() is indeed
just a pseudo-class, and rules like

   :lang(en):lang(de):lang(fr)

...and perfectly valid (if pointless) as are rules like:

   :not(:lang(x-klingon)):not(:lang(x-vulcan)):lang(x)
Severity: enhancement → normal
Status: REOPENED → ASSIGNED
Priority: P4 → P3
Target Milestone: Future → mozilla0.9.6
Blocks: 104166
I'll attach a patch that fixes #2, 3 and 4 above (#1 isn't such a good idea in 
case we need to implement more pseudo-classes that take a string as a parameter).

I tested with a couple of testsuites and a simple testcase.  Before loading the 
testcase, go to Prefs -> Navigator -> Languages and add "French" or "German" in 
addition to "English/US".

Daniel or DBaron: please review
Marc: please s/r
Attached patch patch 2.0 (obsolete) — Splinter Review
Attached file testcase
Comment on attachment 54201 [details] [diff] [review]
patch 2.0

r=glazman

I have only one concern : memory footprint... the pseudo-class list turned into a nsAtomStringList seems quite an expensive choice for a selector we don't use for the moment in our app stylesheets.
Attachment #54201 - Flags: review+
We should be fine regarding memory footprint:
- The string is allocated only when needed, which means never except for :lang.   
- nsAtomStringList takes 3 pointers instead of 2 for nsAtomList, which is still 
ok even if we have hundreds of pseudo-classes.

Note: I modified the patch a little bit in my local tree to optimize the use of 
language.Length().
Comment on attachment 54201 [details] [diff] [review]
patch 2.0

>Index: mozilla/content/html/style/src/nsCSSStyleSheet.cpp
>===================================================================
>RCS file: /m/pub/mozilla/content/html/style/src/nsCSSStyleSheet.cpp,v
>retrieving revision 3.182
>diff -u -2 -r3.182 nsCSSStyleSheet.cpp
>--- nsCSSStyleSheet.cpp	2001/09/29 08:25:58	3.182
>+++ nsCSSStyleSheet.cpp	2001/10/19 10:42:08
>@@ -2293,5 +2293,18 @@
>           mInner->mRelevantAttributes.Put(&key, sel->mAttr);
>         }
>+        if (iter->mPseudoClassList) {
>+          // Search for the :lang() pseudo class.  If it is there add
>+          // the lang attribute to the relevant attribute list.
>+          nsAtomStringList *runp = iter->mPseudoClassList;
>+          do {
>+            if (runp->mAtom == nsCSSAtoms::langPseudo) {
>+              // Found it.
>+              DependentAtomKey langKey(nsHTMLAtoms::lang);
>+              mInner->mRelevantAttributes.Put(&langKey, nsHTMLAtoms::lang);
>+              break;
>+            }
>+          } while ((runp = runp->mNext) != nsnull);
>       }
>+      }
>     } /* fall-through */
>     default:

Why not a while-do without the extra if?  (Well, OK, this may be better on
some processors, so leave it if you want.)  And also, the indentation seems
odd (as in a few other places).  Are there tabs here?  This isn't a diff -w.

>@@ -3331,5 +3344,5 @@
>     if(PR_FALSE == mIsHTMLLink &&
>        mHasAttributes && 
>-       !(aContent->IsContentOfType(nsIContent::eHTML) || aContent->IsContentOfType(nsIContent::eXUL)) && 
>+       !(PR_TRUE == mIsHTMLContent || aContent->IsContentOfType(nsIContent::eXUL)) &&
>        nsStyleUtil::IsSimpleXlink(aContent, mPresContext, &mLinkState)) {
>       mIsSimpleXLink = PR_TRUE;

I really don't like the |PR_TRUE ==| -- it's especially dangerous for truth
tests since occasionally booleans get set to a true value other than 1.

>@@ -3450,4 +3463,30 @@
> 
> 
>+static PRBool DashMatchCompare(const nsString& comparedValue, const nsString& baseValue, const PRBool aCaseSensitive) {

Parameters should be |const nsAString&| rather than |const nsString&| unless
you have good reason to use things on |nsString| that aren't on |nsAString|,
which you don't here.  Also, below I'm going to point out a place where you
need to be passing in an nsAString that isn't an nsString.

>+  PRBool result;
>+  PRUint32 baseLen = baseValue.Length();
>+  PRUint32 compLen = comparedValue.Length();
>+  if (baseLen > compLen) {
>+    result = PR_FALSE;
>+  }
>+  else {
>+    if (baseLen != compLen &&
>+        comparedValue.CharAt(baseLen) != PRUnichar('-')) {
>+      // to match, the comparedValue must have a dash after the end of
>+      // the baseValue's text (unless the baseValue and the comparedValue
>+      // have the same text)
>+      result = PR_FALSE;
>+    }
>+    else {
>+      if (aCaseSensitive)
>+        result = PRBool(NS_OK == Compare(Substring(comparedValue, 0, baseLen), baseValue, nsDefaultStringComparator()));
>+      else
>+        result = PRBool(NS_OK == Compare(Substring(comparedValue, 0, baseLen), baseValue, nsCaseInsensitiveStringComparator()));

No need to cast to bool, and testing against NS_OK is wrong, it should
be a test against 0 (which happens to be NS_OK, but Compare is like
strcmp, it's not returning an nsresult).

>+    }
>+  }
>+  return result;
>+}
>+
>+
> static PRBool SelectorMatches(SelectorMatchesData &data,
>                               nsCSSSelector* aSelector,
>@@ -3576,6 +3615,66 @@
>       }
>       else if (nsCSSAtoms::langPseudo == pseudoClass->mAtom) {
>-        // XXX not yet implemented
>-        result = PR_FALSE;
>+        NS_ASSERTION(nsnull != pseudoClass->mString, "null lang parameter");
>+        result = localFalse;
>+        if (pseudoClass->mString) {
>+          // We have to determine the language of the current element.  Since
>+          // this is currently no property and since the language is inherited
>+          // from the parent we have to be prepared to look at all parent
>+          // nodes.  The language itself is encoded in the LANG attribute.
>+          PRBool decided = PR_FALSE;
>+
>+          nsCOMPtr<nsIContent> element = data.mContent;
>+          while (element) {
>+            PRInt32 attrCount = 0;
>+            element->GetAttrCount(attrCount);
>+            if (attrCount > 0) {
>+              nsAutoString value;
>+              nsresult attrState = element->GetAttr(kNameSpaceID_HTML,
>+                                                      nsHTMLAtoms::lang, value);
>+              if (attrState == NS_CONTENT_ATTR_HAS_VALUE) {
>+                // Compare the attribute string with the language string from
>+                // the style sheet.
>+                result = PRBool(localTrue == DashMatchCompare(value, *pseudoClass->mString, PR_FALSE));
>+                decided = PR_TRUE;
>+                break;
>+              }
>+            }
>+            nsIContent *parent;
>+            element->GetParent(parent);
>+            element = dont_AddRef(parent);
>+          };

This is an extremely expensive loop for selector matching.  Could you lazily
cache the resulting language on the SelectorMatchesData (i.e., access it through
a getter and store a state variable whether you've gotten it or not)?  (We should
start lazily caching other things on the SelectorMatchesData as well, so now is as
good a time as any to start.)

Also, there's a stray semicolon right at the end there.

>+
>+          if (!decided) {
>+            nsIDocument *doc;
>+            if (NS_SUCCEEDED(data.mContent->GetDocument(doc))) {
>+              // Try to get the language from the HTTP header or if this
>+              // is missing as well from the preferences.
>+              // The content language can be a comma-separated list of
>+              // language codes.
>+              nsAutoString language;
>+              if (NS_SUCCEEDED(doc->GetContentLanguage(language)) && (language.Length() > 0)) {
>+                if (nsnull != pseudoClass->mString) {
>+                  language.StripWhitespace();
>+                  PRInt32 begin = 0;
>+                  PRInt32 end = 0;
>+                  while (begin < language.Length()) {
>+                    end = language.FindChar(PRUnichar(','), PR_FALSE, begin);
>+                    if (end == kNotFound) {
>+                      end = language.Length();
>+                    }
>+                    nsAutoString httpLang;
>+                    language.Mid(httpLang, begin, end);
>+                    PRBool match = DashMatchCompare(*pseudoClass->mString, httpLang, PR_FALSE);

Ugh, don't construct an nsAutoString and copy into it, just use
|Substring(language, begin, end-begin)|.  This requires that the
parameter be a |const nsAString&| as I said above.

>+                    if (match) {
>+                      result = PRBool(localTrue == match);
>+                      break;
>+                    }
>+                    begin = end + 1;
>+                  }
>+                }
>+              }
>+            }
>+          }
>+        }
>       }
>       else if (IsEventPseudo(pseudoClass->mAtom)) {

>Index: mozilla/content/html/style/src/nsICSSStyleRule.h
>===================================================================
>RCS file: /m/pub/mozilla/content/html/style/src/nsICSSStyleRule.h,v
>retrieving revision 3.22
>diff -u -2 -r3.22 nsICSSStyleRule.h
>--- nsICSSStyleRule.h	2001/09/25 01:31:18	3.22
>+++ nsICSSStyleRule.h	2001/10/19 10:41:47
>@@ -64,4 +64,17 @@
> };
> 
>+struct nsAtomStringList {
>+public:
>+  nsAtomStringList(nsIAtom* aAtom, const nsString *aString = nsnull);
>+  nsAtomStringList(const nsString& aAtomValue, const nsString *aString = nsnull);
>+  nsAtomStringList(const nsAtomStringList& aCopy);
>+  ~nsAtomStringList(void);
>+  PRBool Equals(const nsAtomStringList* aOther) const;
>+
>+  nsIAtom*          mAtom;
>+  const nsString*   mString;
>+  nsAtomStringList* mNext;
>+};

Bloat is a concern here, as Daniel said.  Perhaps the language string
should be an atom rather than a string?  Also, mAtom should probably
be an |nsCOMPtr<nsIAtom> mAtom| since it is an owning pointer, rather
than dealing with manual NS_ADDREF/NS_RELEASE.  There is a precedent
for atomizing language strings -- see nsILanguageAtom in addition to
nsIAtom.  Using |nsString*| rather than |nsString&| and |nsString| is
rather unconventional, although in this case we certainly don't want
the bloat in all cases.

Also, one could construct the linked list
out of two classes, one derived from the other, and having an nsString
member at the end being the only difference in the derived class, ensuring
that you always had an instance of the derived class if the pseudo is
lang, and casting.  This would cause zero bloat increase except when there
is a :lang pseudo.
> Why not a while-do without the extra if?

Or, even better, a |for| loop.
Thanks for the review.  I made the changes above except:
- kept nsString: one of the methods is used.
- did not convert mString to an atom: it would not bring much benefits if any, 
the language comparisons need to be case-insensitive, nsILanguageAtom is not an 
atom either.
- did not add the casting: the total bloat is 8k (approx. 2000 strucs throughout 
the app), I chose legibility and maintainability over footprint but it can be 
debated - your call, really.

Marc: s/r please.
Attached patch patch 2.1 (obsolete) — Splinter Review
> - kept nsString: one of the methods is used.

Which method?  Many of the methods have replacements.  (See nsReadableUtils.h.)

The performance of this patch looks even worse than the previous one. 
SelectorMatchesData::GetLang should return an nsAFlatString&, not fill in an
nsAWritableString&.  I think you should also fix the other problems I mentioned.
- DashMatchCompare() needs CharAt().
- The casting would hurt the performance on the copy constructor.

Result:
- I just made a small change in my tree for GetLang() to return a pointer.


Marc: s/r please.
Summary: CSS2 :lang pseudo-class won't match on LANG and xml:lang attributes [SELECT] → [review]CSS2 :lang pseudo-class won't match on LANG and xml:lang attributes [SELECT]
Instead of CharAt you can use:

nsAString::const_iterator iter;
...
  *comparedValue.BeginReading(iter).advance(baseLen) != PRUnichar('-') 


You should also address the other comments.
... or once I check in the patch on bug 104651 you could just use const
|nsASingleFragmentString&| with the result of |Substring| on a known flat or
single fragment string.
waterson/attinasi: please take a look at the performance problem below...

> Instead of CharAt you can use:
> nsAString::const_iterator iter;
>   *comparedValue.BeginReading(iter).advance(baseLen) != PRUnichar('-') 

The proposed patch creates a nsAutoString on the stack (guaranteed single 
fragment) and then calls CharAt(), both of fairly negligible CPU time.  I'll 
leave it to super-reviewers to decide which solution is better but at first 
sight, the code above seems less legible and not really more efficient.

> You should also address the other comments.

I think that I included all the other changes except for the atomization of 
language strings and the overlapping structs because of the reasons listed in my 
previous comments.
I stand corrected: your solution with "Substring/const_iterator" is twice as fast 
as "Mid/CharAt".  The times are quite variable but in average I get approximately 
8 microseconds instead of 16.  Thanks!
What is the overall performance impact on pages that do not make use of this
feature? Also, there is some bloat here that is not going to help us make
Mozilla smaller. Do we really need this now?

As for the code, assuming that the base performance is unchanged, sr=attinasi,
although it might be nice to move the logic out of SelectorMatches into an
inline method to keep SelectorMatches more compact.
Creating an nsAutoString on the stack is *not* cheap when you do it many times.
 It used to be a significant percentage of time in SelectorMatches (above 10%, I
think) was spent in the constructor and destructor of nsAutoString.
If you check this in, I'm going to have to go clean up the performance problems
myself.  I guess I'll just become the one who cleans up after you since you seem
intent on checking this in.
My apologies if I did not make myself clear: the patch I intended to checkin 
addresses all your concerns about performance.  The measurements I took after 
your comments yesterday showed that your solution resulted in an execution time 
of 8 microseconds instead of 16 microseconds and, of course, that's the one I had 
in my tree.

Anyhow, it is not the right time to work on features like this.  We'll reconsider 
later if the :lang() pseudo-class is worth 8k of bloat.  MacIE5 implements this 
feature but not WinIE5 so the number of pages that currently use it is 
negligible.

The 8k bloat seems to me unavoidable unless we accept a loss of performance.  
I'll attach an updated patch as a backup for when we come back to it.
Target Milestone: mozilla0.9.6 → mozilla1.0
Attached patch patch 2.2 (obsolete) — Splinter Review
There are two problems with that patch that I'd see at a quick glance:

 * GetLang could just return nsAFlatString&, not nsString*, and the IsEmpty
check could be done by the caller.
 * GetLang should be caching the entire lang check, not just the first part of it.
>- did not convert mString to an atom: it would not bring much benefits if any, 
>the language comparisons need to be case-insensitive, nsILanguageAtom is not an 
>atom either.

The langGroup is an atom (which by the way is used in the font engine to select 
adequate fonts). Could someone take heed to my request to have other string-like 
comparison APIs directly on nsIAtom? nsIAtom::StringEquals[IgnoreCase](aString),
nsIAtom::StringEqualsIgnoreCase(anotherAtom), so that the ever changing string 
fu can be localized in one place. The usefulness of these APIs seems to be 
manifest in several contexts.
I don't think those APIs make sense -- I'd rather see a DependentAtomString
function that returns a |const nsAFlatString&| (really a const
nsDependentString) that is a dependent string on the atom, and then you can test
(Compare(DependentAtomString(atom1), DependentAtomString(atom2),
nsCaseInsensitiveStringComparator()) == 0).
Although this wouldn't be |nsresult|-like, so long as there is an easy way to do
these string-fu operations which occur frequently and are often handled by
people in suboptimal ways (e.g., string copies).
Keywords: mozilla1.0
David Baron told me the existence of this bug when he was reivewing 
bug 105199. Both bugs are handling "lang" html attribute, but probably
for different purpose. Could the experts here take a look at that bug?
We probably need a single patch to address both problems. 
> We'll reconsider later if the :lang() pseudo-class is worth 8k of bloat.

Please note that this blocks bug 16206, which is an HTML 4 compliance issue.
While the Q element doesn't handle different languages (and nesting levels)
intelligently, there's not much point in using it instead of using the
presentational equivalent.
See also bug 115121.
Moving to mozilla1.1. Engineers are overloaded!
Target Milestone: mozilla1.0 → mozilla1.1
Bulk moving from Moz1.1 to future-P1. I will pull from this list when scheduling
work post Mozilla1.0.
Priority: P3 → P1
Target Milestone: mozilla1.1 → Future
cc'ing myself
There is a test case at http://www.richinstyle.com/test/language/lang2.php.
Target Milestone: Future → mozilla1.2alpha
Assigning pierre's remaining Style System-related bugs to myself.
Assignee: pierre → dbaron
Status: ASSIGNED → NEW
Priority: P1 → P2
Whiteboard: (py8ieh: need a thorough test case) → [patch](py8ieh: need a thorough test case)
Essentially patch 2.2, but the return value of GetLang is not tested for being
an empty string twice.	GetLang simply always returns a string reference.
The patch in attachment 90923 [details] [diff] [review] is basically patch 2.2 but with one little
optimization dbaron mentioned: the return value of GetLang should not be tested
twice for being empty.  GetLang now always returns a nsString*.

With regard to other optimizations mentioned: I don't think it is good to change
GetLang to take a nsAutoString& parameter and put the value there.  This would
involve copying a string for no reason.  If we don't have mLanguage in
SelectorMatchesData we'd at least have to call element->GetAttr() for every
GetLang call and copy the string.  I hardly think this is an optimization.

With regard to caching, is this really beneficial?  Where do you suggest it to
happen?  We might be able to merge the DashMatchCompare functionality into
GetLang (renaming it to IsLang or so) and cache the results for various lookups
with different input strings.  But this would make the SelectorMatchesData class
even heavier.  Is it worth it?
This is the previous patch, but merged to the current trunk (the diffs for that
patch are against a really really old tree).  I fixed a few style nits and
other things, but there's still a few other things that I'd like to fix.

Also, what's the deal with xml:lang?
Comment on attachment 91006 [details] [diff] [review]
variant of previous patch merged to current trunk

ignore this patch.  There's something wrong with the selector matching code. 
(I should stop trying to do three things at once...)
Attachment #91006 - Attachment is obsolete: true
> Also, what's the deal with xml:lang?

At the time when I wrote the patch the namespace handling was b0rken.  Does it
work correctly nowadays?

I think the only necessary change is in GetLang where

      if (attrCount > 0) {
        nsAutoString value;
        nsresult attrState = element->GetAttr(kNameSpaceID_HTML,
                                                nsHTMLAtoms::lang, value);
        if (attrState == NS_CONTENT_ATTR_HAS_VALUE) {
          mLanguage = value;
          break;
        }

has to be replaced with something like

      if (attrCount > 0) {
        nsAutoString value;

        if ((IS_XML_DTD &&
             element->GetAttr(kNameSpaceID_XML, nsHTMLAtoms::lang, value) ==
             NS_CONTENT_ATTR_HAS_VALUE)
            || (element->GetAttr(kNameSpaceID_HTML, nsHTMLAtoms::lang, value)
                == NS_CONTENT_ATTR_HAS_VALUE)) {
          mLanguage = value;
          break;
        }

What I don't know is how IS_XML_DTD must look like.
Attached patch mostly working merged patch (obsolete) — Splinter Review
This is a mostly-working merged patch (modulo some issues with attachment
54202 [details]).  I probably won't have a chance to look at this more for at least a few
days, though, and I haven't looked through the whole thing yet closely.

I did add the xml:lang support very quickly (and it seems to work based on
attachment 48154 [details]).  The attribute problems on HTML content should be fixed
(thanks to sicking, I think).
Attached file HTML :lang test
More complete test based on 42159 and 42160.
Attachment #42159 - Attachment is obsolete: true
Attachment #42160 - Attachment is obsolete: true
I've attached a bit more complete test based on earlier tests of mine.

With the patch applied the other tests in the attachments here seem to work. 
The just attached test also mostly works.  The only problem is the second test
with dynamically changing quotes (I am using the quotes patch from bug 24861). 
The change discussed in comment #20 does not fix the problem.

Not that a very similar test which uses colors instead of quotes works just fine.
Blocks: 115121
Whiteboard: [patch](py8ieh: need a thorough test case) → [patch](py8ieh: need a thorough test case) (py8ieh:need test case which dynamically changes the xml:lang of a grandparent element)
Attached file XHTML xml:lang test (obsolete) —
Attachment 91210 [details] contains the XHTML versio of the previous attachment.  The
results are not as good:

- the same test as in the HTML test fails (expected, I guess)

- the last test, adding a paragraph, fails in two ways

  + no coloring happens
  + the paragraph is not added as a paragraph.  I.e., it's not on a separate
    line

  The latter might be my mistake.  Let me know if it is.

- when using setAttribute("xml:lang", la) instead of setAttribute("lang", la)
  the button-press tests fail.  When using "lang" the coloring test succeeds.


Note that this does contain setting the xml:lang of a grandfather node.

> The change discussed in comment #20 does not fix the problem.

Since 'lang' is a special attribute with cascading effects, it may be that its
impact has to become FRAMECHANGE too:
http://lxr.mozilla.org/seamonkey/source/content/html/content/src/nsGenericHTMLElement.cpp#3504
> Since 'lang' is a special attribute with cascading effects, it may be that its
> impact has to become FRAMECHANGE too:

Yes, that seems to work:

diff -u -r1.367 nsGenericHTMLElement.cpp
--- content/html/content/src/nsGenericHTMLElement.cpp	4 Jul 2002 04:30:19 -0000
1.367
+++ content/html/content/src/nsGenericHTMLElement.cpp	19 Jul 2002 04:09:40 -0000
@@ -3509,7 +3509,7 @@
     return PR_TRUE;
   }
   else if (nsHTMLAtoms::lang == aAttribute) {
-    aHint = NS_STYLE_HINT_REFLOW; // LANG attribute affects font selection
+    aHint = NS_STYLE_HINT_FRAMECHANGE; // LANG attribute affects font selection
     return PR_TRUE;
   }
   /*

The only strange effect is that after pressing the butten the layout is a bit
different.  It seems as if an additional empty line got added.  I get the same
result with the XHTML test case.

The only difference between the HTML and XHTML test case is now that adding a
new paragraph simply appends the new text to the last line instead of adding a
new line.  And this only for XHTML.
> The only strange effect is that after pressing the butten the layout is a bit
> different.

Might be a layout bug then.

> The only difference between the HTML and XHTML test case is now that adding a
> new paragraph simply appends the new text to the last line instead of adding a
> new line.  And this only for XHTML.

Looking at your script, the XHTML namespace seems to be missing:

- var newp = document.createElement("p");
+ var newp = document.createElementNS("http://www.w3.org/1999/xhtml", "p");

- newp.setAttribute("xml:lang", la);
+ newp.setAttribute("lang", la);
Fixing the Javascript code: use createElementNS.
Attachment #91210 - Attachment is obsolete: true
> Looking at your script, the XHTML namespace seems to be missing:

You're right, thanks.  I've updated the test, the result is in attachment 91921 [details].


The only issue remaining is the layout change after executing the DOM code for
the first time.


The patch at hand here seems to work fine.  Can we get it reviewed and checked in?
Comment on attachment 91026 [details] [diff] [review]
mostly working merged patch

Is the following XXX comment still necessary? Looks like the patch is taking
care of "xml:base" too, no?

+      // XXX What about xml:lang ?
+      PRInt32 attrCount = 0;
+      content->GetAttrCount(attrCount);
+      if (attrCount > 0) {
+	 nsAutoString value;
+	 nsresult attrState = content->GetAttr(kNameSpaceID_XML,
+					       nsHTMLAtoms::lang, value);
+	 if (attrState != NS_CONTENT_ATTR_HAS_VALUE &&
+	     content->IsContentOfType(nsIContent::eHTML)) {
+	   attrState = content->GetAttr(kNameSpaceID_None,
+					nsHTMLAtoms::lang, value);
+	 }

Mind attaching a patch for the whole lot, i.e., with the other fixup?
s/xml:base/xml:lang/
Also, the comment here should probably tips at the :lang() pseudo too...
+    aHint = NS_STYLE_HINT_FRAMECHANGE; // LANG attribute affects font selection
This is exactly dbaron's patch without the comment questioning the xml:lang
implementation.
Attachment #90923 - Attachment is obsolete: true
Patch misses the FRAMECHANGE bit, is it intentional?
> Patch misses the FRAMECHANGE bit, is it intentional?

Yes, I don't know what exactly you want.
I mean the diff that you have in comment #93 (for which the comment needs to be
updated to reference :lang too).
> I mean the diff that you have in comment #93 (for which the comment needs to
> be updated to reference :lang too).

Should this change really be part of this patch or should it be separately filed?

If it should be here, how do you think the comment should change?

     return PR_TRUE;
   }
   else if (nsHTMLAtoms::lang == aAttribute) {
     // LANG attribute affects font selection and the :lang() pseudo class
     // in CSS can cause arbitrary changes
     aHint = NS_STYLE_HINT_FRAMECHANGE;
     return PR_TRUE;
   }
   /*
> Should this change really be part of this patch or should it be separately
> filed?

I think so, since it is needed for the patch to pass the testcases. It is the
layout glitch that you mentioned that needs another bug (which might linger
there as usual. In the meantime, let's have information wins over presentation.
Plus it will be so rare to see a real website with a JS that flips the 'lang'
anyway...).

> If it should be here, how do you think the comment should change?

Your proposed comment looks okay to me.
Again, dbaron's patch plus the comment change plus the patch described in
comment 93 with updated comment in the sources.
Attachment #41289 - Attachment is obsolete: true
Attachment #41526 - Attachment is obsolete: true
Attachment #42158 - Attachment is obsolete: true
Attachment #46332 - Attachment is obsolete: true
Attachment #46333 - Attachment is obsolete: true
Attachment #48738 - Attachment is obsolete: true
Attachment #49051 - Attachment is obsolete: true
Attachment #49616 - Attachment is obsolete: true
Attachment #91922 - Attachment is obsolete: true
Comment on attachment 91928 [details] [diff] [review]
Same as last patch + the change to set FRAMECHANGE hint for lang attribute

r/sr=rbs. Nice job guys. This bug has been an exercise of determination and
resilience. An edge over IE6 since my copy of it is failing miserably on the
testcases.
Attachment #91928 - Flags: review+
I have applied the patch to check out the layout glitch... It is one of those  
flickers that sometimes happen in layout. It goes away at the slightest 
intention to resize the window -- meaning it is layout.

All the valid attached testcases pass. The one in comment #80 has a bug. It 
claims that the lang of the document is 'de' when there is actually no lang 
there (e.g., the context menu doesn't display the 'Properties'). But continuing 
the test and clicking on the ':lang()' link shows that the rest works fine
(again 'Properties' is a good indicator to see if the lang is set).
Comment on attachment 91928 [details] [diff] [review]
Same as last patch + the change to set FRAMECHANGE hint for lang attribute

>+++ mozilla/content/html/content/src/nsGenericHTMLElement.cpp	19 Jul 2002 08:02:01 -0000
>@@ -3509,7 +3509,9 @@
>     return PR_TRUE;
>   }
>   else if (nsHTMLAtoms::lang == aAttribute) {

Existing else after return is a non-sequitur -- fix while you're hacking here?

/be

>-    aHint = NS_STYLE_HINT_REFLOW; // LANG attribute affects font selection
>+    // LANG attribute affects font selection and the :lang() pseudo class
>+    // in CSS can cause arbitrary changes, bug 35768
>+    aHint = NS_STYLE_HINT_FRAMECHANGE;
>     return PR_TRUE;
>   }
>   /*
Following Brendan's  comment I've removed all the useless elses.
Attachment #91928 - Attachment is obsolete: true
Making the change hint for a lang attribute unconditionally be a framechange is
incorrect.  When attributes change, we will reresolve style if necessary, and
compute the correct things that need to happen as a result of the style change.
 If AttributeAffectsStyle is implemented correctly, this should be unnecessary.
 (It probably isn't yet, for xml:lang.)

There were also a few other changes I wanted to make to this patch.
That |#if 0|ed code about the class attribute being a framechange (in
nsGenericHTMLElement.cpp) should just be removed completely.

The mRelevantAttributes code should actually be sufficient for the
AttributeAffectsStyle changes, since it's not namespace sensitive (although
perhaps it's worth a comment that if it ever becomes namespace-sensitive, what
we care about is the un-namespaced lang attribute on HTML-namespaced content and
the lang attribute in the XML namespace.
> The mRelevantAttributes code should actually be sufficient

It is not sufficient since the patch didn't pass all the tescases. I suspect the
reason why it is not sufficient is because the frames that handle the quotes are
created as generated frames and these have to be changed when the lang is
changed. I know that it is possible to fix that, but as I noted, the dynamic
change of 'lang' is so rare that I wouldn't give it such a high prioririty to
the point of totally blocking the patch (it was mostly idealism vs.
practicality).

I can comment more about the peculiarity of xml:lang now that I have traced the
code. But anyone tracing the code will see too.
Is the testcase that doesn't pass one that dynamically changes the 'quotes'
property?  It seems much more likely that the problem is one with dynamic
changes to a specific property, rather than dynamic changes that affect certain
types of selectors.
> Is the testcase that doesn't pass one that dynamically changes the 'quotes'
> property?

The test failing without the extra patch is the one qhich selects the quotes
based  on the lang attribute.  The very similar test which selects a color based
on the attribute succeeds.
Comment on attachment 91998 [details] [diff] [review]
Same as last patch with useless 'else's removed

That means the lang attribute change in nsGenericHTMLElement.cpp should be
replaced by a change to nsStyleQuotes::CalcDifference in nsStyleStruct.cpp
(perhaps with a comment that it could be a reflow rather than a framechange if
our quotes code worked differently).
> That means the lang attribute change in nsGenericHTMLElement.cpp should be
> replaced by a change to nsStyleQuotes::CalcDifference in nsStyleStruct.cpp

I've tried this patch after removing the nsGenericHTMLElement.cpp change.

diff -u -r3.34 nsStyleStruct.cpp
--- content/shared/src/nsStyleStruct.cpp	3 Jul 2002 17:14:31 -0000	
3.34
+++ content/shared/src/nsStyleStruct.cpp	23 Jul 2002 07:18:42 -0000
@@ -1251,7 +1251,7 @@
     PRUint32 ix = (mQuotesCount * 2);
     while (0 < ix--) {
       if (mQuotes[ix] != aOther.mQuotes[ix]) {
-        return NS_STYLE_HINT_REFLOW;
+        return NS_STYLE_HINT_FRAMECHANGE;
       }
     }
 

I think this is what you went.  The result isn't good: the test case does not
work.  Did you have something else in mind?
You need to change both occurrences of NS_STYLE_HINT_REFLOW in that function.

(Which test case is it that doesn't work?)
> You need to change both occurrences of NS_STYLE_HINT_REFLOW in that function.

Nope, no improvement.

> (Which test case is it that doesn't work?)

The one in attachment 91046 [details] and the one in 91921.  In both cases the first test
with buttons.  The other tests work just like before the
nsGenericHTMLElement.cpp change.
I don't see how it would make a difference in the testcase in question, but we
also need to move the quotes section in nsStyleContext::CalcStyleDifference into
the framechange section rather than the reflow section.
Incorporating dbaron's suggestions.  This actually does produce the correct
results.
I've implemented and tested what dbaron suggested.  The patch in attachment
92599 [details] [diff] [review] removes the change to nsGenericHTMLElement infavor of changes to
nsStyleStruct and nsStyleContext.  I've also added appropriate comments.  If
this patch acceptable?
Comment on attachment 92599 [details] [diff] [review]
patch for Change StyleStruct and StyleContext instead of GenericHTMLElemtn

A bunch of nits, and two significant comments (first, moving the
document language stuff out of SelectorMatches and into the
RuleProcessorData; second, whether AtomStringList should hold raw
PRUnichar* rather than nsString*).  I *think* these should be it.  (Yes,
I realize I'm making this drag on forever...):


> Index: mozilla/content/html/style/src/nsCSSParser.cpp
> -  nsIAtom* pseudo = NS_NewAtom(buffer);
> +  nsCOMPtr<nsIAtom> pseudo(dont_AddRef(NS_NewAtom(buffer)));
> +

Could be the equivalent form

nsCOMPtr<nsIAtom> pseudo = do_GetAtom(buffer);

and there's no need to introduce extra whitespace, I don't think.


The parsing code is written in the style of the parser, which I can't
stand much (since early returns are underused), but I won't complain
since that's the style of the parser...

> Index: mozilla/content/html/style/src/nsCSSStyleRule.cpp
> -  mAtom = NS_NewAtom(aAtomValue);
> +  mAtom = dont_AddRef(NS_NewAtom(aAtomValue));

Likewise, |mAtom = do_GetAtom(aAtomValue);|.

> Index: mozilla/content/html/style/src/nsCSSStyleSheet.cpp
>  CSSStyleSheetImpl::GetEnabled(PRBool& aEnabled) const
>  {
> -  aEnabled = ((PR_TRUE == mDisabled) ? PR_FALSE : PR_TRUE);
> +  aEnabled = (mDisabled ? PR_FALSE : PR_TRUE);

Ugh.  How about |aEnabled = !mDisabled|?

> @@ -2063,7 +2063,7 @@
>  CSSStyleSheetImpl::SetEnabled(PRBool aEnabled)
>  {
>    PRBool oldState = mDisabled;
> -  mDisabled = ((PR_TRUE == aEnabled) ? PR_FALSE : PR_TRUE);
> +  mDisabled = (aEnabled ? PR_FALSE : PR_TRUE);

Likewise.

>        else if (nsCSSAtoms::langPseudo == pseudoClass->mAtom) {
> -        // XXX not yet implemented
> -        result = PR_FALSE;
> +        NS_ASSERTION(nsnull != pseudoClass->mString, "null lang parameter");
> +        result = localFalse;
> +        if (pseudoClass->mString) {
> +          // We have to determine the language of the current element.  Since> +          // this is currently no property and since the language is inherited
> +          // from the parent we have to be prepared to look at all parent
> +          // nodes.  The language itself is encoded in the LANG attribute.
> +          const nsString* lang = data.GetLang();
> +          if (!lang->IsEmpty()) {
> +            result = PRBool(localTrue == DashMatchCompare(*lang, *pseudoClass->mString, PR_FALSE));
> +          }
> +          else {
> +            nsIDocument *doc;
> +            if (NS_SUCCEEDED(data.mContent->GetDocument(doc))) {
> +              // Try to get the language from the HTTP header or if this
> +              // is missing as well from the preferences.
> +              // The content language can be a comma-separated list of
> +              // language codes.
> +              nsAutoString language;
> +              if (NS_SUCCEEDED(doc->GetContentLanguage(language))) {

This really needs to be in the RuleProcessorData (perhaps a second
lazy getter function?).

> +                language.StripWhitespace();
> +                PRInt32 begin = 0;
> +                PRInt32 end = 0;
> +                PRInt32 len = language.Length();
> +                while (begin < len) {
> +                  end = language.FindChar(PRUnichar(','), begin);
> +                  if (end == kNotFound) {
> +                    end = len;
> +                  }
> +                  PRBool match = DashMatchCompare(*pseudoClass->mString, Substring(language, begin, end-begin), PR_FALSE);
> +                  if (match) {
> +                    result = PRBool(localTrue == match);

This could be simplified to
result = localTrue;

> +                    break;
> +                  }
> +                  begin = end + 1;


> Index: mozilla/content/html/style/src/nsICSSStyleRule.h

> +struct nsAtomStringList {

It's worth a comment above this that the class is optimized for the case
that there is no string and it should not be used in cases where the
string is usually present.  However, that might not be necessary given
the following...

Also, is there a reason we want this class to store an |nsString*|
rather than a raw |PRUnichar*| (which requires one allocation rather
than two)?  I certainly would have used the latter if I were writing
this myself.
Two notes:
 * we should add a comment pointing to http://www.w3.org/TR/xhtml1/#C_7 which
says that the xml:lang takes precedence over lang on an HTML-namespace element.
 * xml:lang="" should probably be taken to "un-define" the language as far as
the document is concerned.  We should double-check that we do that.
Never mind about the content language stuff needing to be in the rule processor
data -- that won't speed it up significantly, since there's a good bit of
matching to do even once we have it.
This is the previous patch, with the changes I mentioned in comment 124,
excluding the one I mentioned in comment 126, plus a fix for a leaked reference
to an nsIDocument in SelectorMatches and an additional comment change I had
lying around in my tree.
Attachment #54201 - Attachment is obsolete: true
Attachment #55546 - Attachment is obsolete: true
Attachment #56308 - Attachment is obsolete: true
Attachment #91026 - Attachment is obsolete: true
Attachment #91998 - Attachment is obsolete: true
Attachment #92599 - Attachment is obsolete: true
nsCSSParser::ParseLangSelector:

+    if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof
+      REPORT_UNEXPECTED_EOF(NS_LITERAL_STRING("argument to :lang selector"));
+      aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR;
+      return;
+    }

Should that really be a PR_FALSE in GetToken()?  Is whitespace not allowed after
the '('?  (We need that CSS3 grammar ;) ).  Especially since we do 
"ExpectSymbol(aErrorCode, ')', PR_TRUE)" later...

The code in nsAtomStringList::Equals has lots of comparisons to "nsnull" that
should, imo, be simple boolean tests (eg |if (aOther)| instead of 
|if (nsnull != aOther)|. I guess that's the style of the code in that file,
but... (David's call as module owner here)

nsAtomStringList is checking the "rest of the list" before checking the
existence and equality of mString and aOther.mString.  Was this done for a
reason?  If so, add a comment explaining that reason so some well-meaning soul
does not "fix" it. (David mentioned this in comment 124, I see).

It feels like there should be a comment where you declare mPseudoClassList as an
nsAtomStringList that explains what the "string" part of nsAtomStringList means
for a pseudo-class.... (that this is used for "function pseudo-classes and the
string is whatever was in the parens).

Would it be worthwhile to make mLanguage in RuleProcessorData an nsString* (or
even nsAutoString*) instead of an nsAutoString and to allocate it on the first
call to GetLang()?  The null-ness (or not) of the pointer could then be used in
place of mIsLanguageValid, methinks.  It should make the common-case
RuleProcessorData struct smaller and should not really make the uncommon case
that much slower, imo... (just a suggestion; feel free to tell me I'm on crack
here).

+            result = localTrue == DashMatchCompare(*lang,
+                            nsDependentString(pseudoClass->mString), PR_FALSE);
...
+                nsDependentString langString(pseudoClass->mString);
...
+                  if (DashMatchCompare(langString,
+                                       Substring(language, begin, end-begin),
+                                       PR_FALSE)) {

One of those two DashMatchCompare calls has the string args in the wrong order.
 It would be clear which is wrong if DashMatchCompare had argument names that
made it clear which argument comes from the selector and which argument comes
from the content node or if it had a comment on the topic.  As far as I can
tell, the DashMatchCompare against the Substring() should have Substring() and
langString reversed.

If there is no content-language on the document and there is no lang attr on
anything, we will treat :lang() as a no-op with this code, no?  That is, both
a:lang(foo) and a:lang(bar) will match an anchor.  Further, both :lang(foo) and
:not(:lang(foo)) will match everything...  Is this really what we want?

> +              result = PRBool(localTrue == DashMatchCompare(value,
attr->mValue, isCaseSensitive));

Do you really need the PRBool() part of that?

Please adjust the comments in nsStyleContext.cpp that currently say:

568     // FRAMECHANGE Structs: Display, XUL, Content, UserInterface

637     // REFLOW Structs: Font, Margin, Padding, Border, List, Position, Text,
TextReset,
638     // Visibility, Quotes, Table, TableBorder

since you are making Quotes a FRAMECHANGE struct.

The rest looks good.
http://www.w3.org/TR/css3-selectors/#grammar is clear that whitespace is allowed
in functional pseudos.
> If there is no content-language on the document and there is no lang attr on
> anything, we will treat :lang() as a no-op with this code, no?  That is, both
> a:lang(foo) and a:lang(bar) will match an anchor.  Further, both :lang(foo) and
> :not(:lang(foo)) will match everything...  Is this really what we want?

I don't see what makes you think this.  I did make the change that makes
":lang()" never match, though (and thus ":not(:lang())" always matches).

I'll attach a new patch that should address bzbarsky's comments.
I didn't bother with the nsAutoString pointer -- I don't think the
RuleProcessorData is constructed enough that we have to worry about a single
nsAutoString, although I could be wrong.  Also see my previous comments.
Attachment #94955 - Attachment is obsolete: true
Comment on attachment 95149 [details] [diff] [review]
patch updated according to bzbarsky's comments

> +        result = attributeSubstring.Equals(aSelectorValue,
> +                                          nsCaseInsensitiveStringComparator());

Odd indent there (one char off).

> I don't see what makes you think this.

I had missed the |+	   result = localFalse;| line at the beginning of the
lang-matching code.. ;)

sr=bzbarsky
Attachment #95149 - Flags: superreview+
Checked in to trunk, 2002-08-14 05:34 PDT.  Thanks to all who contributed,
especially Ulrich Drepper, for all the work that's gone into this patch.

(That odd indentation was intentional, to avoid crossing the 80 character boundary.)
Status: NEW → RESOLVED
Closed: 25 years ago22 years ago
Resolution: --- → FIXED
This patch goes on top of the previous one -- it moves to bzbarsky's suggestion
in comment 128 of using an |nsAutoString*| in RuleProcessorData for better
performance in the case when there are no :lang selectors.

I just checked this patch in because tinderbox showed a roughly 1.5% page load
performance regression on two separate tinderboxes after this patch landed, and
this is the only thing I can think of that might have caused it.
I also removed the extraneous |SetLength(0)| which peterv pointed out.
Well, either the two cycles where the page load numbers were higher were a
random blip that's a bit higher than most, or that really did make a difference,
because the numbers look like they're back where they were.  I really need to
remember never to underestimate the amount of time that nsAutoString's
constructor takes!
This just adds a "delete mLanguage;" to the destructor of RuleProcessorData
Comment on attachment 95285 [details] [diff] [review]
Deallocate the string too

Yeah, thanks.  :-)

rs=dbaron
Attachment #95285 - Flags: superreview+
Comment on attachment 95285 [details] [diff] [review]
Deallocate the string too

r=me
Attachment #95285 - Flags: review+
Woo hoo!  Finally resolved after a year.  Thanks for the last changes.

Could somebody now at least comment on the patch in bug 24861?  It's one of the
few internationalization related bugs which are outstanding.  And another step
close to supporting CSS2.
I'm not sure about this:
<p lang="de">bla<span lang="en">?</span></p>

and the style:
*:lang(de){color:blue}

..should the "?" be blue or black (default)...?
It should be blue, since color inherits and you've specified no rules on the
lang="en" content to override that color.
In all the testcases, the xml:lang tests involved inheriting down from another
tag, such as a div being tagged xml:lang whatever and all b tags within it
inherit a certain style.

However, the simple case of styling (for example) all P's of xml:lang whatever
to a given style does not work.  Reopening bug, will be attaching testcase to
illustrate.
Status: RESOLVED → REOPENED
Resolution: FIXED → ---
Attached file New xml:lang testcase
In this test, all P's of lang(fr) are red.  P's tagged with only the "lang"
attribute get the style.  P's tagged with only the "xml:lang" attribute do not.


Since "lang" is deprecated in XHTML 1.1, this becomes a problem.
if you send that file as text/xml it works.
Comment on attachment 95593 [details]
New xml:lang testcase

xml:lang, and namespaces in general, are not supported in the HTML parser. 
Changing mimetype of testcase to "text/xml", which is one of the valid XML MIME
types.
Attachment #95593 - Attachment mime type: text/html → text/xml
Testcase works fine when changed to text/xml.   Marking fixed again.
Status: REOPENED → RESOLVED
Closed: 22 years ago22 years ago
Resolution: --- → FIXED
verified
realy verifying 
Status: RESOLVED → VERIFIED
*** Bug 41978 has been marked as a duplicate of this bug. ***
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: