layout: Produce an empty display list when the Document element is removed#44133
Conversation
|
@mukilan I think this fixes iframe content not being removed! |
|
@rovertrack A couple things:
|
Thanks @mrobinson, will add tests for this fix |
|
🔨 Triggering try run (#24301470301) for Linux (WPT) |
|
Test results for linux-wpt from try job (#24301470301): Flaky unexpected result (35)
Stable unexpected results that are known to be intermittent (22)
Stable unexpected results (2)
|
|
|
| Some(element) => element, | ||
| None => return, | ||
| None => { | ||
| // Trigger update if the document element was removed. | ||
| self.add_restyle_reason(RestyleReason::DOMChanged); | ||
| return; | ||
| }, |
There was a problem hiding this comment.
It looks like this will constantly add restyles if there is no document, but we probably only want to do this once when the document is removed.
There was a problem hiding this comment.
Added root_removal_noted flag for better tracking and to make sure this is only done once!
| // If we previously generated a display list, clear cached trees and | ||
| // send an empty transaction to clear the stale content. | ||
| if self.have_ever_generated_display_list.get() { | ||
| self.box_tree.borrow_mut().take(); | ||
| self.fragment_tree.borrow_mut().take(); | ||
| self.stacking_context_tree.borrow_mut().take(); | ||
| self.paint_api | ||
| .send_initial_transaction(self.webview_id, self.id.into()); | ||
| self.have_ever_generated_display_list.set(false); | ||
| return Some(ReflowResult { | ||
| reflow_phases_run: ReflowPhasesRun::BuiltDisplayList, | ||
| ..Default::default() | ||
| }); | ||
| } |
There was a problem hiding this comment.
We likely only want to do this once when the document becomes empty. We should probably track whether the last display we sent was empty and also move this to a helper method called send_empty_display_list. Additionally, I don't think we want to reuse send_initial_transaction here as that has a special behavior.
There was a problem hiding this comment.
in the latest changes
- Added a last_display_list_was_empty flag for tracking
- Implemented the send_empty_display_list helper method
fc7d6a0 to
1a3df8d
Compare
|
@mrobinson all the suggested improvements have been made test added |
| self.last_display_list_was_empty.set(false); | ||
|
|
There was a problem hiding this comment.
Let's set this below where we set the other LayoutImpl flags.
There was a problem hiding this comment.
Done in latest changes.
| pending_scroll_events: Default::default(), | ||
| rendering_update_reasons: Default::default(), | ||
| waiting_on_canvas_image_updates: Cell::new(false), | ||
| root_removal_noted: Cell::new(false), |
There was a problem hiding this comment.
I think this needs to be true as there we don't need to send an empty display list when the page is first created.
There was a problem hiding this comment.
Done in latest changes.
| let document_element = match self.GetDocumentElement() { | ||
| Some(element) => element, | ||
| None => return, | ||
| Some(element) => { | ||
| self.root_removal_noted.set(false); | ||
| element | ||
| }, | ||
| None => { | ||
| // Trigger update if the document element was removed. | ||
| if !self.root_removal_noted.get() { | ||
| self.add_restyle_reason(RestyleReason::DOMChanged); | ||
| self.root_removal_noted.set(true); | ||
| } | ||
| return; | ||
| }, |
There was a problem hiding this comment.
This can just be:
let Some(document_element) = self.GetDocumentElement() else {
// Trigger update if the document element was removed.
if !self.root_removal_noted.get() {
self.add_restyle_reason(RestyleReason::DOMChanged);
self.root_removal_noted.set(true);
}
return;
};
// This ensures that if the document element is removed in the future, it
// will trigger a new empty display list.
self.root_removal_noted(false);There was a problem hiding this comment.
Done in latest changes.
| /// Clear all cached layout trees | ||
| /// Send empty display list to inform the compositor to clear its display list. | ||
| fn send_empty_display_list(&self) -> Option<ReflowResult> { |
There was a problem hiding this comment.
| /// Clear all cached layout trees | |
| /// Send empty display list to inform the compositor to clear its display list. | |
| fn send_empty_display_list(&self) -> Option<ReflowResult> { | |
| /// Clear all cached layout trees and send an empty display list to paint. | |
| fn clear_layout_trees_and_send_empty_display_list(&self) -> Option<ReflowResult> { |
| let paint_info = self | ||
| .stacking_context_tree | ||
| .borrow() | ||
| .as_ref() | ||
| .map(|tree| tree.paint_info.clone())?; | ||
| self.box_tree.borrow_mut().take(); | ||
| self.fragment_tree.borrow_mut().take(); | ||
| self.stacking_context_tree.borrow_mut().take(); | ||
| let mut builder = webrender_api::DisplayListBuilder::new(paint_info.pipeline_id); | ||
| builder.begin(); | ||
| let (_, empty_display_list) = builder.end(); | ||
| self.paint_api | ||
| .send_display_list(self.webview_id, &paint_info, empty_display_list); | ||
| self.last_display_list_was_empty.set(true); | ||
| Some(ReflowResult { | ||
| reflow_phases_run: ReflowPhasesRun::BuiltDisplayList, | ||
| ..Default::default() | ||
| }) |
There was a problem hiding this comment.
Let's no interlace logic for one step with another. You also should create a new paint info instead of using the old one:
| let paint_info = self | |
| .stacking_context_tree | |
| .borrow() | |
| .as_ref() | |
| .map(|tree| tree.paint_info.clone())?; | |
| self.box_tree.borrow_mut().take(); | |
| self.fragment_tree.borrow_mut().take(); | |
| self.stacking_context_tree.borrow_mut().take(); | |
| let mut builder = webrender_api::DisplayListBuilder::new(paint_info.pipeline_id); | |
| builder.begin(); | |
| let (_, empty_display_list) = builder.end(); | |
| self.paint_api | |
| .send_display_list(self.webview_id, &paint_info, empty_display_list); | |
| self.last_display_list_was_empty.set(true); | |
| Some(ReflowResult { | |
| reflow_phases_run: ReflowPhasesRun::BuiltDisplayList, | |
| ..Default::default() | |
| }) | |
| // Clear layout trees. | |
| self.box_tree.borrow_mut().take(); | |
| self.fragment_tree.borrow_mut().take(); | |
| self.stacking_context_tree.borrow_mut().take(); | |
| // Send empty display list. | |
| let paint_info = PaintDisplayListInfo::new( | |
| viewport_details, | |
| Size2D::zero(), | |
| self.id.into(), | |
| reflow_request.epoch, | |
| AxesScrollSensitivity { x: ScrollType::InputEvents | ScrollType::Script, y: ScrollType::InputEvents | ScrollType::Script }, | |
| !self.have_ever_generated_display_list.get(), | |
| ); | |
| let mut builder = webrender_api::DisplayListBuilder::new(paint_info.pipeline_id); | |
| builder.begin(); | |
| let (_, empty_display_list) = builder.end(); | |
| self.paint_api | |
| .send_display_list(self.webview_id, &paint_info, empty_display_list); | |
| self.last_display_list_was_empty.set(true); | |
| self.set_have_ever_generated_display_list.set(true); | |
| Some(ReflowResult { | |
| reflow_phases_run: ReflowPhasesRun::BuiltDisplayList, | |
| ..Default::default() | |
| }) |
There was a problem hiding this comment.
I didn't like the idea of reallocating a separate paint api guess we still should do it anyways 😅
There was a problem hiding this comment.
The epoch of the previous one is incorrect and I don't know if we can absolutely guarantee that a previous one exists. Consider the case where you immediately remove the Document element before the pipeline has had a chance to create the stacking context tree. This kind of assumption makes me nervous and I think we should write the code defensively here. In the end, the entire data structure is serialized or copied onto the channel anyway.
There was a problem hiding this comment.
Great 👍 will implement it
Thanks
|
There are two new subtests in @rovertrack: You'll need to update the test expectations for these two subtests. It is documented in the book here. |
@mrobinson : I think you actually added the previous |
@mukilan so was this the reason this is not merged ?? |
|
Yes |
Signed-off-by: Rover track <rishan.pgowda@gmail.com>
Signed-off-by: Rover track <rishan.pgowda@gmail.com>
|
📝 Transplanted new upstreamable changes to existing upstream WPT pull request (web-platform-tests/wpt#59312). |
Oh, you are probably right @mukilan. Sorry, @rovertrack, it was my fault all along. 🤦 |
@mrobinson please that happens to all of us ,i have messed up things more than you in this PR 😅 |
|
@mukilan thanks for the instructions I have updated the tests regarding the expectation of the subtests which you had referred |
|
✍ Updated existing upstream WPT pull request (web-platform-tests/wpt#59312) title and body. |
|
Thanks to everyone for their support on this PR 🙂! |
Previously, when the
Documentelement was removed, no further display list updates would be sent to paint. This change makes it so that when theDocumentelement is removed a single new empty display list is sent.Testing: This change adds a new WPT test .
Fixes: #44101