Closed Bug 858850 Opened 11 years ago Closed 11 years ago

jQuery's capability detection fails in non-HTML documents when innerHTML is on Element

Categories

(Tech Evangelism Graveyard :: English US, defect)

x86_64
Windows 7
defect
Not set
normal

Tracking

(Not tracked)

RESOLVED FIXED

People

(Reporter: postbox, Unassigned)

References

Details

Attachments

(3 files)

User Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:20.0) Gecko/20100101 Firefox/20.0
Build ID: 20130326150557

Steps to reproduce:

My site transforms XML (XHTML) pages via client side XSLT to XHTML Strict.
Up to FF version 19 to latest stable jquery 1.9.1 worked with the resulting XHTML page (DOM in XML mode), although common jquery methods like using innerHtml must be  avoided.

The startup code of jquery creates some test nodes with document.createElement. Although this isn't suitable for XML/XHTML FF created full-fledged HTML nodes until now.

Now I updated to FF 20.


Actual results:

jquery can't initialize any more because the created test nodes are now missing essential HTML attributes like style. Without jquery the rest of client side JS is broken on the site.

My checks show that nodes created with document.createElementNS are unaffected of this change.


Expected results:

Ok, you can say it's jquery's fault to use createNode instead of createNodeNS in XML/XHTML but until now it wasn't a problem to created HTML side nodes inside a XHTML document. The nodes were not shown but they had all necessary attributes to pass jquery's checks so that I could use jquery also for XHTML manipulation.

Now it's a breaking change. Is there any reason why HTML nodes aren't created anymore?

Maybe I can express a long time request: Why don't you create nodes via document.createNode in XML mode in the default namespace (taken from xmlns attribute) to deliver valid XHTML nodes? This would ease life when working with JS libs in XML mode. None of them seems to use createElementNS!
Extract in one folder and load test.xhtml into FF 20
Load my attached test case and open Firebug console and insert:
>> a=document.createElement("div")
>> b=document.createElementNS("","div")
>> c=document.createElementNS("http://www.w3.org/1999/xhtml", "div")

Check a, b, c for attributes. Only c is full-fledged, a and b are missing for example style.
Attachment #734164 - Attachment mime type: application/octet-stream → application/zip
Andreas, I just tried your testcase in Firefox 19:

1)  Loaded the test.xhtml
2)  Observed that it has "After Transform" in it.
3)  Tried document.createElement("div")

I got an Element instance, not an HTMLDivElement.  As in, the behavior doesn't seem to have changed from 19 to 20 (which is good, because I didn't think we had changed it).

If you have a testcase that _does_ show a behavior difference between 19 and 20, would you mind posting it?

> Why don't you create nodes via document.createNode in XML mode in the default namespace

At least because the "default namespace" as defined above is not document-invariant...

Note, though, bug 799937.  Lots of active spec discussion about how createElement should behave.
Flags: needinfo?(postbox)
Attached file Extended Tes Case
Flags: needinfo?(postbox)
Attached file Extended Test Case
I made an extended test case.

Test 1: Divs created with createElement and createElementNS before and after transform. Div a1 created before transform with createElement is of type "HTMLDivElement". Div b1 created after transform with createElement is of type "Element" and has for example no style property. The behaviour is the same on FF 19.0.2 and 20.0, so there seems to be no change. However I consider it as a bug: I transform from XHTML to XHTML, so why do I get a HTMLDivElement via createElement before transform and an Element afterwards?

Test 2: Simply try to load actual stable jquery 1.9.1. It fails on 20.0 and works on 19.0.2. See line 1330 in jquery-1.9.1.js. Some lines before you can see how the failing anchor element was created: with the help of innerHtml on a created div. As far as I can judge it there must be a change at this point because in 19.0.2 you get a full fledged anchor node whereas 20.0 misses the style property. What puzzles me too is that I thought there is no innerHtml parsing on the DOM available in XML mode?

Interestingly I create a div with createElement in 19.0.2 and append it via jquery to the body. In test 1 you can see that a similar node (b1) is of type "Element", also when inserted in the tree (appendChild on body). But inserted with jquery's append on a jquery-extended body I get a HTMLDivElement in the tree!!!

Ok, but that's a side node. The main fault is that jquery does not load any more on 20.0.

PS: Bugzillas detection of application/zip attachments is buggy. In automatic mode it always sets application/octet-stream, the drop down has no application/zip option and I'm to lazy to enter it manually. Sorry that you felt the need to change it.
Sorry, one false statement:
"Interestingly I create a div with createElement in 19.0.2 and append it via jquery to the body. In test 1 you can see that a similar node (b1) is of type "Element", also when inserted in the tree (appendChild on body). But inserted with jquery's append on a jquery-extended body I get a HTMLDivElement in the tree!!!"

That's not true, I accidentally created the div with createElementNS. But the jquery problem remains untouched!
Summary: Essential attributes missing on created HTML DOM nodes in a XHTML document after update to FF 20 → Essential attributes/properties missing on created HTML DOM nodes in a XHTML document after update to FF 20
Summary: Essential attributes/properties missing on created HTML DOM nodes in a XHTML document after update to FF 20 → Essential attributes/properties missing on JS created HTML DOM nodes in a XHTML document after update to FF 20
Attachment #734310 - Attachment mime type: application/octet-stream → application/zip
Attachment #734311 - Attachment mime type: application/octet-stream → application/zip
> I transform from XHTML to XHTML, so why do I get a HTMLDivElement via createElement
> before transform and an Element afterwards?

Because what createElement does depends on the _document_.  And while parsing an application/xhtml+xml response produces an HTMLDocument, the output of XSLT seems to always be an XMLDocument.  Note that if you just renamed your .xhtml files to be .xml you'd get the same pre-transform behavior there...

Thanks for the updated testcase with jQuery.  What that code is doing is this (stripping out the irrelevant bits):

  var div = document.createElement("div");
  div.innerHTML = "  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
  // Support tests won't run in some limited or non-browser environments
  a = div.getElementsByTagName("a")[ 0 ];
  if (!a) {
    return {};
  }  
  a.style.cssText = "top:1px;float:left;opacity:.5";

In Gecko 19, the innerHTML property lived on HTMLElement.  So the set of .innerHTML above did nothing (well, set a random property to a string on the div, but didn't change the DOM), "a" came out undefined, and the "limited environment" early return was taken.

In Gecko 20, innerHTML lives on Element (see bug 811701).  So the innerHTML set modifies the DOM, "a" comes out as an element (albeit a non-HTML one), and things break.

This looks like a jQuery bug to me, honestly; you can patch it in your local version by testing for a.style in that "limited environments" test...
Assignee: nobody → english-us
Blocks: 811701
Component: XML → English US
Product: Core → Tech Evangelism
Summary: Essential attributes/properties missing on JS created HTML DOM nodes in a XHTML document after update to FF 20 → jQuery's capability detection fails in non-HTML documents when innerHTML is on Element
Version: 20 Branch → Trunk
I filed http://bugs.jquery.com/ticket/13754

Just to be clear, nothing changed in what sort of elements are created here.  What changed is whether "a" is created at all.  ;)
Ok, as I said: It may be jquery's fault. It's a matter of fact that jquery doesn't deal well with XML (see the lack of using createElementNS and using innerHtml instead). But up to Gecko 19 it worked "good enough" even that I don't realized using a "limited environment" with FF. ;)

But as you confirmed: A change in FF breaks existing code. I really wonder that nobody else complains their broken scripts when using client side XSLT to XML. And I have to ask: Why do you even support innerHtml/outerHtml on a XML DOM now? IMHO it is the wrong way, much worse than let createElement making nodes in a "default" namespace whatever that may be. So it's ok moving the bug to "Tech Evangelism". It's kind of fundamental behaviour.

And also a question to test case 1 arises: Why do I search the web for the best MIME types to use ending in application/xml and Gecko simply looks at the file ending to detect if it is XHTML??? That's not funny. But why do I complain, looking at the mess of HTML5 marks me as a dying breed. Am I the only one favouring well formed documents over tag soup? :-(
> I really wonder that nobody else complains their broken scripts when using client side
> XSLT to XML.

To a first approximation, no one does client-side XSLT....

> And I have to ask: Why do you even support innerHtml/outerHtml on a XML DOM now?

Basically because the specs have changed to define it so, and there didn't seem to be any obvious reason not to.  See http://domparsing.spec.whatwg.org/#extensions-to-the-element-interface

> and Gecko simply looks at the file ending to detect if it is XHTML???

You mean for local files?  What else would it do, exactly?

One thing I did notice looking at your transform is that you use <xsl:output method="xml">.  Why not method="html" if you want to produce HTML?  It sure would solve the problems you're having...

> Am I the only one favouring well formed documents over tag soup?

Pretty close, unfortunately.
> To a first approximation, no one does client-side XSLT....

Yes, they built browser switches instead. With client side XSLT you always got standards mode in browsers, even the stupid IE6 behaves less badly.

> Basically because the specs have changed to define it so,
> and there didn't seem to be any obvious reason not to.

F**** WHATWG. One obvious reason not to do is the name "innerHtml". I don't want HTML.

> You mean for local files?  What else would it do, exactly?

Arg, I really should not report bugs at 4:00 am.

> One thing I did notice looking at your transform is that you use
> <xsl:output method="xml">.  Why not method="html" if you want to
> produce HTML?

Because I'm not producing HTML? I produce XHTML 1.0 Strict, RDF, SVG and all sorts of other fine XML languages. And because I (don't want to) known nothing from this stupid HTML5 I expect to get a W3C conform DOM. But ok, I ask you: Works your innerHtml with XML namespaces and creates nodes with createElementNS? I suppose not.
> I don't want HTML.

You should read the spec instead of cursing.  The parsing in the setter uses an XML parser if you're in an XML document.... which it already did for an HTML-namespaced element in such a document.

The naming is unfortunate, and other names were considered, but in the end the "it already works on (X)HTML elements everywhere, using an XML parser, and that behavior makes sense for all elements" won out.

> Because I'm not producing HTML?

OK.  Note that you're not producing XHTML either.  You're producing a DOM directly, whereas XHTML is a syntax specification.

> Works your innerHtml with XML namespaces and creates nodes with createElementNS?

Yes, in the sense that it just uses an XML parser, with the in-scope namespaces being whatever is in scope at the element the setter is called on.  As you would know had you tried it.

So if you have a <foo xmlns="http://www.w3.org/1999/xhtml"> in an XML document context and you set its innerHTML to the string:

  <bar/><baz xmlns="something"/><xyz:quux xmlns:xyz="somethingelse"/>

it will do what you would expect it to do in an XML document.
Status: UNCONFIRMED → RESOLVED
Closed: 11 years ago
Resolution: --- → FIXED
Oh, and this is now fixed on the jQuery end.
Status: RESOLVED → UNCONFIRMED
Resolution: FIXED → ---
Status: UNCONFIRMED → RESOLVED
Closed: 11 years ago11 years ago
Resolution: --- → FIXED
Thank you, they fixed it in jquery really fast. Tried the latest GIT version and my site worked again.

> You should read the spec instead of cursing.

Mhm, what spec? In the past innerHTML did not work reliably on a XML DOM, furthermore it wasn't even defined for any DOM. I do not expect to get HTML5 behaviour when a XHTML 1 doctype is given explicitly.

> Note that you're not producing XHTML either.  You're producing
> a DOM directly, whereas XHTML is a syntax specification.

No, no I do produce XHTML via XSLT. Isn't it XHTML anymore when containing SVG, RDF? I almost never create nodes in JavaScript and in the few cases I use createNodeNS instead of innerHTML or relying on jquery's append method.

> As you would know had you tried it.

I tried it now. Surprisingly it really worked! :p
It uses the "default" namespace of the node whose innerHTML is set or takes the explicitly given ones from the string to parse. It seems that it does not inherit namespace prefixes, but that's not a problem.

So thank you for the help and clarification.
> Mhm, what spec? 

The one I linked to in comment 11.

> I do not expect to get HTML5 behaviour when a XHTML 1 doctype is given explicitly.

It doesn't work that way.  For various reasons, browsers ship only one DOM implementation: the most recent one.

> No, no I do produce XHTML via XSLT.

No, you produce a DOM tree, containing nodes in various namespaces.

> I almost never create nodes in JavaScript

Sure, but XSLT creates nodes directly.  It doesn't produce text that then gets parsed (unless you tell it to explicitly, of course, which you're not)....
Oh, and you're very welcome!
>> Mhm, what spec? 
> The one I linked to in comment 11.

Just a rhetorical question. :)

> No, you produce a DOM tree, containing nodes in various
> namespaces.

In general or just the way Gecko does it? Since XML is polyglot it's difficult to define the output. But if the root starts with <html> why not consider it as XHMTL with additions? Also MIME type "application/xhtml+xml" suggests that.

> Sure, but XSLT creates nodes directly.  It doesn't produce text
> that then gets parsed (unless you tell it to explicitly, of
> course, which you're not)....

I checked different outputs methods via xsl:output. In fact when using "html" I will get a complete HTML node with createNode. But it's HTML and not a well formed XML document.

So I ask how to do your parenthesised suggestion to achieve the effect that I will get transformed XHTML but parsed like a plain XHTML file, where we proved that createElement results in complete nodes? Output-method "text" does not work and "xml" always results in "application/xml", whereas the plain XHTML file has "application/xhtml+xml". Setting the media type with xsl:output does not matter, it changes the type on the page info box but the behaviour persists.

So what's the correct way to do XHTML to XHTML transformations and get the same behaviour like with a plain XHTML file? Of course I could do it on the server but that' not the question. :-)
> In general or just the way Gecko does it?

The latter for sure; in general the XSLT spec allows several different operating modes that are supposed to end up acting the same.

> So I ask how to do your parenthesised suggestion to achieve the effect that I will get
> transformed XHTML but parsed like a plain XHTML file

It's possible that using the "text" output method with an application/xhtml+xml media-type will do it per spec... but I'm not sure we support that, honestly.

> Setting the media type with xsl:output does not matter, it changes the type on the page
> info box but the behaviour persists.

That sounds like a bug worth filing on the XSLT implementation.  Though I should warn you it's not exactly actively maintained.

> So what's the correct way to do XHTML to XHTML transformations

That actually works in browsers?  Not sure there is one...
that .xhtml header looks suspiciously broken/wrong. it's filename is XHTML, but it's header is XML. are you sure that's what you wanted? I suggest you pick up the books:
- XML Schema by O'reilly
- Inside XML DTD's (out of print, still available used, valuable resource)
- Mastering XML (or some more recent, comprehensive book)

you can turn XML+XSLT into XHTML or HTML5. I have done that at http://jesusnjim.com/common/bogushouses.xml
you are welcome to view source and learn from what I did. it generates HTML5 which is supposed to be harder to do.

I should think you could generate your XHTML using CDATA-escaped <script>. even if you do generate a new page with XHTML in script, you have the problem of how you plan on displaying it.  are you going to use popups? with XML you don't have to deal with that. the browser doesn't deal with it as a web page.

also, IE doesn't like XHTML and doesn't know what to do with it.  and, with XHTML your apache server (such as with .htaccess) must be configured with appropriate commands to handle outputting XHTML as XHTML with appropriate mime types, and not as the default html!
http://protempore.net/~calvins/howto/xhtml-apache/
What do you mean with header? Do you complain about the "<?xml ?>" at the beginning? Since XHTML is XML, there's nothing wrong with a XML prologue. No browser breaks on that.

I think you missed my points. I do not create my page via JS, I do use XSLT. But even the generated XML DOM isn't a problem for JS in all browsers, and the fixed jquery behaves well, too. In fact using XML always avoided quirks mode on stupid IE 6, what made things a lot easier.
Product: Tech Evangelism → Tech Evangelism Graveyard
You need to log in before you can comment on or make changes to this bug.