Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upFix iframe.onload being fired twice on about:blank. #16091
Conversation
highfive
commented
Mar 23, 2017
highfive
commented
Mar 23, 2017
|
Looks good, although I did find another edge case |
| } | ||
| }); | ||
| }, "IFrameSingleOnLoad"); | ||
| document.getElementById('blank_iframe').contentWindow.location.reload(); |
This comment has been minimized.
This comment has been minimized.
cbrewster
Mar 23, 2017
Member
I'm not sure if this causes the same behavior as doing an inline onload on the iframe. I wonder if we could just create and add an iframe via javascript. (I would test this first on the master branch and see if that yields the onload event being fired twice).
This comment has been minimized.
This comment has been minimized.
gpoesia
Mar 23, 2017
Author
Contributor
I had tested it on master and it did trigger two load events. However, I agree it's better to add a new iframe, since it's simpler and doesn't raise doubts. Will do!
| function iframeOnLoad(t) { | ||
| iframe_loads++; | ||
|
|
||
| setTimeout(function() { |
This comment has been minimized.
This comment has been minimized.
cbrewster
Mar 23, 2017
Member
Unfortunately, I am not entirely sure if there is a better option than doing a setTimeout.
| Some(iframe) => { | ||
| // If the iframe has no "src" attribute, a load event will have already been fired | ||
| // when binding the iframe to the tree. | ||
| if iframe.upcast::<Element>().has_attribute(&local_name!("src")) { |
This comment has been minimized.
This comment has been minimized.
cbrewster
Mar 23, 2017
Member
I have been thinking about this and testing a bit more... I don't know if this will be a sufficient solution. Consider the following case:
- You have the
iframewith nosrc - The load event steps for the initial
about:blankare fired async as per the spec - In a script you set the
srcof theiframeto some URL - The load event steps are now processed and a
loadevent is fired for the initialabout:blank
A possible fix is to cancel the load event steps runnable when the src is set.
I have been working on about:blank fixes (#14764) and here is a commit that does this 46220d4
I would be ok if you wanted to implement something like that here since I don't know how soon I will have enough time to finish up the rest of that PR
This comment has been minimized.
This comment has been minimized.
gpoesia
Mar 24, 2017
•
Author
Contributor
Hmm, right, now I see that case. I couldn't reproduce it, though (I tried this:
<html>
<body>
<iframe id="iframe" onload="alert('onload');"></iframe>
<script type="text/javascript">
document.getElementById("iframe").src = "a.html";
</script>
</body>
</html>)
Does the spec say anything on what should happen to the first load event in the case of changing the src? I looked at your commit, but if I got it correctly it will only prevent the load steps from being loaded as long as the IFrameLoadEventSteps runnable checks the cancel flag after it is set, right? I'm imagining a case in which the program is exactly before this.iframe_load_event_steps(self.pipeline_id);, but after the if condition has been evaluated, and at this point the src changes and the other thread tries to cancel the event. The check makes it even harder for both events to run, but does it really guarantee it?
If you think it helps I can merge that commit into this PR. What do you think?
This comment has been minimized.
This comment has been minimized.
cbrewster
Mar 25, 2017
Member
So my solution may not be the best way, the spec basically says to delay the load event if a new navigation is triggered: https://html.spec.whatwg.org/multipage/embedded-content.html#iframe-load-event-steps (See the first NOTE).
The code is not running multithreaded here, we just have asynchronous tasks. So when we queue a task to do the iframe load event steps, this will allow other things to potentially be processed first (i.e. src changing before the task is handled). We currently have a LoadBlocker for Documents that handle this. Maybe we should do something similar with iframes.
|
r? @cbrewster |
|
@cbrewster Oh, I see, thanks for the clarifications and patience! For some reason I thought we had multiple threads involved. I've merged your solution into this commit then, and also modified the test to create the iframe in JavaScript instead. Would you take another look? |
|
Looks like there were some unintended changes in the |
| @@ -54200,6 +54200,16 @@ | |||
| {} | |||
| ] | |||
| ], | |||
| "html/webappapis/scripting/events/iframe-onload-frame.html": [ | |||
This comment has been minimized.
This comment has been minimized.
| {} | ||
| ] | ||
| ], | ||
| "html/webappapis/scripting/events/iframe-onload.html2": [ |
This comment has been minimized.
This comment has been minimized.
| @@ -179815,6 +179831,18 @@ | |||
| "491fd73a4ec8812cb8dc1ee01e34a7ff2a07ffb3", | |||
| "testharness" | |||
| ], | |||
| "html/webappapis/scripting/events/iframe-onload-frame.html": [ | |||
This comment has been minimized.
This comment has been minimized.
| "d5e1649c88102f27da5fe1ac16715cfee7f70f84", | ||
| "testharness" | ||
| ], | ||
| "html/webappapis/scripting/events/iframe-onload.html2": [ |
This comment has been minimized.
This comment has been minimized.
| @@ -7023,6 +7023,11 @@ | |||
| {} | |||
| ] | |||
| ], | |||
| "css/border_collapse_simple_ref.html2": [ | |||
This comment has been minimized.
This comment has been minimized.
| @@ -20385,6 +20405,10 @@ | |||
| "00c9c85a861dacec7b34f019a43e347c11098ab0", | |||
| "support" | |||
| ], | |||
| "css/border_collapse_simple_ref.html2": [ | |||
This comment has been minimized.
This comment has been minimized.
| @@ -23413,6 +23437,10 @@ | |||
| "0617c8de92ec1a283ac429dbbb073805e6bad28c", | |||
| "reftest" | |||
| ], | |||
| "css/table_margin_a.html.bak": [ | |||
This comment has been minimized.
This comment has been minimized.
| @@ -23425,6 +23453,10 @@ | |||
| "d2ccfd91d67914e7e6653e1ce86deeebf5cd5977", | |||
| "support" | |||
| ], | |||
| "css/table_margin_ref.html.bak": [ | |||
This comment has been minimized.
This comment has been minimized.
| @@ -25737,6 +25769,10 @@ | |||
| "333a80e3d4aec4a8be5566605fae5675a821085b", | |||
| "support" | |||
| ], | |||
| "mozilla/referrer-policy/generic/subresource/mozresource.pyc": [ | |||
This comment has been minimized.
This comment has been minimized.
| function iframeOnLoad(t) { | ||
| iframe_loads++; | ||
|
|
||
| setTimeout(function() { |
This comment has been minimized.
This comment has been minimized.
cbrewster
Mar 26, 2017
Member
does this test fail on master? I think
t.step(function() {
assert_true(iframe_loads === 1, "iframe.onload should be called exactly once.");
});
should be above the timeout
|
I have been thinking about this more, there are still edge cases where using the |
|
@gpoesia sorry about the back and forth on this one... I believe you can remove my changes and this should be good to go. I forgot that when firing a load event, we check to see if the pipeline of the child document that was loaded matches the latest pipeline of the iframe. This already handles the case I mentioned above. |
|
@cbrewster no problem! I've fixed the unwanted changes in the MANIFEST files, and reverted the cancel flag part as well. About the test, I've checked again and it does fail on master. The idea was waiting to call |
|
@gpoesia looks good! Thanks! @bors-servo r+ |
|
|
|
@cbrewster Do you have time to continue reviewing this? |
|
I do, but I'm not entirely sure what the correct, spec-compliant approach is for fixing this issue. It would be good to have someone else look into it. |
|
r? @asajeffrey |
|
Back from vacation, I'll have a look over this. |
|
Some IRC chat with @jdm: http://logs.glob.uno/?c=mozilla%23servo&s=7+Sep+2017&e=7+Sep+2017#c746388 TL;DR we shouldn't be navigating to the initial about:blank, we should just have it magically appear. The problem line is |
|
Hmm, we're already navigating the newly created browsing context when we process the iframe attributes: Can we remove the navigation when we create the browsing context? If we remove that navigation, there may be some extra code needed to handle the first-load-with-no-src case servo/components/script/dom/htmliframeelement.rs Lines 235 to 238 in e7f4502 |
|
Doing a bit more digging... If we added a field to |
|
And some more digging... if we add another option to fn dispatch_load(&self) {
let document = self.addr.root();
if document.browsing_context().is_none() || document.source == DocumentSource::InitialAboutBlank {
return;
}
...This way the constellation won't get asked to forward the load event on to the parent. @gpoesia I realize this is a very different approach from the one you were using before, does this sound sensible to you? |
|
@gpoesia thoughts? |
|
@gpoesia ping? |
|
Thanks for looking into it, @asajeffrey! Basically, the reflow that was caused by the second load event was actually important. Without it, the compositor thread wouldn't ever paint the iframe (and tests would hang, as a consequence). I didn't figure out why that was the case, and it's even possible it isn't true anymore. I really like the solution of simply adding another |
|
@asajeffrey this is what would happen when the load event was suppressed before the second reflow: #13149 |
|
Hmm, so we need to find a way to trigger a reflow without a second load event. BTW, heads up that #18587 might make a difference here! |
|
@asajeffrey I've updated the code, is that more or less what you had in mind? Unfortunately, the From what I could gather, we need a reflow that happens after the compositor has been notified of the iframe's existence and latest pipeline ID. Since the data flow is always I guess receiving a So: 1- Does my code implement now what you had in mind? If it doesn't, I can fix it and try to make it work. Another possible solution I can try is to simply store a flag in |
|
|
|
Yes, this is the sort of thing I had in mind, yay! There are a few nits, like it would be nice to use an enum rather than a bool for whether a document is the initial about:blank, but we can sort those out once we've worked out how to get the tests to pass. It sounds like you have a point, we need a way for the constellation to know that the iframe has finished loading. Is this just for One thing which has occurred to me is that |
|
As far as I could tell, this is all to satisfy I see two ways of going about the Constellation issue: we can create a "special path" for the iframe load to reach the constellation in that case, or we can just extend the path with more flags/enums so that the event stops earlier than usual. I think this latter one is less special-cased than creating a new path (e.g. a new message type for the constellation). I'm not sure about where the reflow would fit, though. Am I right in thinking that only the script thread has enough information to issue a reflow? @asajeffrey Do you think adding the flag in |
|
@gpoesia which flag on |
|
@gpoesia what would be the best way to move forward on this? |
|
@gpoesia Are you still working on this? |
|
This looks dormant, feel free to reopen it! |
gpoesia commentedMar 23, 2017
•
edited by larsbergstrom
Supress the load event in the script thread when the iframe doesn't have a src attribute.
In that case, a load event is already fired when binding the iframe to the tree.
./mach build -ddoes not report any errors./mach test-tidydoes not report any errorsThese changes fix #15727 (github issue number if applicable).
There are tests for these changes OR
This change is