-
-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Wait for DOMContentLoaded before checking if we have a camera in the DOM #5124
Wait for DOMContentLoaded before checking if we have a camera in the DOM #5124
Conversation
Also possibly seems worth mentioning that this auto-injection of a camera bug seems to have come up and caused the hubs team some pain, and possibly contributed to their beginning to transition away from a-frame. |
If this doesn't reproduce consistently there might be something we are misunderstanding or a bug somewhere. As per MDN |
Not sure what you're referring to; this bug does reproduce consistently, and I have isolated with breakpoints in aframe code systems initializing before the DOM has finished loading. |
See here for reproducible: https://glitch.com/edit/#!/aframe-camera-glitch?path=index.html%3A31%3A62 |
This seems to be the mistake; at times, this seems to not be true. Here's I've shown one example that forces it to not be true in a very simple scene, but I've also observed it in two other complex projects (one where I solved it by declaring the camera at the top of my HTML instead of the bottom), and it is also consistent with a comment made by the Hubs team who seem to have encountered this behavior. As far as I can tell, there is no code guaranteeing that A-Frame wait until |
We need to understand why in some cases systems / components initialize before |
A good experiment could be moving the setupInitialCamera to an update method of the system. See if that makes any difference. |
I just tested. The bug also appears in firefox, and the fix also works in firefox. Can you explain why the systems shouldn't initialize before DOMContentLoaded without this patch? What in the code is supposed to make A-Frame wait to initialize its systems until the dom content is loaded? While we ended up going with this listener as per my original suggestions, Vincent suggested trying several other options before finally agreeing this seemed best. As per slack, here are several of his comments from this discussion:
?
I tried all (I think three?) of Vincent's proposed alternate solutions, none fixed the issue and/or did not produce other bugs. If there is no existing guarantee on A-Frame to wait for the DOM to load, then we should probably add one. We are seeing that in the wild, it does not always wait for the DOM to load, it just happens to incidentally not fire too early most of the time. Is there any argument to not make this explicit? Are you wondering whether we should also add this condition elsewhere? I guess my thing is, asking kind of the same thing in a different way: Why do A-Frame's systems not just try to init before the body has even begun to be parsed? What triggers it? It goes back to some attachedCallback iirc, but it wasn't clear to me what fires that during my brief digging. |
I can try this tomorrow, but honestly if it works I would be more worried that this is a hack that works because of an interruption, more akin to relying on something like |
There is no guarantee that DOMContentLoaded is executed before initSystems, from my understanding the scene initialization starts with the first attachCallback of a-scene node while the DOM is constructed and the whole page may not have been parsed yet, that's also why we have all the loaded logic in a-node to wait for DOM nodes to be initialized. |
Moving Lines 42 to 43 in 30c3faf
|
We have another place in the code where we encounter the issue, see 7f017a3 We also have the issue with component init in general where a querySelector in init may randomly fail to find an element in the page. I saw this several times in the past myself and on some glitch examples that other persons wrote when I was helping with networked-aframe on slack. If you want to use querySelector in init, you really need to ensure you do that when document.readState !== "loading" or in DOMContentLoaded listener or when the scene is loaded. |
We can try to defer attachedCallback logic until DOMContentLoaded fires? should solve the issue for systems and components. Not sure about side effects. |
Is there a clear description of exactly how A-Frame bootstraps and how order of execution works in A-Frame? When I was just attempting to read the source, like I said, I got blocked at trying to figure out when the attachedCallback fires. What I would be getting at is: is it possible that solving this for system init solves this entirely? Do we know that we need to fix this for components as well? I don't know of any reported bugs related to that, but without knowing exactly the order of execution when A-Frame is starting up, I can't really speak to it either way, I just know that waiting for the DOM to init before A-Frame decides if it should inject cameras or lights makes sense. Also: would be nice if there were a flag to turn that functionality off, if nothing else. Honestly, imo, we should actually remove the auto-injection functionality, and just include a camera and a light in the hello world demos. I think that would be much cleaner and more sane, and is a bit over-helpful and adding unnecessary complexity in A-Frame. A-Frame makes adding those two things easy enough with primitives as-is, auto-injecting just makes it a bit more confusing and mysterious and adds potential for bugs like this. (But that's probably a discussion for another time.) |
@dmarcos I'll do more tests later with @kylebakerio No I don't think there is a open issue about the component init where querySelector can randomly fail. I didn't considered it as an aframe issue but more like an issue in my own code until now so I never reported it or worked on fixing it in the aframe code. |
The |
…ed camera with querySelectorAll
de2e908
to
4de3e6f
Compare
4de3e6f
to
4aef7d8
Compare
Components initialization is done with |
src/core/a-entity.js
Outdated
this.load(); | ||
return; | ||
} | ||
utils.waitForDOMContentLoaded().then(function () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about just deferring in line 70 do:
utils.waitForDOMContentLoaded.then(this.attachedCallback.bind(this));
it should solve for both scene and entities I believe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even better would be to do it in the attachedCallback for a-node.js
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually tried to put that in a-node attachedCallback first, but some tests failed.
But actually I don't think it makes sense, see a-scene attachedCallback is calling a-entity attachedCallback which is calling a-node attachedCallback. Putting a Promise in a-node attachedCallback means the caller a-entity attachedCallback may not have some attributes set yet like this.sceneEl. The promise needs to be at the top of the calling chain, so this probably make more sense to do patch attachedCallback to do thhe waitForDOMContentLoaded in registerElement after we do the wrapMethods.
This is what I did in the latest commit, I patched the attachedCallback for any registered elements that uses AEntity prototype (so a-scene, a-entity and all primitives a-camera a-gltf-model etc).
I didn't do it for registered elements that uses the ANode prototype (a-assets, a-mixin), some a-assets tests are failing if I do that, but there is not really an issue of system or component init in those anyway.
…lement use AEntity prototype
Not sure if introducing an exception for entities is a good idea. It breaks consistency. We can try to defer all component life cycle methods here: https://github.com/aframevr/aframe/blob/master/src/core/a-register-element.js#L159 That way the model will be simple to think about with no ad-hoc exceptions. |
The line you gave here will wrap for the given prototype the four methods aframe/src/core/a-register-element.js Lines 114 to 115 in 7fb1d75
So doing the change here to call waitForDOMContentLoaded will add waitForDOMContentLoaded twice, once for ANode.attachedCallback and second time for AEntity.attachedCallback . Like I said wrapping in a Promise the method on anything other than the method in the top calling chain is not good. And using the waitForDOMContentLoaded for ANode make some assets tests fail. Also it doesn't make sense to me to wait for DOMContentLoaded on attributeChangedCallback or detachedCallback for example, we could do it but it's really unnecessary. I tried to patch on createdCallback instead of attachedCallback , some tests fail.
|
not sure if I made some mistake while quickly trying to troubleshoot, but figured I'd mention this:
I did not triple-check my experience, so it's possible I made some error, or that something went wrong with the build tools, etc. |
I just verified right now with the latest changes in the branch, it's working for me |
That does seem to work for me--must have been a build tool error, perhaps I should wipe my old node_modules and do a clean npm install. |
I opened this #5136 that I think will help us address the root cause of the issue and also modernizes the code base. I would love to add a test that reproduces the camera issue to make sure it gets fixed. Do you think you can help with that? Thanks so much for the effort and patience. |
Thanks for reviving the custom elements v1 work. I'll review your PR later this weekend. |
I think we can close this in favor of #5136. Hopefully the entity interdependency issues are gone for good now |
I just verify again on the glitch example and latest aframe master build with some logs in a-node and a-entity, the |
Description:
Initially discussed on slack https://aframevr.slack.com/archives/C0FAACNA0/p1663208880632399 and https://aframevr.slack.com/archives/C0GG937RN/p1663912806511639
@kylebakerio reported to have an issue in several projects where he had 1/5 chance that the camera used was not the one he defined but the auto injected one.
This can happen in projects with lots of entities defined in the html and there is more chance to reproduce the issue when the camera is defined at the end of the page.
He did a glitch to reproduce the issue every time by triggering an interruption with a script tag just before the camera. https://glitch.com/edit/#!/aframe-camera-glitch?path=index.html%3A31%3A62
He did the initial fix and I made some changes to it.
So what is the issue? The camera system is searching for camera in the DOM with
aframe/src/systems/camera.js
Line 46 in d780484
and if none was found, it creates a default camera.
The issue here with
querySelectorAll
is that it doesn't find anything because the DOM content is not fully loaded.Changes proposed: