1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/core/display_lock/display_lock_context.h"
6 
7 #include <memory>
8 #include <utility>
9 
10 #include "base/memory/ptr_util.h"
11 #include "base/test/scoped_feature_list.h"
12 #include "cc/base/features.h"
13 #include "mojo/public/cpp/bindings/pending_remote.h"
14 #include "mojo/public/cpp/bindings/receiver.h"
15 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
16 #include "third_party/blink/renderer/core/css/style_change_reason.h"
17 #include "third_party/blink/renderer/core/display_lock/display_lock_document_state.h"
18 #include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
19 #include "third_party/blink/renderer/core/dom/dom_token_list.h"
20 #include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
21 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
22 #include "third_party/blink/renderer/core/editing/ephemeral_range.h"
23 #include "third_party/blink/renderer/core/editing/finder/text_finder.h"
24 #include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h"
25 #include "third_party/blink/renderer/core/editing/visible_units.h"
26 #include "third_party/blink/renderer/core/frame/find_in_page.h"
27 #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
28 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
29 #include "third_party/blink/renderer/core/geometry/dom_rect.h"
30 #include "third_party/blink/renderer/core/html/html_template_element.h"
31 #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
32 #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
33 #include "third_party/blink/renderer/core/paint/paint_layer.h"
34 #include "third_party/blink/renderer/core/style/computed_style.h"
35 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
36 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
37 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
38 
39 namespace blink {
40 namespace {
41 class DisplayLockTestFindInPageClient : public mojom::blink::FindInPageClient {
42  public:
DisplayLockTestFindInPageClient()43   DisplayLockTestFindInPageClient()
44       : find_results_are_ready_(false), active_index_(-1), count_(-1) {}
45 
46   ~DisplayLockTestFindInPageClient() override = default;
47 
SetFrame(WebLocalFrameImpl * frame)48   void SetFrame(WebLocalFrameImpl* frame) {
49     frame->GetFindInPage()->SetClient(receiver_.BindNewPipeAndPassRemote());
50   }
51 
SetNumberOfMatches(int request_id,unsigned int current_number_of_matches,mojom::blink::FindMatchUpdateType final_update)52   void SetNumberOfMatches(
53       int request_id,
54       unsigned int current_number_of_matches,
55       mojom::blink::FindMatchUpdateType final_update) final {
56     count_ = current_number_of_matches;
57     find_results_are_ready_ =
58         (final_update == mojom::blink::FindMatchUpdateType::kFinalUpdate);
59   }
60 
SetActiveMatch(int request_id,const gfx::Rect & active_match_rect,int active_match_ordinal,mojom::blink::FindMatchUpdateType final_update)61   void SetActiveMatch(int request_id,
62                       const gfx::Rect& active_match_rect,
63                       int active_match_ordinal,
64                       mojom::blink::FindMatchUpdateType final_update) final {
65     active_match_rect_ = IntRect(active_match_rect);
66     active_index_ = active_match_ordinal;
67     find_results_are_ready_ =
68         (final_update == mojom::blink::FindMatchUpdateType::kFinalUpdate);
69   }
70 
FindResultsAreReady() const71   bool FindResultsAreReady() const { return find_results_are_ready_; }
Count() const72   int Count() const { return count_; }
ActiveIndex() const73   int ActiveIndex() const { return active_index_; }
ActiveMatchRect() const74   IntRect ActiveMatchRect() const { return active_match_rect_; }
75 
Reset()76   void Reset() {
77     find_results_are_ready_ = false;
78     count_ = -1;
79     active_index_ = -1;
80     active_match_rect_ = IntRect();
81   }
82 
83  private:
84   IntRect active_match_rect_;
85   bool find_results_are_ready_;
86   int active_index_;
87 
88   int count_;
89   mojo::Receiver<mojom::blink::FindInPageClient> receiver_{this};
90 };
91 
92 class DisplayLockEmptyEventListener final : public NativeEventListener {
93  public:
Invoke(ExecutionContext *,Event *)94   void Invoke(ExecutionContext*, Event*) final {}
95 };
96 }  // namespace
97 
98 class DisplayLockContextTest
99     : public testing::Test,
100       private ScopedCSSContentVisibilityHiddenMatchableForTest {
101  public:
DisplayLockContextTest()102   DisplayLockContextTest()
103       : ScopedCSSContentVisibilityHiddenMatchableForTest(true) {}
104 
SetUp()105   void SetUp() override { web_view_helper_.Initialize(); }
106 
TearDown()107   void TearDown() override { web_view_helper_.Reset(); }
108 
GetDocument()109   Document& GetDocument() {
110     return *static_cast<Document*>(
111         web_view_helper_.LocalMainFrame()->GetDocument());
112   }
GetFindInPage()113   FindInPage* GetFindInPage() {
114     return web_view_helper_.LocalMainFrame()->GetFindInPage();
115   }
LocalMainFrame()116   WebLocalFrameImpl* LocalMainFrame() {
117     return web_view_helper_.LocalMainFrame();
118   }
119 
UpdateAllLifecyclePhasesForTest()120   void UpdateAllLifecyclePhasesForTest() {
121     GetDocument().View()->UpdateAllLifecyclePhasesForTest();
122   }
123 
SetHtmlInnerHTML(const char * content)124   void SetHtmlInnerHTML(const char* content) {
125     GetDocument().documentElement()->setInnerHTML(String::FromUTF8(content));
126     UpdateAllLifecyclePhasesForTest();
127   }
128 
ResizeAndFocus()129   void ResizeAndFocus() {
130     web_view_helper_.Resize(gfx::Size(640, 480));
131     web_view_helper_.GetWebView()->MainFrameWidget()->SetFocus(true);
132     test::RunPendingTasks();
133   }
134 
LockElement(Element & element,bool activatable)135   void LockElement(Element& element, bool activatable) {
136     StringBuilder value;
137     value.Append("content-visibility: hidden");
138     if (activatable)
139       value.Append("-matchable");
140     element.setAttribute(html_names::kStyleAttr, value.ToAtomicString());
141     UpdateAllLifecyclePhasesForTest();
142   }
143 
CommitElement(Element & element,bool update_lifecycle=true)144   void CommitElement(Element& element, bool update_lifecycle = true) {
145     element.setAttribute(html_names::kStyleAttr, "");
146     if (update_lifecycle)
147       UpdateAllLifecyclePhasesForTest();
148   }
149 
UnlockImmediate(DisplayLockContext * context)150   void UnlockImmediate(DisplayLockContext* context) {
151     context->SetRequestedState(EContentVisibility::kVisible);
152   }
153 
FindOptions(bool new_session=true)154   mojom::blink::FindOptionsPtr FindOptions(bool new_session = true) {
155     auto find_options = mojom::blink::FindOptions::New();
156     find_options->run_synchronously_for_testing = true;
157     find_options->new_session = new_session;
158     find_options->forward = true;
159     return find_options;
160   }
161 
Find(String search_text,DisplayLockTestFindInPageClient & client,bool new_session=true)162   void Find(String search_text,
163             DisplayLockTestFindInPageClient& client,
164             bool new_session = true) {
165     client.Reset();
166     GetFindInPage()->Find(FAKE_FIND_ID, search_text, FindOptions(new_session));
167     test::RunPendingTasks();
168   }
169 
ReattachWasBlocked(DisplayLockContext * context)170   bool ReattachWasBlocked(DisplayLockContext* context) {
171     return context->reattach_layout_tree_was_blocked_;
172   }
173 
174   const int FAKE_FIND_ID = 1;
175 
176  private:
177   frame_test_helpers::WebViewHelper web_view_helper_;
178 };
179 
TEST_F(DisplayLockContextTest,LockAfterAppendStyleDirtyBits)180 TEST_F(DisplayLockContextTest, LockAfterAppendStyleDirtyBits) {
181   SetHtmlInnerHTML(R"HTML(
182     <style>
183     div {
184       width: 100px;
185       height: 100px;
186       contain: style layout paint;
187     }
188     </style>
189     <body><div id="container"><div id="child"></div></div></body>
190   )HTML");
191 
192   auto* element = GetDocument().getElementById("container");
193   LockElement(*element, false);
194 
195   // Finished acquiring the lock.
196   EXPECT_FALSE(element->GetDisplayLockContext()->ShouldStyleChildren());
197   EXPECT_FALSE(element->GetDisplayLockContext()->ShouldLayoutChildren());
198   EXPECT_FALSE(element->GetDisplayLockContext()->ShouldPaintChildren());
199   EXPECT_EQ(
200       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 1);
201 
202   // If the element is dirty, style recalc would handle it in the next recalc.
203   element->setAttribute(html_names::kStyleAttr,
204                         "content-visibility: hidden; color: red;");
205   EXPECT_TRUE(GetDocument().body()->ChildNeedsStyleRecalc());
206   EXPECT_TRUE(element->NeedsStyleRecalc());
207   EXPECT_FALSE(element->ChildNeedsStyleRecalc());
208 
209   UpdateAllLifecyclePhasesForTest();
210   EXPECT_FALSE(element->NeedsStyleRecalc());
211   EXPECT_FALSE(element->ChildNeedsStyleRecalc());
212   EXPECT_TRUE(element->GetComputedStyle());
213   EXPECT_EQ(
214       element->GetComputedStyle()->VisitedDependentColor(GetCSSPropertyColor()),
215       MakeRGB(255, 0, 0));
216   // Manually commit the lock so that we can verify which dirty bits get
217   // propagated.
218   UnlockImmediate(element->GetDisplayLockContext());
219   element->setAttribute(html_names::kStyleAttr, "color: red;");
220 
221   auto* child = GetDocument().getElementById("child");
222   EXPECT_TRUE(GetDocument().body()->ChildNeedsStyleRecalc());
223   EXPECT_TRUE(element->NeedsStyleRecalc());
224   EXPECT_FALSE(element->ChildNeedsStyleRecalc());
225   EXPECT_FALSE(child->NeedsStyleRecalc());
226   UpdateAllLifecyclePhasesForTest();
227 
228   EXPECT_FALSE(GetDocument().body()->ChildNeedsStyleRecalc());
229   EXPECT_FALSE(element->NeedsStyleRecalc());
230   EXPECT_FALSE(element->ChildNeedsStyleRecalc());
231   EXPECT_FALSE(child->NeedsStyleRecalc());
232 
233   // Lock the child.
234   child->setAttribute(html_names::kStyleAttr,
235                       "content-visibility: hidden; color: blue;");
236   UpdateAllLifecyclePhasesForTest();
237 
238   EXPECT_FALSE(GetDocument().body()->ChildNeedsStyleRecalc());
239   EXPECT_FALSE(element->NeedsStyleRecalc());
240   EXPECT_FALSE(element->ChildNeedsStyleRecalc());
241   EXPECT_FALSE(child->NeedsStyleRecalc());
242   ASSERT_TRUE(child->GetComputedStyle());
243   EXPECT_EQ(
244       child->GetComputedStyle()->VisitedDependentColor(GetCSSPropertyColor()),
245       MakeRGB(0, 0, 255));
246 
247   UnlockImmediate(child->GetDisplayLockContext());
248   child->setAttribute(html_names::kStyleAttr, "color: blue;");
249   EXPECT_TRUE(GetDocument().body()->ChildNeedsStyleRecalc());
250   EXPECT_FALSE(element->NeedsStyleRecalc());
251   EXPECT_TRUE(element->ChildNeedsStyleRecalc());
252   EXPECT_TRUE(child->NeedsStyleRecalc());
253   UpdateAllLifecyclePhasesForTest();
254 
255   EXPECT_FALSE(GetDocument().body()->ChildNeedsStyleRecalc());
256   EXPECT_FALSE(element->NeedsStyleRecalc());
257   EXPECT_FALSE(element->ChildNeedsStyleRecalc());
258   EXPECT_FALSE(child->NeedsStyleRecalc());
259   ASSERT_TRUE(child->GetComputedStyle());
260   EXPECT_EQ(
261       child->GetComputedStyle()->VisitedDependentColor(GetCSSPropertyColor()),
262       MakeRGB(0, 0, 255));
263 }
264 
TEST_F(DisplayLockContextTest,LockedElementIsNotSearchableViaFindInPage)265 TEST_F(DisplayLockContextTest, LockedElementIsNotSearchableViaFindInPage) {
266   ResizeAndFocus();
267   SetHtmlInnerHTML(R"HTML(
268     <style>
269     #container {
270       width: 100px;
271       height: 100px;
272       contain: style layout paint;
273     }
274     </style>
275     <body><div id="container">testing</div></body>
276   )HTML");
277 
278   const String search_text = "testing";
279   DisplayLockTestFindInPageClient client;
280   client.SetFrame(LocalMainFrame());
281 
282   auto* container = GetDocument().getElementById("container");
283   LockElement(*container, false /* activatable */);
284   Find(search_text, client);
285   EXPECT_EQ(0, client.Count());
286 
287   // Check if we can find the result after we commit.
288   CommitElement(*container);
289   Find(search_text, client);
290   EXPECT_EQ(1, client.Count());
291 }
292 
TEST_F(DisplayLockContextTest,ActivatableLockedElementIsSearchableViaFindInPage)293 TEST_F(DisplayLockContextTest,
294        ActivatableLockedElementIsSearchableViaFindInPage) {
295   ResizeAndFocus();
296   SetHtmlInnerHTML(R"HTML(
297     <style>
298     .spacer {
299       height: 10000px;
300     }
301     #container {
302       width: 100px;
303       height: 100px;
304       contain: style layout paint;
305     }
306     </style>
307     <body><div class=spacer></div><div id="container">testing</div></body>
308   )HTML");
309 
310   const String search_text = "testing";
311   DisplayLockTestFindInPageClient client;
312   client.SetFrame(LocalMainFrame());
313 
314   // Finds on a normal element.
315   Find(search_text, client);
316   EXPECT_EQ(1, client.Count());
317   // Clears selections since we're going to use the same query next time.
318   GetFindInPage()->StopFinding(
319       mojom::StopFindAction::kStopFindActionClearSelection);
320 
321   auto* container = GetDocument().getElementById("container");
322   LockElement(*container, true /* activatable */);
323 
324   EXPECT_TRUE(container->GetDisplayLockContext()->IsLocked());
325   // Check if we can still get the same result with the same query.
326   Find(search_text, client);
327   EXPECT_EQ(1, client.Count());
328   EXPECT_TRUE(container->GetDisplayLockContext()->IsLocked());
329   EXPECT_GT(GetDocument().scrollingElement()->scrollTop(), 1000);
330 }
331 
TEST_F(DisplayLockContextTest,FindInPageContinuesAfterRelock)332 TEST_F(DisplayLockContextTest, FindInPageContinuesAfterRelock) {
333   ResizeAndFocus();
334   SetHtmlInnerHTML(R"HTML(
335     <style>
336     .spacer {
337       height: 10000px;
338     }
339     #container {
340       width: 100px;
341       height: 100px;
342     }
343     .auto { content-visibility: auto }
344     </style>
345     <body><div class=spacer></div><div id="container" class=auto>testing</div></body>
346   )HTML");
347 
348   const String search_text = "testing";
349   DisplayLockTestFindInPageClient client;
350   client.SetFrame(LocalMainFrame());
351 
352   // Finds on a normal element.
353   Find(search_text, client);
354   EXPECT_EQ(1, client.Count());
355 
356   auto* container = GetDocument().getElementById("container");
357   GetDocument().scrollingElement()->setScrollTop(0);
358 
359   UpdateAllLifecyclePhasesForTest();
360   UpdateAllLifecyclePhasesForTest();
361 
362   EXPECT_TRUE(container->GetDisplayLockContext()->IsLocked());
363 
364   // Clears selections since we're going to use the same query next time.
365   GetFindInPage()->StopFinding(
366       mojom::StopFindAction::kStopFindActionKeepSelection);
367 
368   UpdateAllLifecyclePhasesForTest();
369 
370   // This should not crash.
371   Find(search_text, client, false);
372 
373   EXPECT_EQ(1, client.Count());
374 }
375 
TEST_F(DisplayLockContextTest,FindInPageTargetBelowLockedSize)376 TEST_F(DisplayLockContextTest, FindInPageTargetBelowLockedSize) {
377   ResizeAndFocus();
378   SetHtmlInnerHTML(R"HTML(
379     <style>
380     .spacer { height: 1000px; }
381     #container { contain-intrinsic-size: 1px; }
382     .auto { content-visibility: auto }
383     </style>
384     <body>
385       <div class=spacer></div>
386       <div id=container class=auto>
387         <div class=spacer></div>
388         <div id=target>testing</div>
389       </div>
390       <div class=spacer></div>
391       <div class=spacer></div>
392     </body>
393   )HTML");
394 
395   const String search_text = "testing";
396   DisplayLockTestFindInPageClient client;
397   client.SetFrame(LocalMainFrame());
398 
399   Find(search_text, client);
400   EXPECT_EQ(1, client.Count());
401 
402   auto* container = GetDocument().getElementById("container");
403   // The container should be unlocked.
404   EXPECT_FALSE(container->GetDisplayLockContext()->IsLocked());
405   UpdateAllLifecyclePhasesForTest();
406   EXPECT_FALSE(container->GetDisplayLockContext()->IsLocked());
407 
408   EXPECT_FLOAT_EQ(GetDocument().scrollingElement()->scrollTop(), 1768);
409 }
410 
TEST_F(DisplayLockContextTest,ActivatableLockedElementTickmarksAreAtLockedRoots)411 TEST_F(DisplayLockContextTest,
412        ActivatableLockedElementTickmarksAreAtLockedRoots) {
413   ResizeAndFocus();
414   SetHtmlInnerHTML(R"HTML(
415     <style>
416     body {
417       margin: 0;
418       padding: 0;
419     }
420     .small {
421       width: 100px;
422       height: 100px;
423     }
424     .medium {
425       width: 150px;
426       height: 150px;
427     }
428     .large {
429       width: 200px;
430       height: 200px;
431     }
432     </style>
433     <body>
434       testing
435       <div id="container1" class=small>testing</div>
436       <div id="container2" class=medium>testing</div>
437       <div id="container3" class=large>
438         <div id="container4" class=medium>testing</div>
439       </div>
440       <div id="container5" class=small>testing</div>
441     </body>
442   )HTML");
443 
444   const String search_text = "testing";
445   DisplayLockTestFindInPageClient client;
446   client.SetFrame(LocalMainFrame());
447 
448   auto* container1 = GetDocument().getElementById("container1");
449   auto* container2 = GetDocument().getElementById("container2");
450   auto* container3 = GetDocument().getElementById("container3");
451   auto* container4 = GetDocument().getElementById("container4");
452   auto* container5 = GetDocument().getElementById("container5");
453   LockElement(*container5, false /* activatable */);
454   LockElement(*container4, true /* activatable */);
455   LockElement(*container3, true /* activatable */);
456   LockElement(*container2, true /* activatable */);
457   LockElement(*container1, true /* activatable */);
458 
459   EXPECT_TRUE(container1->GetDisplayLockContext()->IsLocked());
460   EXPECT_TRUE(container2->GetDisplayLockContext()->IsLocked());
461   EXPECT_TRUE(container3->GetDisplayLockContext()->IsLocked());
462   EXPECT_TRUE(container4->GetDisplayLockContext()->IsLocked());
463   EXPECT_TRUE(container5->GetDisplayLockContext()->IsLocked());
464 
465   // Do a find-in-page.
466   Find(search_text, client);
467   // "testing" outside of the container divs, and 3 inside activatable divs.
468   EXPECT_EQ(4, client.Count());
469 
470   auto tick_rects = GetDocument().Markers().LayoutRectsForTextMatchMarkers();
471   ASSERT_EQ(4u, tick_rects.size());
472 
473   // Sort the layout rects by y coordinate for deterministic checks below.
474   std::sort(tick_rects.begin(), tick_rects.end(),
475             [](const IntRect& a, const IntRect& b) { return a.Y() < b.Y(); });
476 
477   int y_offset = tick_rects[0].Height();
478 
479   // The first tick rect will be based on the text itself, so we don't need to
480   // check that. The next three should be the small, medium and large rects,
481   // since those are the locked roots.
482   EXPECT_EQ(IntRect(0, y_offset, 100, 100), tick_rects[1]);
483   y_offset += tick_rects[1].Height();
484   EXPECT_EQ(IntRect(0, y_offset, 150, 150), tick_rects[2]);
485   y_offset += tick_rects[2].Height();
486   EXPECT_EQ(IntRect(0, y_offset, 200, 200), tick_rects[3]);
487 }
488 
TEST_F(DisplayLockContextTest,FindInPageWhileLockedContentChangesDoesNotCrash)489 TEST_F(DisplayLockContextTest,
490        FindInPageWhileLockedContentChangesDoesNotCrash) {
491   ResizeAndFocus();
492   SetHtmlInnerHTML(R"HTML(
493     <style>
494     #container {
495       width: 100px;
496       height: 100px;
497       contain: style layout paint;
498     }
499     </style>
500     <body>testing<div id="container">testing</div></body>
501   )HTML");
502 
503   const String search_text = "testing";
504   DisplayLockTestFindInPageClient client;
505   client.SetFrame(LocalMainFrame());
506 
507   // Lock the container.
508   auto* container = GetDocument().getElementById("container");
509   LockElement(*container, true /* activatable */);
510   EXPECT_TRUE(container->GetDisplayLockContext()->IsLocked());
511 
512   // Find the first "testing", container still locked since the match is outside
513   // the container.
514   Find(search_text, client);
515   EXPECT_EQ(2, client.Count());
516   EXPECT_TRUE(container->GetDisplayLockContext()->IsLocked());
517 
518   // Change the inner text, this should not DCHECK.
519   container->setInnerHTML("please don't DCHECK");
520   UpdateAllLifecyclePhasesForTest();
521 }
522 
TEST_F(DisplayLockContextTest,FindInPageWithChangedContent)523 TEST_F(DisplayLockContextTest, FindInPageWithChangedContent) {
524   if (!RuntimeEnabledFeatures::LayoutNGEnabled())
525     return;
526   ResizeAndFocus();
527   SetHtmlInnerHTML(R"HTML(
528     <style>
529     #container {
530       width: 100px;
531       height: 100px;
532       contain: style layout paint;
533     }
534     </style>
535     <body><div id="container">testing</div></body>
536   )HTML");
537 
538   // Check if the result is correct if we update the contents.
539   auto* container = GetDocument().getElementById("container");
540   LockElement(*container, true /* activatable */);
541   EXPECT_TRUE(container->GetDisplayLockContext()->IsLocked());
542   container->setInnerHTML(
543       "testing"
544       "<div>testing</div>"
545       "tes<div style='display:none;'>x</div>ting");
546 
547   DisplayLockTestFindInPageClient client;
548   client.SetFrame(LocalMainFrame());
549   Find("testing", client);
550   EXPECT_EQ(3, client.Count());
551   EXPECT_TRUE(container->GetDisplayLockContext()->IsLocked());
552 }
553 
TEST_F(DisplayLockContextTest,FindInPageWithNoMatchesWontUnlock)554 TEST_F(DisplayLockContextTest, FindInPageWithNoMatchesWontUnlock) {
555   ResizeAndFocus();
556   SetHtmlInnerHTML(R"HTML(
557     <style>
558     #container {
559       width: 100px;
560       height: 100px;
561       contain: style layout paint;
562     }
563     </style>
564     <body><div id="container">tes<div>ting</div><div style='display:none;'>testing</div></div></body>
565   )HTML");
566 
567   auto* container = GetDocument().getElementById("container");
568   LockElement(*container, true /* activatable */);
569   LockElement(*container, true /* activatable */);
570   EXPECT_TRUE(container->GetDisplayLockContext()->IsLocked());
571 
572   DisplayLockTestFindInPageClient client;
573   client.SetFrame(LocalMainFrame());
574   Find("testing", client);
575   // No results found, container stays locked.
576   EXPECT_EQ(0, client.Count());
577   EXPECT_TRUE(container->GetDisplayLockContext()->IsLocked());
578 }
579 
TEST_F(DisplayLockContextTest,NestedActivatableLockedElementIsSearchableViaFindInPage)580 TEST_F(DisplayLockContextTest,
581        NestedActivatableLockedElementIsSearchableViaFindInPage) {
582   ResizeAndFocus();
583   SetHtmlInnerHTML(R"HTML(
584     <body>
585       <style>
586         div {
587           width: 100px;
588           height: 100px;
589           contain: style layout;
590         }
591       </style>
592       <div id='container'>
593         <div>testing1</div>
594         <div id='activatable'>
595         testing2
596           <div id='nestedNonActivatable'>
597             testing3
598           </div>
599         </div>
600         <div id='nonActivatable'>testing4</div>
601       </div>
602     "</body>"
603   )HTML");
604 
605   auto* container = GetDocument().getElementById("container");
606   auto* activatable = GetDocument().getElementById("activatable");
607   auto* non_activatable = GetDocument().getElementById("nonActivatable");
608   auto* nested_non_activatable =
609       GetDocument().getElementById("nestedNonActivatable");
610 
611   LockElement(*non_activatable, false /* activatable */);
612   LockElement(*nested_non_activatable, false /* activatable */);
613   LockElement(*activatable, true /* activatable */);
614   LockElement(*container, true /* activatable */);
615 
616   EXPECT_TRUE(container->GetDisplayLockContext()->IsLocked());
617   EXPECT_TRUE(activatable->GetDisplayLockContext()->IsLocked());
618   EXPECT_TRUE(non_activatable->GetDisplayLockContext()->IsLocked());
619   EXPECT_TRUE(nested_non_activatable->GetDisplayLockContext()->IsLocked());
620 
621   // We can find testing1 and testing2.
622   DisplayLockTestFindInPageClient client;
623   client.SetFrame(LocalMainFrame());
624   Find("testing", client);
625   EXPECT_EQ(2, client.Count());
626   EXPECT_EQ(1, client.ActiveIndex());
627 
628   EXPECT_TRUE(container->GetDisplayLockContext()->IsLocked());
629   EXPECT_TRUE(activatable->GetDisplayLockContext()->IsLocked());
630   EXPECT_TRUE(non_activatable->GetDisplayLockContext()->IsLocked());
631   EXPECT_TRUE(nested_non_activatable->GetDisplayLockContext()->IsLocked());
632 }
633 
TEST_F(DisplayLockContextTest,NestedActivatableLockedElementIsNotUnlockedByFindInPage)634 TEST_F(DisplayLockContextTest,
635        NestedActivatableLockedElementIsNotUnlockedByFindInPage) {
636   ResizeAndFocus();
637   SetHtmlInnerHTML(R"HTML(
638     <body>
639       <style>
640         div {
641           width: 100px;
642           height: 100px;
643           contain: style layout;
644         }
645       </style>
646       <div id='container'>
647         <div id='child'>testing1</div>
648       </div>
649   )HTML");
650   auto* container = GetDocument().getElementById("container");
651   auto* child = GetDocument().getElementById("child");
652   LockElement(*child, true /* activatable */);
653   LockElement(*container, true /* activatable */);
654 
655   EXPECT_TRUE(container->GetDisplayLockContext()->IsLocked());
656   EXPECT_TRUE(child->GetDisplayLockContext()->IsLocked());
657   // We can find testing1 and testing2.
658   DisplayLockTestFindInPageClient client;
659   client.SetFrame(LocalMainFrame());
660   Find("testing", client);
661   EXPECT_EQ(1, client.Count());
662   EXPECT_EQ(1, client.ActiveIndex());
663 
664   EXPECT_TRUE(container->GetDisplayLockContext()->IsLocked());
665   EXPECT_TRUE(child->GetDisplayLockContext()->IsLocked());
666 }
667 
TEST_F(DisplayLockContextTest,CallUpdateStyleAndLayoutAfterChange)668 TEST_F(DisplayLockContextTest, CallUpdateStyleAndLayoutAfterChange) {
669   ResizeAndFocus();
670   SetHtmlInnerHTML(R"HTML(
671     <style>
672     #container {
673       width: 100px;
674       height: 100px;
675       contain: style layout paint;
676     }
677     </style>
678     <body><div id="container"><b>t</b>esting</div></body>
679   )HTML");
680   auto* element = GetDocument().getElementById("container");
681   LockElement(*element, false);
682 
683   // Sanity checks to ensure the element is locked.
684   EXPECT_TRUE(element->GetDisplayLockContext()->IsLocked());
685   EXPECT_FALSE(element->GetDisplayLockContext()->ShouldStyleChildren());
686   EXPECT_FALSE(element->GetDisplayLockContext()->ShouldLayoutChildren());
687   EXPECT_FALSE(element->GetDisplayLockContext()->ShouldPaintChildren());
688   EXPECT_EQ(
689       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 1);
690   EXPECT_EQ(GetDocument()
691                 .GetDisplayLockDocumentState()
692                 .DisplayLockBlockingAllActivationCount(),
693             1);
694 
695   EXPECT_FALSE(element->NeedsStyleRecalc());
696   EXPECT_FALSE(element->ChildNeedsStyleRecalc());
697   EXPECT_FALSE(element->NeedsReattachLayoutTree());
698   EXPECT_FALSE(element->ChildNeedsReattachLayoutTree());
699 
700   // Testing whitespace reattachment, shouldn't mark for reattachment.
701   element->firstChild()->remove();
702 
703   EXPECT_FALSE(element->NeedsStyleRecalc());
704   EXPECT_FALSE(element->ChildNeedsStyleRecalc());
705   EXPECT_FALSE(element->NeedsReattachLayoutTree());
706   EXPECT_FALSE(element->ChildNeedsReattachLayoutTree());
707 
708   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
709 
710   EXPECT_FALSE(element->NeedsStyleRecalc());
711   EXPECT_FALSE(element->ChildNeedsStyleRecalc());
712   EXPECT_FALSE(element->NeedsReattachLayoutTree());
713   EXPECT_FALSE(element->ChildNeedsReattachLayoutTree());
714 
715   // Testing whitespace reattachment + dirty style.
716   element->setInnerHTML("<div>something</div>");
717 
718   EXPECT_FALSE(element->NeedsStyleRecalc());
719   EXPECT_TRUE(element->ChildNeedsStyleRecalc());
720   EXPECT_FALSE(element->NeedsReattachLayoutTree());
721   EXPECT_FALSE(element->ChildNeedsReattachLayoutTree());
722 
723   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
724 
725   EXPECT_FALSE(element->NeedsStyleRecalc());
726   EXPECT_TRUE(element->ChildNeedsStyleRecalc());
727   EXPECT_FALSE(element->NeedsReattachLayoutTree());
728   EXPECT_FALSE(element->ChildNeedsReattachLayoutTree());
729 
730   // Manually start commit, so that we can verify which dirty bits get
731   // propagated.
732   UnlockImmediate(element->GetDisplayLockContext());
733   EXPECT_TRUE(element->ChildNeedsStyleRecalc());
734   EXPECT_FALSE(element->NeedsReattachLayoutTree());
735   EXPECT_FALSE(element->ChildNeedsReattachLayoutTree());
736 
737   // Simulating style recalc happening, will mark for reattachment.
738   element->ClearChildNeedsStyleRecalc();
739   element->firstChild()->ClearNeedsStyleRecalc();
740   element->GetDisplayLockContext()->DidStyleChildren();
741 
742   EXPECT_FALSE(element->ChildNeedsStyleRecalc());
743   EXPECT_FALSE(element->NeedsReattachLayoutTree());
744   EXPECT_TRUE(element->ChildNeedsReattachLayoutTree());
745 }
746 
TEST_F(DisplayLockContextTest,CallUpdateStyleAndLayoutAfterChangeCSS)747 TEST_F(DisplayLockContextTest, CallUpdateStyleAndLayoutAfterChangeCSS) {
748   ResizeAndFocus();
749   SetHtmlInnerHTML(R"HTML(
750     <style>
751     #container {
752       width: 100px;
753       height: 100px;
754       contain: style layout paint;
755     }
756     .bg {
757       background: blue;
758     }
759     .locked {
760       content-visibility: hidden;
761     }
762     </style>
763     <body><div class=locked id="container"><b>t</b>esting<div id=inner></div></div></body>
764   )HTML");
765   auto* element = GetDocument().getElementById("container");
766   auto* inner = GetDocument().getElementById("inner");
767 
768   // Sanity checks to ensure the element is locked.
769   EXPECT_FALSE(element->GetDisplayLockContext()->ShouldStyleChildren());
770   EXPECT_FALSE(element->GetDisplayLockContext()->ShouldLayoutChildren());
771   EXPECT_FALSE(element->GetDisplayLockContext()->ShouldPaintChildren());
772   EXPECT_EQ(
773       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 1);
774   EXPECT_EQ(GetDocument()
775                 .GetDisplayLockDocumentState()
776                 .DisplayLockBlockingAllActivationCount(),
777             1);
778 
779   EXPECT_TRUE(ReattachWasBlocked(element->GetDisplayLockContext()));
780   // Note that we didn't create a layout object for inner, since the layout tree
781   // attachment was blocked.
782   EXPECT_FALSE(inner->GetLayoutObject());
783 
784   EXPECT_FALSE(element->NeedsStyleRecalc());
785   EXPECT_FALSE(element->ChildNeedsStyleRecalc());
786   EXPECT_FALSE(element->NeedsReattachLayoutTree());
787   EXPECT_FALSE(element->ChildNeedsReattachLayoutTree());
788 
789   element->classList().Remove("locked");
790 
791   // Class list changed, so we should need self style change.
792   EXPECT_TRUE(element->NeedsStyleRecalc());
793   EXPECT_FALSE(element->ChildNeedsStyleRecalc());
794   EXPECT_FALSE(element->NeedsReattachLayoutTree());
795   EXPECT_FALSE(element->ChildNeedsReattachLayoutTree());
796 
797   UpdateAllLifecyclePhasesForTest();
798 
799   EXPECT_FALSE(element->NeedsStyleRecalc());
800   EXPECT_FALSE(element->ChildNeedsStyleRecalc());
801   EXPECT_FALSE(element->NeedsReattachLayoutTree());
802   EXPECT_FALSE(element->ChildNeedsReattachLayoutTree());
803   // Because we upgraded our style change, we created a layout object for inner.
804   EXPECT_TRUE(inner->GetLayoutObject());
805 }
806 
TEST_F(DisplayLockContextTest,LockedElementAndDescendantsAreNotFocusable)807 TEST_F(DisplayLockContextTest, LockedElementAndDescendantsAreNotFocusable) {
808   ResizeAndFocus();
809   SetHtmlInnerHTML(R"HTML(
810     <style>
811     #container {
812       width: 100px;
813       height: 100px;
814       contain: style layout paint;
815     }
816     </style>
817     <body>
818     <div id="container">
819       <input id="textfield", type="text">
820     </div>
821     </body>
822   )HTML");
823 
824   // We start off as being focusable.
825   ASSERT_TRUE(GetDocument().getElementById("textfield")->IsKeyboardFocusable());
826   ASSERT_TRUE(GetDocument().getElementById("textfield")->IsMouseFocusable());
827   ASSERT_TRUE(GetDocument().getElementById("textfield")->IsFocusable());
828   EXPECT_EQ(
829       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 0);
830   EXPECT_EQ(GetDocument()
831                 .GetDisplayLockDocumentState()
832                 .DisplayLockBlockingAllActivationCount(),
833             0);
834 
835   auto* element = GetDocument().getElementById("container");
836   LockElement(*element, false);
837 
838   // Sanity checks to ensure the element is locked.
839   EXPECT_FALSE(element->GetDisplayLockContext()->ShouldStyleChildren());
840   EXPECT_FALSE(element->GetDisplayLockContext()->ShouldLayoutChildren());
841   EXPECT_FALSE(element->GetDisplayLockContext()->ShouldPaintChildren());
842   EXPECT_EQ(
843       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 1);
844   EXPECT_EQ(GetDocument()
845                 .GetDisplayLockDocumentState()
846                 .DisplayLockBlockingAllActivationCount(),
847             1);
848 
849   // The input should not be focusable now.
850   EXPECT_FALSE(
851       GetDocument().getElementById("textfield")->IsKeyboardFocusable());
852   EXPECT_FALSE(GetDocument().getElementById("textfield")->IsMouseFocusable());
853   EXPECT_FALSE(GetDocument().getElementById("textfield")->IsFocusable());
854 
855   // Calling explicit focus() should also not focus the element.
856   GetDocument().getElementById("textfield")->focus();
857   EXPECT_FALSE(GetDocument().FocusedElement());
858 
859   // Now commit the lock and ensure we can focus the input
860   CommitElement(*element);
861 
862   EXPECT_TRUE(element->GetDisplayLockContext()->ShouldStyleChildren());
863   EXPECT_TRUE(element->GetDisplayLockContext()->ShouldLayoutChildren());
864   EXPECT_TRUE(element->GetDisplayLockContext()->ShouldPaintChildren());
865 
866   UpdateAllLifecyclePhasesForTest();
867 
868   EXPECT_EQ(
869       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 0);
870   EXPECT_EQ(GetDocument()
871                 .GetDisplayLockDocumentState()
872                 .DisplayLockBlockingAllActivationCount(),
873             0);
874   EXPECT_TRUE(GetDocument().getElementById("textfield")->IsKeyboardFocusable());
875   EXPECT_TRUE(GetDocument().getElementById("textfield")->IsMouseFocusable());
876   EXPECT_TRUE(GetDocument().getElementById("textfield")->IsFocusable());
877 
878   // Calling explicit focus() should focus the element
879   GetDocument().getElementById("textfield")->focus();
880   EXPECT_EQ(GetDocument().FocusedElement(),
881             GetDocument().getElementById("textfield"));
882 }
883 
TEST_F(DisplayLockContextTest,DisplayLockPreventsActivation)884 TEST_F(DisplayLockContextTest, DisplayLockPreventsActivation) {
885   ResizeAndFocus();
886   SetHtmlInnerHTML(R"HTML(
887     <body>
888     <div id="shadowHost">
889       <div id="slotted"></div>
890     </div>
891     </body>
892   )HTML");
893 
894   auto* host = GetDocument().getElementById("shadowHost");
895   auto* slotted = GetDocument().getElementById("slotted");
896 
897   ASSERT_FALSE(
898       host->DisplayLockPreventsActivation(DisplayLockActivationReason::kAny));
899   ASSERT_FALSE(slotted->DisplayLockPreventsActivation(
900       DisplayLockActivationReason::kAny));
901 
902   ShadowRoot& shadow_root =
903       host->AttachShadowRootInternal(ShadowRootType::kOpen);
904   shadow_root.setInnerHTML(
905       "<div id='container' style='contain:style layout "
906       "paint;'><slot></slot></div>");
907   UpdateAllLifecyclePhasesForTest();
908 
909   auto* container = shadow_root.getElementById("container");
910   EXPECT_FALSE(
911       host->DisplayLockPreventsActivation(DisplayLockActivationReason::kAny));
912   EXPECT_FALSE(container->DisplayLockPreventsActivation(
913       DisplayLockActivationReason::kAny));
914   EXPECT_FALSE(slotted->DisplayLockPreventsActivation(
915       DisplayLockActivationReason::kAny));
916 
917   LockElement(*container, false);
918 
919   EXPECT_EQ(
920       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 1);
921   EXPECT_EQ(GetDocument()
922                 .GetDisplayLockDocumentState()
923                 .DisplayLockBlockingAllActivationCount(),
924             1);
925   EXPECT_FALSE(
926       host->DisplayLockPreventsActivation(DisplayLockActivationReason::kAny));
927   EXPECT_TRUE(container->DisplayLockPreventsActivation(
928       DisplayLockActivationReason::kAny));
929   EXPECT_TRUE(slotted->DisplayLockPreventsActivation(
930       DisplayLockActivationReason::kAny));
931 
932   // Ensure that we resolve the acquire callback, thus finishing the acquire
933   // step.
934   UpdateAllLifecyclePhasesForTest();
935 
936   CommitElement(*container);
937 
938   EXPECT_EQ(
939       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 0);
940   EXPECT_EQ(GetDocument()
941                 .GetDisplayLockDocumentState()
942                 .DisplayLockBlockingAllActivationCount(),
943             0);
944   EXPECT_FALSE(
945       host->DisplayLockPreventsActivation(DisplayLockActivationReason::kAny));
946   EXPECT_FALSE(container->DisplayLockPreventsActivation(
947       DisplayLockActivationReason::kAny));
948   EXPECT_FALSE(slotted->DisplayLockPreventsActivation(
949       DisplayLockActivationReason::kAny));
950 
951   UpdateAllLifecyclePhasesForTest();
952 
953   EXPECT_EQ(
954       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 0);
955   EXPECT_EQ(GetDocument()
956                 .GetDisplayLockDocumentState()
957                 .DisplayLockBlockingAllActivationCount(),
958             0);
959   EXPECT_FALSE(
960       host->DisplayLockPreventsActivation(DisplayLockActivationReason::kAny));
961   EXPECT_FALSE(container->DisplayLockPreventsActivation(
962       DisplayLockActivationReason::kAny));
963   EXPECT_FALSE(slotted->DisplayLockPreventsActivation(
964       DisplayLockActivationReason::kAny));
965 
966   SetHtmlInnerHTML(R"HTML(
967     <body>
968     <div id="nonviewport" style="content-visibility: hidden-matchable">
969     </div>
970     </body>
971   )HTML");
972   auto* non_viewport = GetDocument().getElementById("nonviewport");
973 
974   EXPECT_EQ(
975       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 1);
976   EXPECT_EQ(GetDocument()
977                 .GetDisplayLockDocumentState()
978                 .DisplayLockBlockingAllActivationCount(),
979             0);
980 
981   EXPECT_FALSE(non_viewport->DisplayLockPreventsActivation(
982       DisplayLockActivationReason::kAny));
983   EXPECT_FALSE(non_viewport->DisplayLockPreventsActivation(
984       DisplayLockActivationReason::kFindInPage));
985   EXPECT_TRUE(non_viewport->DisplayLockPreventsActivation(
986       DisplayLockActivationReason::kUserFocus));
987 }
988 
TEST_F(DisplayLockContextTest,LockedElementAndFlatTreeDescendantsAreNotFocusable)989 TEST_F(DisplayLockContextTest,
990        LockedElementAndFlatTreeDescendantsAreNotFocusable) {
991   ResizeAndFocus();
992   SetHtmlInnerHTML(R"HTML(
993     <body>
994     <div id="shadowHost">
995       <input id="textfield" type="text">
996     </div>
997     </body>
998   )HTML");
999 
1000   auto* host = GetDocument().getElementById("shadowHost");
1001   auto* text_field = GetDocument().getElementById("textfield");
1002   ShadowRoot& shadow_root =
1003       host->AttachShadowRootInternal(ShadowRootType::kOpen);
1004   shadow_root.setInnerHTML(
1005       "<div id='container' style='contain:style layout "
1006       "paint;'><slot></slot></div>");
1007 
1008   UpdateAllLifecyclePhasesForTest();
1009   ASSERT_TRUE(text_field->IsKeyboardFocusable());
1010   ASSERT_TRUE(text_field->IsMouseFocusable());
1011   ASSERT_TRUE(text_field->IsFocusable());
1012 
1013   auto* element = shadow_root.getElementById("container");
1014   LockElement(*element, false);
1015 
1016   // Sanity checks to ensure the element is locked.
1017   EXPECT_FALSE(element->GetDisplayLockContext()->ShouldStyleChildren());
1018   EXPECT_FALSE(element->GetDisplayLockContext()->ShouldLayoutChildren());
1019   EXPECT_FALSE(element->GetDisplayLockContext()->ShouldPaintChildren());
1020   EXPECT_EQ(
1021       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 1);
1022   EXPECT_EQ(GetDocument()
1023                 .GetDisplayLockDocumentState()
1024                 .DisplayLockBlockingAllActivationCount(),
1025             1);
1026 
1027   // The input should not be focusable now.
1028   EXPECT_FALSE(text_field->IsKeyboardFocusable());
1029   EXPECT_FALSE(text_field->IsMouseFocusable());
1030   EXPECT_FALSE(text_field->IsFocusable());
1031 
1032   // Calling explicit focus() should also not focus the element.
1033   text_field->focus();
1034   EXPECT_FALSE(GetDocument().FocusedElement());
1035 }
1036 
TEST_F(DisplayLockContextTest,LockedCountsWithMultipleLocks)1037 TEST_F(DisplayLockContextTest, LockedCountsWithMultipleLocks) {
1038   ResizeAndFocus();
1039   SetHtmlInnerHTML(R"HTML(
1040     <style>
1041     .container {
1042       width: 100px;
1043       height: 100px;
1044       contain: style layout paint;
1045     }
1046     </style>
1047     <body>
1048     <div id="one" class="container">
1049       <div id="two" class="container"></div>
1050     </div>
1051     <div id="three" class="container"></div>
1052     </body>
1053   )HTML");
1054 
1055   EXPECT_EQ(
1056       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 0);
1057   EXPECT_EQ(GetDocument()
1058                 .GetDisplayLockDocumentState()
1059                 .DisplayLockBlockingAllActivationCount(),
1060             0);
1061 
1062   auto* one = GetDocument().getElementById("one");
1063   auto* two = GetDocument().getElementById("two");
1064   auto* three = GetDocument().getElementById("three");
1065 
1066   LockElement(*one, false);
1067 
1068   EXPECT_EQ(
1069       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 1);
1070   EXPECT_EQ(GetDocument()
1071                 .GetDisplayLockDocumentState()
1072                 .DisplayLockBlockingAllActivationCount(),
1073             1);
1074 
1075   LockElement(*two, false);
1076 
1077   // Because |two| is nested, the lock counts aren't updated since the lock
1078   // doesn't actually take effect until style can determine that we should lock.
1079   EXPECT_EQ(
1080       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 1);
1081   EXPECT_EQ(GetDocument()
1082                 .GetDisplayLockDocumentState()
1083                 .DisplayLockBlockingAllActivationCount(),
1084             1);
1085 
1086   LockElement(*three, false);
1087 
1088   EXPECT_EQ(
1089       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 2);
1090   EXPECT_EQ(GetDocument()
1091                 .GetDisplayLockDocumentState()
1092                 .DisplayLockBlockingAllActivationCount(),
1093             2);
1094 
1095   // Now commit the outer lock.
1096   CommitElement(*one);
1097 
1098   // The counts remain the same since now the inner lock is determined to be
1099   // locked.
1100   EXPECT_EQ(
1101       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 2);
1102   EXPECT_EQ(GetDocument()
1103                 .GetDisplayLockDocumentState()
1104                 .DisplayLockBlockingAllActivationCount(),
1105             2);
1106 
1107   // Commit the inner lock.
1108   CommitElement(*two);
1109 
1110   // Both inner and outer locks should have committed.
1111   EXPECT_EQ(
1112       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 1);
1113   EXPECT_EQ(GetDocument()
1114                 .GetDisplayLockDocumentState()
1115                 .DisplayLockBlockingAllActivationCount(),
1116             1);
1117 
1118   // Commit the sibling lock.
1119   CommitElement(*three);
1120 
1121   // Both inner and outer locks should have committed.
1122   EXPECT_EQ(
1123       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 0);
1124   EXPECT_EQ(GetDocument()
1125                 .GetDisplayLockDocumentState()
1126                 .DisplayLockBlockingAllActivationCount(),
1127             0);
1128 }
1129 
TEST_F(DisplayLockContextTest,ActivatableNotCountedAsBlocking)1130 TEST_F(DisplayLockContextTest, ActivatableNotCountedAsBlocking) {
1131   ResizeAndFocus();
1132   SetHtmlInnerHTML(R"HTML(
1133     <style>
1134     .container {
1135       width: 100px;
1136       height: 100px;
1137       contain: style layout paint;
1138     }
1139     </style>
1140     <body>
1141     <div id="activatable" class="container"></div>
1142     <div id="nonActivatable" class="container"></div>
1143     </body>
1144   )HTML");
1145 
1146   EXPECT_EQ(
1147       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 0);
1148   EXPECT_EQ(GetDocument()
1149                 .GetDisplayLockDocumentState()
1150                 .DisplayLockBlockingAllActivationCount(),
1151             0);
1152 
1153   auto* activatable = GetDocument().getElementById("activatable");
1154   auto* non_activatable = GetDocument().getElementById("nonActivatable");
1155 
1156   // Initial display lock context should be activatable, since nothing skipped
1157   // activation for it.
1158   EXPECT_TRUE(activatable->EnsureDisplayLockContext().IsActivatable(
1159       DisplayLockActivationReason::kAny));
1160 
1161   LockElement(*activatable, true);
1162 
1163   EXPECT_EQ(
1164       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 1);
1165   EXPECT_EQ(GetDocument()
1166                 .GetDisplayLockDocumentState()
1167                 .DisplayLockBlockingAllActivationCount(),
1168             0);
1169   EXPECT_TRUE(activatable->GetDisplayLockContext()->IsActivatable(
1170       DisplayLockActivationReason::kAny));
1171 
1172   LockElement(*non_activatable, false);
1173 
1174   EXPECT_EQ(
1175       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 2);
1176   EXPECT_EQ(GetDocument()
1177                 .GetDisplayLockDocumentState()
1178                 .DisplayLockBlockingAllActivationCount(),
1179             1);
1180   EXPECT_FALSE(non_activatable->GetDisplayLockContext()->IsActivatable(
1181       DisplayLockActivationReason::kAny));
1182 
1183   // Now commit the lock for |non_activatable|.
1184   CommitElement(*non_activatable);
1185 
1186   EXPECT_EQ(
1187       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 1);
1188   EXPECT_EQ(GetDocument()
1189                 .GetDisplayLockDocumentState()
1190                 .DisplayLockBlockingAllActivationCount(),
1191             0);
1192 
1193   // Re-acquire the lock for |activatable| again with the activatable flag.
1194   LockElement(*activatable, true);
1195 
1196   EXPECT_EQ(
1197       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 1);
1198   EXPECT_EQ(GetDocument()
1199                 .GetDisplayLockDocumentState()
1200                 .DisplayLockBlockingAllActivationCount(),
1201             0);
1202   EXPECT_TRUE(activatable->GetDisplayLockContext()->IsActivatable(
1203       DisplayLockActivationReason::kAny));
1204 }
1205 
TEST_F(DisplayLockContextTest,ElementInTemplate)1206 TEST_F(DisplayLockContextTest, ElementInTemplate) {
1207   ResizeAndFocus();
1208   SetHtmlInnerHTML(R"HTML(
1209     <style>
1210     #child {
1211       width: 100px;
1212       height: 100px;
1213       contain: style layout paint;
1214     }
1215     #grandchild {
1216       color: blue;
1217     }
1218     #container {
1219       display: none;
1220     }
1221     </style>
1222     <body>
1223       <template id="template"><div id="child"><div id="grandchild">foo</div></div></template>
1224       <div id="container"></div>
1225     </body>
1226   )HTML");
1227 
1228   EXPECT_EQ(
1229       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 0);
1230   EXPECT_EQ(GetDocument()
1231                 .GetDisplayLockDocumentState()
1232                 .DisplayLockBlockingAllActivationCount(),
1233             0);
1234 
1235   auto* template_el =
1236       To<HTMLTemplateElement>(GetDocument().getElementById("template"));
1237   auto* child = To<Element>(template_el->content()->firstChild());
1238   EXPECT_FALSE(child->isConnected());
1239 
1240   // Try to lock an element in a template.
1241   LockElement(*child, false);
1242 
1243   EXPECT_EQ(
1244       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 0);
1245   EXPECT_EQ(GetDocument()
1246                 .GetDisplayLockDocumentState()
1247                 .DisplayLockBlockingAllActivationCount(),
1248             0);
1249   EXPECT_FALSE(child->GetDisplayLockContext());
1250 
1251   // Commit also works, but does nothing.
1252   CommitElement(*child);
1253   EXPECT_FALSE(child->GetDisplayLockContext());
1254 
1255   // Try to lock an element that was moved from a template to a document.
1256   auto* document_child =
1257       To<Element>(GetDocument().adoptNode(child, ASSERT_NO_EXCEPTION));
1258   auto* container = GetDocument().getElementById("container");
1259   container->appendChild(document_child);
1260 
1261   LockElement(*document_child, false);
1262 
1263   // These should be 0, since container is display: none, so locking its child
1264   // is not visible to style.
1265   EXPECT_EQ(
1266       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 0);
1267   EXPECT_EQ(GetDocument()
1268                 .GetDisplayLockDocumentState()
1269                 .DisplayLockBlockingAllActivationCount(),
1270             0);
1271   ASSERT_FALSE(document_child->GetDisplayLockContext());
1272 
1273   container->setAttribute(html_names::kStyleAttr, "display: block;");
1274   EXPECT_TRUE(container->NeedsStyleRecalc());
1275   UpdateAllLifecyclePhasesForTest();
1276 
1277   EXPECT_EQ(
1278       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 1);
1279   EXPECT_EQ(GetDocument()
1280                 .GetDisplayLockDocumentState()
1281                 .DisplayLockBlockingAllActivationCount(),
1282             1);
1283   ASSERT_TRUE(document_child->GetDisplayLockContext());
1284   EXPECT_TRUE(document_child->GetDisplayLockContext()->IsLocked());
1285 
1286   document_child->setAttribute(html_names::kStyleAttr,
1287                                "content-visibility: hidden; color: red;");
1288   UpdateAllLifecyclePhasesForTest();
1289 
1290   EXPECT_FALSE(document_child->NeedsStyleRecalc());
1291 
1292   // Commit will unlock the element and update the style.
1293   document_child->setAttribute(html_names::kStyleAttr, "color: red;");
1294   UpdateAllLifecyclePhasesForTest();
1295 
1296   EXPECT_FALSE(document_child->GetDisplayLockContext()->IsLocked());
1297   EXPECT_EQ(
1298       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 0);
1299   EXPECT_EQ(GetDocument()
1300                 .GetDisplayLockDocumentState()
1301                 .DisplayLockBlockingAllActivationCount(),
1302             0);
1303 
1304   EXPECT_FALSE(document_child->NeedsStyleRecalc());
1305   EXPECT_FALSE(document_child->ChildNeedsStyleRecalc());
1306   ASSERT_TRUE(document_child->GetComputedStyle());
1307   EXPECT_EQ(document_child->GetComputedStyle()->VisitedDependentColor(
1308                 GetCSSPropertyColor()),
1309             MakeRGB(255, 0, 0));
1310 
1311   auto* grandchild = GetDocument().getElementById("grandchild");
1312   EXPECT_FALSE(grandchild->NeedsStyleRecalc());
1313   EXPECT_FALSE(grandchild->ChildNeedsStyleRecalc());
1314   ASSERT_TRUE(grandchild->GetComputedStyle());
1315   EXPECT_EQ(grandchild->GetComputedStyle()->VisitedDependentColor(
1316                 GetCSSPropertyColor()),
1317             MakeRGB(0, 0, 255));
1318 }
1319 
TEST_F(DisplayLockContextTest,AncestorAllowedTouchAction)1320 TEST_F(DisplayLockContextTest, AncestorAllowedTouchAction) {
1321   SetHtmlInnerHTML(R"HTML(
1322     <style>
1323     #locked {
1324       width: 100px;
1325       height: 100px;
1326       contain: style layout paint;
1327     }
1328     </style>
1329     <div id="ancestor">
1330       <div id="handler">
1331         <div id="descendant">
1332           <div id="locked">
1333             <div id="lockedchild"></div>
1334           </div>
1335         </div>
1336       </div>
1337     </div>
1338   )HTML");
1339 
1340   auto* ancestor_element = GetDocument().getElementById("ancestor");
1341   auto* handler_element = GetDocument().getElementById("handler");
1342   auto* descendant_element = GetDocument().getElementById("descendant");
1343   auto* locked_element = GetDocument().getElementById("locked");
1344   auto* lockedchild_element = GetDocument().getElementById("lockedchild");
1345 
1346   LockElement(*locked_element, false);
1347   EXPECT_TRUE(locked_element->GetDisplayLockContext()->IsLocked());
1348 
1349   auto* ancestor_object = ancestor_element->GetLayoutObject();
1350   auto* handler_object = handler_element->GetLayoutObject();
1351   auto* descendant_object = descendant_element->GetLayoutObject();
1352   auto* locked_object = locked_element->GetLayoutObject();
1353   auto* lockedchild_object = lockedchild_element->GetLayoutObject();
1354 
1355   EXPECT_FALSE(ancestor_object->EffectiveAllowedTouchActionChanged());
1356   EXPECT_FALSE(handler_object->EffectiveAllowedTouchActionChanged());
1357   EXPECT_FALSE(descendant_object->EffectiveAllowedTouchActionChanged());
1358   EXPECT_FALSE(locked_object->EffectiveAllowedTouchActionChanged());
1359   EXPECT_FALSE(lockedchild_object->EffectiveAllowedTouchActionChanged());
1360 
1361   EXPECT_FALSE(ancestor_object->DescendantEffectiveAllowedTouchActionChanged());
1362   EXPECT_FALSE(handler_object->DescendantEffectiveAllowedTouchActionChanged());
1363   EXPECT_FALSE(
1364       descendant_object->DescendantEffectiveAllowedTouchActionChanged());
1365   EXPECT_FALSE(locked_object->DescendantEffectiveAllowedTouchActionChanged());
1366   EXPECT_FALSE(
1367       lockedchild_object->DescendantEffectiveAllowedTouchActionChanged());
1368 
1369   EXPECT_FALSE(ancestor_object->InsideBlockingTouchEventHandler());
1370   EXPECT_FALSE(handler_object->InsideBlockingTouchEventHandler());
1371   EXPECT_FALSE(descendant_object->InsideBlockingTouchEventHandler());
1372   EXPECT_FALSE(locked_object->InsideBlockingTouchEventHandler());
1373   EXPECT_FALSE(lockedchild_object->InsideBlockingTouchEventHandler());
1374 
1375   auto* callback = MakeGarbageCollected<DisplayLockEmptyEventListener>();
1376   handler_element->addEventListener(event_type_names::kTouchstart, callback);
1377 
1378   EXPECT_FALSE(ancestor_object->EffectiveAllowedTouchActionChanged());
1379   EXPECT_TRUE(handler_object->EffectiveAllowedTouchActionChanged());
1380   EXPECT_FALSE(descendant_object->EffectiveAllowedTouchActionChanged());
1381   EXPECT_FALSE(locked_object->EffectiveAllowedTouchActionChanged());
1382   EXPECT_FALSE(lockedchild_object->EffectiveAllowedTouchActionChanged());
1383 
1384   EXPECT_TRUE(ancestor_object->DescendantEffectiveAllowedTouchActionChanged());
1385   EXPECT_FALSE(handler_object->DescendantEffectiveAllowedTouchActionChanged());
1386   EXPECT_FALSE(
1387       descendant_object->DescendantEffectiveAllowedTouchActionChanged());
1388   EXPECT_FALSE(locked_object->DescendantEffectiveAllowedTouchActionChanged());
1389   EXPECT_FALSE(
1390       lockedchild_object->DescendantEffectiveAllowedTouchActionChanged());
1391 
1392   UpdateAllLifecyclePhasesForTest();
1393   EXPECT_FALSE(ancestor_object->EffectiveAllowedTouchActionChanged());
1394   EXPECT_FALSE(handler_object->EffectiveAllowedTouchActionChanged());
1395   EXPECT_FALSE(descendant_object->EffectiveAllowedTouchActionChanged());
1396   EXPECT_FALSE(locked_object->EffectiveAllowedTouchActionChanged());
1397   EXPECT_FALSE(lockedchild_object->EffectiveAllowedTouchActionChanged());
1398 
1399   EXPECT_FALSE(ancestor_object->DescendantEffectiveAllowedTouchActionChanged());
1400   EXPECT_FALSE(handler_object->DescendantEffectiveAllowedTouchActionChanged());
1401   EXPECT_FALSE(
1402       descendant_object->DescendantEffectiveAllowedTouchActionChanged());
1403   EXPECT_FALSE(locked_object->DescendantEffectiveAllowedTouchActionChanged());
1404   EXPECT_FALSE(
1405       lockedchild_object->DescendantEffectiveAllowedTouchActionChanged());
1406 
1407   EXPECT_FALSE(ancestor_object->InsideBlockingTouchEventHandler());
1408   EXPECT_TRUE(handler_object->InsideBlockingTouchEventHandler());
1409   EXPECT_TRUE(descendant_object->InsideBlockingTouchEventHandler());
1410   EXPECT_TRUE(locked_object->InsideBlockingTouchEventHandler());
1411   EXPECT_FALSE(lockedchild_object->InsideBlockingTouchEventHandler());
1412 
1413   // Manually commit the lock so that we can verify which dirty bits get
1414   // propagated.
1415   CommitElement(*locked_element, false);
1416   UnlockImmediate(locked_element->GetDisplayLockContext());
1417 
1418   EXPECT_FALSE(ancestor_object->EffectiveAllowedTouchActionChanged());
1419   EXPECT_FALSE(handler_object->EffectiveAllowedTouchActionChanged());
1420   EXPECT_FALSE(descendant_object->EffectiveAllowedTouchActionChanged());
1421   EXPECT_TRUE(locked_object->EffectiveAllowedTouchActionChanged());
1422   EXPECT_FALSE(lockedchild_object->EffectiveAllowedTouchActionChanged());
1423 
1424   EXPECT_TRUE(ancestor_object->DescendantEffectiveAllowedTouchActionChanged());
1425   EXPECT_TRUE(handler_object->DescendantEffectiveAllowedTouchActionChanged());
1426   EXPECT_TRUE(
1427       descendant_object->DescendantEffectiveAllowedTouchActionChanged());
1428   EXPECT_FALSE(locked_object->DescendantEffectiveAllowedTouchActionChanged());
1429   EXPECT_FALSE(
1430       lockedchild_object->DescendantEffectiveAllowedTouchActionChanged());
1431 
1432   EXPECT_FALSE(ancestor_object->InsideBlockingTouchEventHandler());
1433   EXPECT_TRUE(handler_object->InsideBlockingTouchEventHandler());
1434   EXPECT_TRUE(descendant_object->InsideBlockingTouchEventHandler());
1435   EXPECT_TRUE(locked_object->InsideBlockingTouchEventHandler());
1436   EXPECT_FALSE(lockedchild_object->InsideBlockingTouchEventHandler());
1437 
1438   UpdateAllLifecyclePhasesForTest();
1439   EXPECT_FALSE(ancestor_object->EffectiveAllowedTouchActionChanged());
1440   EXPECT_FALSE(handler_object->EffectiveAllowedTouchActionChanged());
1441   EXPECT_FALSE(descendant_object->EffectiveAllowedTouchActionChanged());
1442   EXPECT_FALSE(locked_object->EffectiveAllowedTouchActionChanged());
1443   EXPECT_FALSE(lockedchild_object->EffectiveAllowedTouchActionChanged());
1444 
1445   EXPECT_FALSE(ancestor_object->DescendantEffectiveAllowedTouchActionChanged());
1446   EXPECT_FALSE(handler_object->DescendantEffectiveAllowedTouchActionChanged());
1447   EXPECT_FALSE(
1448       descendant_object->DescendantEffectiveAllowedTouchActionChanged());
1449   EXPECT_FALSE(locked_object->DescendantEffectiveAllowedTouchActionChanged());
1450   EXPECT_FALSE(
1451       lockedchild_object->DescendantEffectiveAllowedTouchActionChanged());
1452 
1453   EXPECT_FALSE(ancestor_object->InsideBlockingTouchEventHandler());
1454   EXPECT_TRUE(handler_object->InsideBlockingTouchEventHandler());
1455   EXPECT_TRUE(descendant_object->InsideBlockingTouchEventHandler());
1456   EXPECT_TRUE(locked_object->InsideBlockingTouchEventHandler());
1457   EXPECT_TRUE(lockedchild_object->InsideBlockingTouchEventHandler());
1458 }
1459 
TEST_F(DisplayLockContextTest,DescendantAllowedTouchAction)1460 TEST_F(DisplayLockContextTest, DescendantAllowedTouchAction) {
1461   SetHtmlInnerHTML(R"HTML(
1462     <style>
1463     #locked {
1464       width: 100px;
1465       height: 100px;
1466       contain: style layout paint;
1467     }
1468     </style>
1469     <div id="ancestor">
1470       <div id="descendant">
1471         <div id="locked">
1472           <div id="handler"></div>
1473         </div>
1474       </div>
1475     </div>
1476   )HTML");
1477 
1478   auto* ancestor_element = GetDocument().getElementById("ancestor");
1479   auto* descendant_element = GetDocument().getElementById("descendant");
1480   auto* locked_element = GetDocument().getElementById("locked");
1481   auto* handler_element = GetDocument().getElementById("handler");
1482 
1483   LockElement(*locked_element, false);
1484   EXPECT_TRUE(locked_element->GetDisplayLockContext()->IsLocked());
1485 
1486   auto* ancestor_object = ancestor_element->GetLayoutObject();
1487   auto* descendant_object = descendant_element->GetLayoutObject();
1488   auto* locked_object = locked_element->GetLayoutObject();
1489   auto* handler_object = handler_element->GetLayoutObject();
1490 
1491   EXPECT_FALSE(ancestor_object->EffectiveAllowedTouchActionChanged());
1492   EXPECT_FALSE(descendant_object->EffectiveAllowedTouchActionChanged());
1493   EXPECT_FALSE(locked_object->EffectiveAllowedTouchActionChanged());
1494   EXPECT_FALSE(handler_object->EffectiveAllowedTouchActionChanged());
1495 
1496   EXPECT_FALSE(ancestor_object->DescendantEffectiveAllowedTouchActionChanged());
1497   EXPECT_FALSE(
1498       descendant_object->DescendantEffectiveAllowedTouchActionChanged());
1499   EXPECT_FALSE(locked_object->DescendantEffectiveAllowedTouchActionChanged());
1500   EXPECT_FALSE(handler_object->DescendantEffectiveAllowedTouchActionChanged());
1501 
1502   EXPECT_FALSE(ancestor_object->InsideBlockingTouchEventHandler());
1503   EXPECT_FALSE(descendant_object->InsideBlockingTouchEventHandler());
1504   EXPECT_FALSE(locked_object->InsideBlockingTouchEventHandler());
1505   EXPECT_FALSE(handler_object->InsideBlockingTouchEventHandler());
1506 
1507   auto* callback = MakeGarbageCollected<DisplayLockEmptyEventListener>();
1508   handler_element->addEventListener(event_type_names::kTouchstart, callback);
1509 
1510   EXPECT_FALSE(ancestor_object->EffectiveAllowedTouchActionChanged());
1511   EXPECT_FALSE(descendant_object->EffectiveAllowedTouchActionChanged());
1512   EXPECT_FALSE(locked_object->EffectiveAllowedTouchActionChanged());
1513   EXPECT_TRUE(handler_object->EffectiveAllowedTouchActionChanged());
1514 
1515   EXPECT_FALSE(ancestor_object->DescendantEffectiveAllowedTouchActionChanged());
1516   EXPECT_FALSE(
1517       descendant_object->DescendantEffectiveAllowedTouchActionChanged());
1518   EXPECT_TRUE(locked_object->DescendantEffectiveAllowedTouchActionChanged());
1519   EXPECT_FALSE(handler_object->DescendantEffectiveAllowedTouchActionChanged());
1520 
1521   UpdateAllLifecyclePhasesForTest();
1522   EXPECT_FALSE(ancestor_object->EffectiveAllowedTouchActionChanged());
1523   EXPECT_FALSE(descendant_object->EffectiveAllowedTouchActionChanged());
1524   EXPECT_FALSE(locked_object->EffectiveAllowedTouchActionChanged());
1525   EXPECT_TRUE(handler_object->EffectiveAllowedTouchActionChanged());
1526 
1527   EXPECT_FALSE(ancestor_object->DescendantEffectiveAllowedTouchActionChanged());
1528   EXPECT_FALSE(
1529       descendant_object->DescendantEffectiveAllowedTouchActionChanged());
1530   EXPECT_TRUE(locked_object->DescendantEffectiveAllowedTouchActionChanged());
1531   EXPECT_FALSE(handler_object->DescendantEffectiveAllowedTouchActionChanged());
1532 
1533   EXPECT_FALSE(ancestor_object->InsideBlockingTouchEventHandler());
1534   EXPECT_FALSE(descendant_object->InsideBlockingTouchEventHandler());
1535   EXPECT_FALSE(locked_object->InsideBlockingTouchEventHandler());
1536   EXPECT_FALSE(handler_object->InsideBlockingTouchEventHandler());
1537 
1538   // Do the same check again. For now, nothing is expected to change. However,
1539   // when we separate self and child layout, then some flags would be different.
1540   UpdateAllLifecyclePhasesForTest();
1541   EXPECT_FALSE(ancestor_object->EffectiveAllowedTouchActionChanged());
1542   EXPECT_FALSE(descendant_object->EffectiveAllowedTouchActionChanged());
1543   EXPECT_FALSE(locked_object->EffectiveAllowedTouchActionChanged());
1544   EXPECT_TRUE(handler_object->EffectiveAllowedTouchActionChanged());
1545 
1546   EXPECT_FALSE(ancestor_object->DescendantEffectiveAllowedTouchActionChanged());
1547   EXPECT_FALSE(
1548       descendant_object->DescendantEffectiveAllowedTouchActionChanged());
1549   EXPECT_TRUE(locked_object->DescendantEffectiveAllowedTouchActionChanged());
1550   EXPECT_FALSE(handler_object->DescendantEffectiveAllowedTouchActionChanged());
1551 
1552   EXPECT_FALSE(ancestor_object->InsideBlockingTouchEventHandler());
1553   EXPECT_FALSE(descendant_object->InsideBlockingTouchEventHandler());
1554   EXPECT_FALSE(locked_object->InsideBlockingTouchEventHandler());
1555   EXPECT_FALSE(handler_object->InsideBlockingTouchEventHandler());
1556 
1557   // Manually commit the lock so that we can verify which dirty bits get
1558   // propagated.
1559   CommitElement(*locked_element, false);
1560   UnlockImmediate(locked_element->GetDisplayLockContext());
1561 
1562   EXPECT_FALSE(ancestor_object->EffectiveAllowedTouchActionChanged());
1563   EXPECT_FALSE(descendant_object->EffectiveAllowedTouchActionChanged());
1564   EXPECT_TRUE(locked_object->EffectiveAllowedTouchActionChanged());
1565   EXPECT_TRUE(handler_object->EffectiveAllowedTouchActionChanged());
1566 
1567   EXPECT_TRUE(ancestor_object->DescendantEffectiveAllowedTouchActionChanged());
1568   EXPECT_TRUE(
1569       descendant_object->DescendantEffectiveAllowedTouchActionChanged());
1570   EXPECT_TRUE(locked_object->DescendantEffectiveAllowedTouchActionChanged());
1571   EXPECT_FALSE(handler_object->DescendantEffectiveAllowedTouchActionChanged());
1572 
1573   EXPECT_FALSE(ancestor_object->InsideBlockingTouchEventHandler());
1574   EXPECT_FALSE(descendant_object->InsideBlockingTouchEventHandler());
1575   EXPECT_FALSE(locked_object->InsideBlockingTouchEventHandler());
1576   EXPECT_FALSE(handler_object->InsideBlockingTouchEventHandler());
1577 
1578   UpdateAllLifecyclePhasesForTest();
1579   EXPECT_FALSE(ancestor_object->EffectiveAllowedTouchActionChanged());
1580   EXPECT_FALSE(descendant_object->EffectiveAllowedTouchActionChanged());
1581   EXPECT_FALSE(locked_object->EffectiveAllowedTouchActionChanged());
1582   EXPECT_FALSE(handler_object->EffectiveAllowedTouchActionChanged());
1583 
1584   EXPECT_FALSE(ancestor_object->DescendantEffectiveAllowedTouchActionChanged());
1585   EXPECT_FALSE(
1586       descendant_object->DescendantEffectiveAllowedTouchActionChanged());
1587   EXPECT_FALSE(locked_object->DescendantEffectiveAllowedTouchActionChanged());
1588   EXPECT_FALSE(handler_object->DescendantEffectiveAllowedTouchActionChanged());
1589 
1590   EXPECT_FALSE(ancestor_object->InsideBlockingTouchEventHandler());
1591   EXPECT_FALSE(descendant_object->InsideBlockingTouchEventHandler());
1592   EXPECT_FALSE(locked_object->InsideBlockingTouchEventHandler());
1593   EXPECT_TRUE(handler_object->InsideBlockingTouchEventHandler());
1594 }
1595 
TEST_F(DisplayLockContextTest,AncestorWheelEventHandler)1596 TEST_F(DisplayLockContextTest, AncestorWheelEventHandler) {
1597   base::test::ScopedFeatureList scoped_feature_list;
1598   scoped_feature_list.InitAndEnableFeature(::features::kWheelEventRegions);
1599   SetHtmlInnerHTML(R"HTML(
1600     <style>
1601     #locked {
1602       width: 100px;
1603       height: 100px;
1604       contain: style layout paint;
1605     }
1606     </style>
1607     <div id="ancestor">
1608       <div id="handler">
1609         <div id="descendant">
1610           <div id="locked">
1611             <div id="lockedchild"></div>
1612           </div>
1613         </div>
1614       </div>
1615     </div>
1616   )HTML");
1617 
1618   auto* ancestor_element = GetDocument().getElementById("ancestor");
1619   auto* handler_element = GetDocument().getElementById("handler");
1620   auto* descendant_element = GetDocument().getElementById("descendant");
1621   auto* locked_element = GetDocument().getElementById("locked");
1622   auto* lockedchild_element = GetDocument().getElementById("lockedchild");
1623 
1624   LockElement(*locked_element, false);
1625   EXPECT_TRUE(locked_element->GetDisplayLockContext()->IsLocked());
1626 
1627   auto* ancestor_object = ancestor_element->GetLayoutObject();
1628   auto* handler_object = handler_element->GetLayoutObject();
1629   auto* descendant_object = descendant_element->GetLayoutObject();
1630   auto* locked_object = locked_element->GetLayoutObject();
1631   auto* lockedchild_object = lockedchild_element->GetLayoutObject();
1632 
1633   EXPECT_FALSE(ancestor_object->BlockingWheelEventHandlerChanged());
1634   EXPECT_FALSE(handler_object->BlockingWheelEventHandlerChanged());
1635   EXPECT_FALSE(descendant_object->BlockingWheelEventHandlerChanged());
1636   EXPECT_FALSE(locked_object->BlockingWheelEventHandlerChanged());
1637   EXPECT_FALSE(lockedchild_object->BlockingWheelEventHandlerChanged());
1638 
1639   EXPECT_FALSE(ancestor_object->DescendantBlockingWheelEventHandlerChanged());
1640   EXPECT_FALSE(handler_object->DescendantBlockingWheelEventHandlerChanged());
1641   EXPECT_FALSE(descendant_object->DescendantBlockingWheelEventHandlerChanged());
1642   EXPECT_FALSE(locked_object->DescendantBlockingWheelEventHandlerChanged());
1643   EXPECT_FALSE(
1644       lockedchild_object->DescendantBlockingWheelEventHandlerChanged());
1645 
1646   EXPECT_FALSE(ancestor_object->InsideBlockingWheelEventHandler());
1647   EXPECT_FALSE(handler_object->InsideBlockingWheelEventHandler());
1648   EXPECT_FALSE(descendant_object->InsideBlockingWheelEventHandler());
1649   EXPECT_FALSE(locked_object->InsideBlockingWheelEventHandler());
1650   EXPECT_FALSE(lockedchild_object->InsideBlockingWheelEventHandler());
1651 
1652   auto* callback = MakeGarbageCollected<DisplayLockEmptyEventListener>();
1653   handler_element->addEventListener(event_type_names::kWheel, callback);
1654 
1655   EXPECT_FALSE(ancestor_object->BlockingWheelEventHandlerChanged());
1656   EXPECT_TRUE(handler_object->BlockingWheelEventHandlerChanged());
1657   EXPECT_FALSE(descendant_object->BlockingWheelEventHandlerChanged());
1658   EXPECT_FALSE(locked_object->BlockingWheelEventHandlerChanged());
1659   EXPECT_FALSE(lockedchild_object->BlockingWheelEventHandlerChanged());
1660 
1661   EXPECT_TRUE(ancestor_object->DescendantBlockingWheelEventHandlerChanged());
1662   EXPECT_FALSE(handler_object->DescendantBlockingWheelEventHandlerChanged());
1663   EXPECT_FALSE(descendant_object->DescendantBlockingWheelEventHandlerChanged());
1664   EXPECT_FALSE(locked_object->DescendantBlockingWheelEventHandlerChanged());
1665   EXPECT_FALSE(
1666       lockedchild_object->DescendantBlockingWheelEventHandlerChanged());
1667 
1668   UpdateAllLifecyclePhasesForTest();
1669   EXPECT_FALSE(ancestor_object->BlockingWheelEventHandlerChanged());
1670   EXPECT_FALSE(handler_object->BlockingWheelEventHandlerChanged());
1671   EXPECT_FALSE(descendant_object->BlockingWheelEventHandlerChanged());
1672   EXPECT_FALSE(locked_object->BlockingWheelEventHandlerChanged());
1673   EXPECT_FALSE(lockedchild_object->BlockingWheelEventHandlerChanged());
1674 
1675   EXPECT_FALSE(ancestor_object->DescendantBlockingWheelEventHandlerChanged());
1676   EXPECT_FALSE(handler_object->DescendantBlockingWheelEventHandlerChanged());
1677   EXPECT_FALSE(descendant_object->DescendantBlockingWheelEventHandlerChanged());
1678   EXPECT_FALSE(locked_object->DescendantBlockingWheelEventHandlerChanged());
1679   EXPECT_FALSE(
1680       lockedchild_object->DescendantBlockingWheelEventHandlerChanged());
1681 
1682   EXPECT_FALSE(ancestor_object->InsideBlockingWheelEventHandler());
1683   EXPECT_TRUE(handler_object->InsideBlockingWheelEventHandler());
1684   EXPECT_TRUE(descendant_object->InsideBlockingWheelEventHandler());
1685   EXPECT_TRUE(locked_object->InsideBlockingWheelEventHandler());
1686   EXPECT_FALSE(lockedchild_object->InsideBlockingWheelEventHandler());
1687 
1688   // Manually commit the lock so that we can verify which dirty bits get
1689   // propagated.
1690   CommitElement(*locked_element, false);
1691   UnlockImmediate(locked_element->GetDisplayLockContext());
1692 
1693   EXPECT_FALSE(ancestor_object->BlockingWheelEventHandlerChanged());
1694   EXPECT_FALSE(handler_object->BlockingWheelEventHandlerChanged());
1695   EXPECT_FALSE(descendant_object->BlockingWheelEventHandlerChanged());
1696   EXPECT_TRUE(locked_object->BlockingWheelEventHandlerChanged());
1697   EXPECT_FALSE(lockedchild_object->BlockingWheelEventHandlerChanged());
1698 
1699   EXPECT_TRUE(ancestor_object->DescendantBlockingWheelEventHandlerChanged());
1700   EXPECT_TRUE(handler_object->DescendantBlockingWheelEventHandlerChanged());
1701   EXPECT_TRUE(descendant_object->DescendantBlockingWheelEventHandlerChanged());
1702   EXPECT_FALSE(locked_object->DescendantBlockingWheelEventHandlerChanged());
1703   EXPECT_FALSE(
1704       lockedchild_object->DescendantBlockingWheelEventHandlerChanged());
1705 
1706   EXPECT_FALSE(ancestor_object->InsideBlockingWheelEventHandler());
1707   EXPECT_TRUE(handler_object->InsideBlockingWheelEventHandler());
1708   EXPECT_TRUE(descendant_object->InsideBlockingWheelEventHandler());
1709   EXPECT_TRUE(locked_object->InsideBlockingWheelEventHandler());
1710   EXPECT_FALSE(lockedchild_object->InsideBlockingWheelEventHandler());
1711 
1712   UpdateAllLifecyclePhasesForTest();
1713   EXPECT_FALSE(ancestor_object->BlockingWheelEventHandlerChanged());
1714   EXPECT_FALSE(handler_object->BlockingWheelEventHandlerChanged());
1715   EXPECT_FALSE(descendant_object->BlockingWheelEventHandlerChanged());
1716   EXPECT_FALSE(locked_object->BlockingWheelEventHandlerChanged());
1717   EXPECT_FALSE(lockedchild_object->BlockingWheelEventHandlerChanged());
1718 
1719   EXPECT_FALSE(ancestor_object->DescendantBlockingWheelEventHandlerChanged());
1720   EXPECT_FALSE(handler_object->DescendantBlockingWheelEventHandlerChanged());
1721   EXPECT_FALSE(descendant_object->DescendantBlockingWheelEventHandlerChanged());
1722   EXPECT_FALSE(locked_object->DescendantBlockingWheelEventHandlerChanged());
1723   EXPECT_FALSE(
1724       lockedchild_object->DescendantBlockingWheelEventHandlerChanged());
1725 
1726   EXPECT_FALSE(ancestor_object->InsideBlockingWheelEventHandler());
1727   EXPECT_TRUE(handler_object->InsideBlockingWheelEventHandler());
1728   EXPECT_TRUE(descendant_object->InsideBlockingWheelEventHandler());
1729   EXPECT_TRUE(locked_object->InsideBlockingWheelEventHandler());
1730   EXPECT_TRUE(lockedchild_object->InsideBlockingWheelEventHandler());
1731 }
1732 
TEST_F(DisplayLockContextTest,DescendantWheelEventHandler)1733 TEST_F(DisplayLockContextTest, DescendantWheelEventHandler) {
1734   base::test::ScopedFeatureList scoped_feature_list;
1735   scoped_feature_list.InitAndEnableFeature(::features::kWheelEventRegions);
1736   SetHtmlInnerHTML(R"HTML(
1737     <style>
1738     #locked {
1739       width: 100px;
1740       height: 100px;
1741       contain: style layout paint;
1742     }
1743     </style>
1744     <div id="ancestor">
1745       <div id="descendant">
1746         <div id="locked">
1747           <div id="handler"></div>
1748         </div>
1749       </div>
1750     </div>
1751   )HTML");
1752 
1753   auto* ancestor_element = GetDocument().getElementById("ancestor");
1754   auto* descendant_element = GetDocument().getElementById("descendant");
1755   auto* locked_element = GetDocument().getElementById("locked");
1756   auto* handler_element = GetDocument().getElementById("handler");
1757 
1758   LockElement(*locked_element, false);
1759   EXPECT_TRUE(locked_element->GetDisplayLockContext()->IsLocked());
1760 
1761   auto* ancestor_object = ancestor_element->GetLayoutObject();
1762   auto* descendant_object = descendant_element->GetLayoutObject();
1763   auto* locked_object = locked_element->GetLayoutObject();
1764   auto* handler_object = handler_element->GetLayoutObject();
1765 
1766   EXPECT_FALSE(ancestor_object->BlockingWheelEventHandlerChanged());
1767   EXPECT_FALSE(descendant_object->BlockingWheelEventHandlerChanged());
1768   EXPECT_FALSE(locked_object->BlockingWheelEventHandlerChanged());
1769   EXPECT_FALSE(handler_object->BlockingWheelEventHandlerChanged());
1770 
1771   EXPECT_FALSE(ancestor_object->DescendantBlockingWheelEventHandlerChanged());
1772   EXPECT_FALSE(descendant_object->DescendantBlockingWheelEventHandlerChanged());
1773   EXPECT_FALSE(locked_object->DescendantBlockingWheelEventHandlerChanged());
1774   EXPECT_FALSE(handler_object->DescendantBlockingWheelEventHandlerChanged());
1775 
1776   EXPECT_FALSE(ancestor_object->InsideBlockingWheelEventHandler());
1777   EXPECT_FALSE(descendant_object->InsideBlockingWheelEventHandler());
1778   EXPECT_FALSE(locked_object->InsideBlockingWheelEventHandler());
1779   EXPECT_FALSE(handler_object->InsideBlockingWheelEventHandler());
1780 
1781   auto* callback = MakeGarbageCollected<DisplayLockEmptyEventListener>();
1782   handler_element->addEventListener(event_type_names::kWheel, callback);
1783 
1784   EXPECT_FALSE(ancestor_object->BlockingWheelEventHandlerChanged());
1785   EXPECT_FALSE(descendant_object->BlockingWheelEventHandlerChanged());
1786   EXPECT_FALSE(locked_object->BlockingWheelEventHandlerChanged());
1787   EXPECT_TRUE(handler_object->BlockingWheelEventHandlerChanged());
1788 
1789   EXPECT_FALSE(ancestor_object->DescendantBlockingWheelEventHandlerChanged());
1790   EXPECT_FALSE(descendant_object->DescendantBlockingWheelEventHandlerChanged());
1791   EXPECT_TRUE(locked_object->DescendantBlockingWheelEventHandlerChanged());
1792   EXPECT_FALSE(handler_object->DescendantBlockingWheelEventHandlerChanged());
1793 
1794   UpdateAllLifecyclePhasesForTest();
1795   EXPECT_FALSE(ancestor_object->BlockingWheelEventHandlerChanged());
1796   EXPECT_FALSE(descendant_object->BlockingWheelEventHandlerChanged());
1797   EXPECT_FALSE(locked_object->BlockingWheelEventHandlerChanged());
1798   EXPECT_TRUE(handler_object->BlockingWheelEventHandlerChanged());
1799 
1800   EXPECT_FALSE(ancestor_object->DescendantBlockingWheelEventHandlerChanged());
1801   EXPECT_FALSE(descendant_object->DescendantBlockingWheelEventHandlerChanged());
1802   EXPECT_TRUE(locked_object->DescendantBlockingWheelEventHandlerChanged());
1803   EXPECT_FALSE(handler_object->DescendantBlockingWheelEventHandlerChanged());
1804 
1805   EXPECT_FALSE(ancestor_object->InsideBlockingWheelEventHandler());
1806   EXPECT_FALSE(descendant_object->InsideBlockingWheelEventHandler());
1807   EXPECT_FALSE(locked_object->InsideBlockingWheelEventHandler());
1808   EXPECT_FALSE(handler_object->InsideBlockingWheelEventHandler());
1809 
1810   // Do the same check again. For now, nothing is expected to change. However,
1811   // when we separate self and child layout, then some flags would be different.
1812   UpdateAllLifecyclePhasesForTest();
1813   EXPECT_FALSE(ancestor_object->BlockingWheelEventHandlerChanged());
1814   EXPECT_FALSE(descendant_object->BlockingWheelEventHandlerChanged());
1815   EXPECT_FALSE(locked_object->BlockingWheelEventHandlerChanged());
1816   EXPECT_TRUE(handler_object->BlockingWheelEventHandlerChanged());
1817 
1818   EXPECT_FALSE(ancestor_object->DescendantBlockingWheelEventHandlerChanged());
1819   EXPECT_FALSE(descendant_object->DescendantBlockingWheelEventHandlerChanged());
1820   EXPECT_TRUE(locked_object->DescendantBlockingWheelEventHandlerChanged());
1821   EXPECT_FALSE(handler_object->DescendantBlockingWheelEventHandlerChanged());
1822 
1823   EXPECT_FALSE(ancestor_object->InsideBlockingWheelEventHandler());
1824   EXPECT_FALSE(descendant_object->InsideBlockingWheelEventHandler());
1825   EXPECT_FALSE(locked_object->InsideBlockingWheelEventHandler());
1826   EXPECT_FALSE(handler_object->InsideBlockingWheelEventHandler());
1827 
1828   // Manually commit the lock so that we can verify which dirty bits get
1829   // propagated.
1830   CommitElement(*locked_element, false);
1831   UnlockImmediate(locked_element->GetDisplayLockContext());
1832 
1833   EXPECT_FALSE(ancestor_object->BlockingWheelEventHandlerChanged());
1834   EXPECT_FALSE(descendant_object->BlockingWheelEventHandlerChanged());
1835   EXPECT_TRUE(locked_object->BlockingWheelEventHandlerChanged());
1836   EXPECT_TRUE(handler_object->BlockingWheelEventHandlerChanged());
1837 
1838   EXPECT_TRUE(ancestor_object->DescendantBlockingWheelEventHandlerChanged());
1839   EXPECT_TRUE(descendant_object->DescendantBlockingWheelEventHandlerChanged());
1840   EXPECT_TRUE(locked_object->DescendantBlockingWheelEventHandlerChanged());
1841   EXPECT_FALSE(handler_object->DescendantBlockingWheelEventHandlerChanged());
1842 
1843   EXPECT_FALSE(ancestor_object->InsideBlockingWheelEventHandler());
1844   EXPECT_FALSE(descendant_object->InsideBlockingWheelEventHandler());
1845   EXPECT_FALSE(locked_object->InsideBlockingWheelEventHandler());
1846   EXPECT_FALSE(handler_object->InsideBlockingWheelEventHandler());
1847 
1848   UpdateAllLifecyclePhasesForTest();
1849   EXPECT_FALSE(ancestor_object->BlockingWheelEventHandlerChanged());
1850   EXPECT_FALSE(descendant_object->BlockingWheelEventHandlerChanged());
1851   EXPECT_FALSE(locked_object->BlockingWheelEventHandlerChanged());
1852   EXPECT_FALSE(handler_object->BlockingWheelEventHandlerChanged());
1853 
1854   EXPECT_FALSE(ancestor_object->DescendantBlockingWheelEventHandlerChanged());
1855   EXPECT_FALSE(descendant_object->DescendantBlockingWheelEventHandlerChanged());
1856   EXPECT_FALSE(locked_object->DescendantBlockingWheelEventHandlerChanged());
1857   EXPECT_FALSE(handler_object->DescendantBlockingWheelEventHandlerChanged());
1858 
1859   EXPECT_FALSE(ancestor_object->InsideBlockingWheelEventHandler());
1860   EXPECT_FALSE(descendant_object->InsideBlockingWheelEventHandler());
1861   EXPECT_FALSE(locked_object->InsideBlockingWheelEventHandler());
1862   EXPECT_TRUE(handler_object->InsideBlockingWheelEventHandler());
1863 }
1864 
TEST_F(DisplayLockContextTest,DescendantNeedsPaintPropertyUpdateBlocked)1865 TEST_F(DisplayLockContextTest, DescendantNeedsPaintPropertyUpdateBlocked) {
1866   SetHtmlInnerHTML(R"HTML(
1867     <style>
1868     #locked {
1869       width: 100px;
1870       height: 100px;
1871       contain: style layout paint;
1872     }
1873     </style>
1874     <div id="ancestor">
1875       <div id="descendant">
1876         <div id="locked">
1877           <div id="handler"></div>
1878         </div>
1879       </div>
1880     </div>
1881   )HTML");
1882 
1883   auto* ancestor_element = GetDocument().getElementById("ancestor");
1884   auto* descendant_element = GetDocument().getElementById("descendant");
1885   auto* locked_element = GetDocument().getElementById("locked");
1886   auto* handler_element = GetDocument().getElementById("handler");
1887 
1888   LockElement(*locked_element, false);
1889   EXPECT_TRUE(locked_element->GetDisplayLockContext()->IsLocked());
1890 
1891   auto* ancestor_object = ancestor_element->GetLayoutObject();
1892   auto* descendant_object = descendant_element->GetLayoutObject();
1893   auto* locked_object = locked_element->GetLayoutObject();
1894   auto* handler_object = handler_element->GetLayoutObject();
1895 
1896   EXPECT_FALSE(ancestor_object->NeedsPaintPropertyUpdate());
1897   EXPECT_FALSE(descendant_object->NeedsPaintPropertyUpdate());
1898   EXPECT_FALSE(locked_object->NeedsPaintPropertyUpdate());
1899   EXPECT_FALSE(handler_object->NeedsPaintPropertyUpdate());
1900 
1901   EXPECT_FALSE(ancestor_object->DescendantNeedsPaintPropertyUpdate());
1902   EXPECT_FALSE(descendant_object->DescendantNeedsPaintPropertyUpdate());
1903   EXPECT_FALSE(locked_object->DescendantNeedsPaintPropertyUpdate());
1904   EXPECT_FALSE(handler_object->DescendantNeedsPaintPropertyUpdate());
1905 
1906   handler_object->SetNeedsPaintPropertyUpdate();
1907 
1908   EXPECT_FALSE(ancestor_object->NeedsPaintPropertyUpdate());
1909   EXPECT_FALSE(descendant_object->NeedsPaintPropertyUpdate());
1910   EXPECT_FALSE(locked_object->NeedsPaintPropertyUpdate());
1911   EXPECT_TRUE(handler_object->NeedsPaintPropertyUpdate());
1912 
1913   EXPECT_TRUE(ancestor_object->DescendantNeedsPaintPropertyUpdate());
1914   EXPECT_TRUE(descendant_object->DescendantNeedsPaintPropertyUpdate());
1915   EXPECT_TRUE(locked_object->DescendantNeedsPaintPropertyUpdate());
1916   EXPECT_FALSE(handler_object->DescendantNeedsPaintPropertyUpdate());
1917 
1918   UpdateAllLifecyclePhasesForTest();
1919 
1920   EXPECT_FALSE(ancestor_object->NeedsPaintPropertyUpdate());
1921   EXPECT_FALSE(descendant_object->NeedsPaintPropertyUpdate());
1922   EXPECT_FALSE(locked_object->NeedsPaintPropertyUpdate());
1923   EXPECT_TRUE(handler_object->NeedsPaintPropertyUpdate());
1924 
1925   EXPECT_FALSE(ancestor_object->DescendantNeedsPaintPropertyUpdate());
1926   EXPECT_FALSE(descendant_object->DescendantNeedsPaintPropertyUpdate());
1927   EXPECT_TRUE(locked_object->DescendantNeedsPaintPropertyUpdate());
1928   EXPECT_FALSE(handler_object->DescendantNeedsPaintPropertyUpdate());
1929 
1930   locked_object->SetShouldCheckForPaintInvalidationWithoutGeometryChange();
1931   UpdateAllLifecyclePhasesForTest();
1932 
1933   EXPECT_FALSE(ancestor_object->NeedsPaintPropertyUpdate());
1934   EXPECT_FALSE(descendant_object->NeedsPaintPropertyUpdate());
1935   EXPECT_FALSE(locked_object->NeedsPaintPropertyUpdate());
1936   EXPECT_TRUE(handler_object->NeedsPaintPropertyUpdate());
1937 
1938   EXPECT_FALSE(ancestor_object->DescendantNeedsPaintPropertyUpdate());
1939   EXPECT_FALSE(descendant_object->DescendantNeedsPaintPropertyUpdate());
1940   EXPECT_TRUE(locked_object->DescendantNeedsPaintPropertyUpdate());
1941   EXPECT_FALSE(handler_object->DescendantNeedsPaintPropertyUpdate());
1942 
1943   // Manually commit the lock so that we can verify which dirty bits get
1944   // propagated.
1945   CommitElement(*locked_element, false);
1946   UnlockImmediate(locked_element->GetDisplayLockContext());
1947 
1948   EXPECT_FALSE(ancestor_object->NeedsPaintPropertyUpdate());
1949   EXPECT_FALSE(descendant_object->NeedsPaintPropertyUpdate());
1950   EXPECT_TRUE(locked_object->NeedsPaintPropertyUpdate());
1951   EXPECT_TRUE(handler_object->NeedsPaintPropertyUpdate());
1952 
1953   EXPECT_TRUE(ancestor_object->DescendantNeedsPaintPropertyUpdate());
1954   EXPECT_TRUE(descendant_object->DescendantNeedsPaintPropertyUpdate());
1955   EXPECT_TRUE(locked_object->DescendantNeedsPaintPropertyUpdate());
1956   EXPECT_FALSE(handler_object->DescendantNeedsPaintPropertyUpdate());
1957 
1958   UpdateAllLifecyclePhasesForTest();
1959 
1960   EXPECT_FALSE(ancestor_object->NeedsPaintPropertyUpdate());
1961   EXPECT_FALSE(descendant_object->NeedsPaintPropertyUpdate());
1962   EXPECT_FALSE(locked_object->NeedsPaintPropertyUpdate());
1963   EXPECT_FALSE(handler_object->NeedsPaintPropertyUpdate());
1964 
1965   EXPECT_FALSE(ancestor_object->DescendantNeedsPaintPropertyUpdate());
1966   EXPECT_FALSE(descendant_object->DescendantNeedsPaintPropertyUpdate());
1967   EXPECT_FALSE(locked_object->DescendantNeedsPaintPropertyUpdate());
1968   EXPECT_FALSE(handler_object->DescendantNeedsPaintPropertyUpdate());
1969 }
1970 
1971 class DisplayLockContextRenderingTest
1972     : public RenderingTest,
1973       private ScopedCSSContentVisibilityHiddenMatchableForTest {
1974  public:
DisplayLockContextRenderingTest()1975   DisplayLockContextRenderingTest()
1976       : RenderingTest(MakeGarbageCollected<SingleChildLocalFrameClient>()),
1977         ScopedCSSContentVisibilityHiddenMatchableForTest(true) {}
1978 
SetUp()1979   void SetUp() override {
1980     EnableCompositing();
1981     RenderingTest::SetUp();
1982   }
1983 
IsObservingLifecycle(DisplayLockContext * context) const1984   bool IsObservingLifecycle(DisplayLockContext* context) const {
1985     return context->is_registered_for_lifecycle_notifications_;
1986   }
DescendantDependentFlagUpdateWasBlocked(DisplayLockContext * context) const1987   bool DescendantDependentFlagUpdateWasBlocked(
1988       DisplayLockContext* context) const {
1989     return context->needs_compositing_dependent_flag_update_;
1990   }
LockImmediate(DisplayLockContext * context)1991   void LockImmediate(DisplayLockContext* context) {
1992     context->SetRequestedState(EContentVisibility::kHidden);
1993   }
RunStartOfLifecycleTasks()1994   void RunStartOfLifecycleTasks() {
1995     auto start_of_lifecycle_tasks =
1996         GetDocument().View()->TakeStartOfLifecycleTasksForTest();
1997     for (auto& task : start_of_lifecycle_tasks)
1998       std::move(task).Run();
1999   }
GetScopedForcedUpdate(const Node * node,bool include_self=false)2000   DisplayLockUtilities::ScopedForcedUpdate GetScopedForcedUpdate(
2001       const Node* node,
2002       bool include_self = false) {
2003     return DisplayLockUtilities::ScopedForcedUpdate(node, include_self);
2004   }
2005 };
2006 
TEST_F(DisplayLockContextRenderingTest,FrameDocumentRemovedWhileAcquire)2007 TEST_F(DisplayLockContextRenderingTest, FrameDocumentRemovedWhileAcquire) {
2008   SetHtmlInnerHTML(R"HTML(
2009     <iframe id="frame"></iframe>
2010   )HTML");
2011   SetChildFrameHTML(R"HTML(
2012     <style>
2013       div {
2014         contain: style layout;
2015       }
2016     </style>
2017     <div id="target"></target>
2018   )HTML");
2019 
2020   auto* target = ChildDocument().getElementById("target");
2021   GetDocument().getElementById("frame")->remove();
2022 
2023   LockImmediate(&target->EnsureDisplayLockContext());
2024 }
2025 
TEST_F(DisplayLockContextRenderingTest,VisualOverflowCalculateOnChildPaintLayer)2026 TEST_F(DisplayLockContextRenderingTest,
2027        VisualOverflowCalculateOnChildPaintLayer) {
2028   SetHtmlInnerHTML(R"HTML(
2029     <style>
2030       .hidden { content-visibility: hidden }
2031       .paint_layer { contain: paint }
2032       .composited { will-change: transform }
2033     </style>
2034     <div id=lockable class=paint_layer>
2035       <div id=parent class=paint_layer>
2036         <div id=child class=paint_layer>
2037           <span>content</span>
2038           <span>content</span>
2039           <span>content</span>
2040         </div>
2041       </div>
2042     </div>
2043   )HTML");
2044 
2045   auto* parent = GetDocument().getElementById("parent");
2046   auto* parent_box = parent->GetLayoutBoxModelObject();
2047   ASSERT_TRUE(parent_box);
2048   EXPECT_TRUE(parent_box->Layer());
2049   EXPECT_TRUE(parent_box->HasSelfPaintingLayer());
2050 
2051   // Lock the container.
2052   auto* lockable = GetDocument().getElementById("lockable");
2053   lockable->classList().Add("hidden");
2054   UpdateAllLifecyclePhasesForTest();
2055 
2056   auto* child_layer = GetPaintLayerByElementId("child");
2057   child_layer->SetNeedsVisualOverflowRecalc();
2058   EXPECT_TRUE(child_layer->NeedsVisualOverflowRecalc());
2059 
2060   // The following should not crash/DCHECK.
2061   UpdateAllLifecyclePhasesForTest();
2062 
2063   // Verify that the display lock knows that the descendant dependent flags
2064   // update was blocked.
2065   ASSERT_TRUE(lockable->GetDisplayLockContext());
2066   EXPECT_TRUE(DescendantDependentFlagUpdateWasBlocked(
2067       lockable->GetDisplayLockContext()));
2068   EXPECT_TRUE(child_layer->NeedsVisualOverflowRecalc());
2069 
2070   // After unlocking, we should process the pending visual overflow recalc.
2071   lockable->classList().Remove("hidden");
2072   UpdateAllLifecyclePhasesForTest();
2073 
2074   EXPECT_FALSE(child_layer->NeedsVisualOverflowRecalc());
2075 }
2076 
TEST_F(DisplayLockContextRenderingTest,FloatChildLocked)2077 TEST_F(DisplayLockContextRenderingTest, FloatChildLocked) {
2078   SetHtmlInnerHTML(R"HTML(
2079     <style>
2080       .hidden { content-visibility: hidden }
2081       #floating { float: left; width: 100px; height: 100px }
2082     </style>
2083     <div id=lockable style="width: 200px; height: 50px; position: absolute">
2084       <div id=floating></div>
2085     </div>
2086   )HTML");
2087 
2088   auto* lockable = GetDocument().getElementById("lockable");
2089   auto* lockable_box = lockable->GetLayoutBox();
2090   auto* floating = GetDocument().getElementById("floating");
2091   EXPECT_EQ(LayoutRect(0, 0, 200, 100), lockable_box->VisualOverflowRect());
2092   EXPECT_EQ(LayoutRect(0, 0, 200, 100), lockable_box->LayoutOverflowRect());
2093 
2094   lockable->classList().Add("hidden");
2095   UpdateAllLifecyclePhasesForTest();
2096 
2097   // Verify that the display lock knows that the descendant dependent flags
2098   // update was blocked.
2099   ASSERT_TRUE(lockable->GetDisplayLockContext());
2100   EXPECT_TRUE(DescendantDependentFlagUpdateWasBlocked(
2101       lockable->GetDisplayLockContext()));
2102   EXPECT_EQ(LayoutRect(0, 0, 200, 50), lockable_box->VisualOverflowRect());
2103   EXPECT_EQ(LayoutRect(0, 0, 200, 50), lockable_box->LayoutOverflowRect());
2104 
2105   floating->setAttribute(html_names::kStyleAttr, "height: 200px");
2106   // The following should not crash/DCHECK.
2107   UpdateAllLifecyclePhasesForTest();
2108 
2109   ASSERT_TRUE(lockable->GetDisplayLockContext());
2110   EXPECT_TRUE(DescendantDependentFlagUpdateWasBlocked(
2111       lockable->GetDisplayLockContext()));
2112   EXPECT_EQ(LayoutRect(0, 0, 200, 50), lockable_box->VisualOverflowRect());
2113   EXPECT_EQ(LayoutRect(0, 0, 200, 50), lockable_box->LayoutOverflowRect());
2114 
2115   // After unlocking, we should process the pending visual overflow recalc.
2116   lockable->classList().Remove("hidden");
2117   UpdateAllLifecyclePhasesForTest();
2118 
2119   EXPECT_EQ(LayoutRect(0, 0, 200, 200), lockable_box->VisualOverflowRect());
2120   EXPECT_EQ(LayoutRect(0, 0, 200, 200), lockable_box->LayoutOverflowRect());
2121 }
2122 
TEST_F(DisplayLockContextRenderingTest,VisualOverflowCalculateOnChildPaintLayerInForcedLock)2123 TEST_F(DisplayLockContextRenderingTest,
2124        VisualOverflowCalculateOnChildPaintLayerInForcedLock) {
2125   SetHtmlInnerHTML(R"HTML(
2126     <style>
2127       .hidden { content-visibility: hidden }
2128       .paint_layer { contain: paint }
2129       .composited { will-change: transform }
2130     </style>
2131     <div id=lockable class=paint_layer>
2132       <div id=parent class=paint_layer>
2133         <div id=child class=paint_layer>
2134           <span>content</span>
2135           <span>content</span>
2136           <span>content</span>
2137         </div>
2138       </div>
2139     </div>
2140   )HTML");
2141 
2142   auto* parent = GetDocument().getElementById("parent");
2143   auto* parent_box = parent->GetLayoutBoxModelObject();
2144   ASSERT_TRUE(parent_box);
2145   EXPECT_TRUE(parent_box->Layer());
2146   EXPECT_TRUE(parent_box->HasSelfPaintingLayer());
2147 
2148   // Lock the container.
2149   auto* lockable = GetDocument().getElementById("lockable");
2150   lockable->classList().Add("hidden");
2151   UpdateAllLifecyclePhasesForTest();
2152 
2153   auto* child_layer = GetPaintLayerByElementId("child");
2154   child_layer->SetNeedsVisualOverflowRecalc();
2155   EXPECT_TRUE(child_layer->NeedsVisualOverflowRecalc());
2156 
2157   ASSERT_TRUE(lockable->GetDisplayLockContext());
2158   {
2159     auto scope = GetScopedForcedUpdate(lockable, true /* include self */);
2160 
2161     // The following should not crash/DCHECK.
2162     UpdateAllLifecyclePhasesForTest();
2163   }
2164 
2165   // Verify that the display lock doesn't keep extra state since the update was
2166   // processed.
2167   EXPECT_FALSE(DescendantDependentFlagUpdateWasBlocked(
2168       lockable->GetDisplayLockContext()));
2169   EXPECT_FALSE(child_layer->NeedsVisualOverflowRecalc());
2170 
2171   // After unlocking, we should not need to do any extra work.
2172   lockable->classList().Remove("hidden");
2173   EXPECT_FALSE(child_layer->NeedsVisualOverflowRecalc());
2174 
2175   UpdateAllLifecyclePhasesForTest();
2176 }
TEST_F(DisplayLockContextRenderingTest,SelectionOnAnonymousColumnSpannerDoesNotCrash)2177 TEST_F(DisplayLockContextRenderingTest,
2178        SelectionOnAnonymousColumnSpannerDoesNotCrash) {
2179   SetHtmlInnerHTML(R"HTML(
2180     <style>
2181       #columns {
2182         column-count: 5;
2183       }
2184       #spanner {
2185         column-span: all;
2186       }
2187     </style>
2188     <div id="columns">
2189       <div id="spanner"></div>
2190     </div>
2191   )HTML");
2192 
2193   auto* columns_object =
2194       GetDocument().getElementById("columns")->GetLayoutObject();
2195   LayoutObject* spanner_placeholder_object = nullptr;
2196   for (auto* candidate = columns_object->SlowFirstChild(); candidate;
2197        candidate = candidate->NextSibling()) {
2198     if (candidate->IsLayoutMultiColumnSpannerPlaceholder()) {
2199       spanner_placeholder_object = candidate;
2200       break;
2201     }
2202   }
2203 
2204   ASSERT_TRUE(spanner_placeholder_object);
2205   EXPECT_FALSE(spanner_placeholder_object->CanBeSelectionLeaf());
2206 }
2207 
TEST_F(DisplayLockContextRenderingTest,ObjectsNeedingLayoutConsidersLocks)2208 TEST_F(DisplayLockContextRenderingTest, ObjectsNeedingLayoutConsidersLocks) {
2209   SetHtmlInnerHTML(R"HTML(
2210     <div id=a>
2211       <div id=b>
2212         <div id=c></div>
2213         <div id=d></div>
2214       </div>
2215       <div id=e>
2216         <div id=f></div>
2217         <div id=g></div>
2218       </div>
2219     </div>
2220   )HTML");
2221 
2222   // Dirty all of the leaf nodes.
2223   auto dirty_all = [this]() {
2224     GetDocument().getElementById("c")->GetLayoutObject()->SetNeedsLayout(
2225         "test");
2226     GetDocument().getElementById("d")->GetLayoutObject()->SetNeedsLayout(
2227         "test");
2228     GetDocument().getElementById("f")->GetLayoutObject()->SetNeedsLayout(
2229         "test");
2230     GetDocument().getElementById("g")->GetLayoutObject()->SetNeedsLayout(
2231         "test");
2232   };
2233 
2234   unsigned dirty_count = 0;
2235   unsigned total_count = 0;
2236   bool is_subtree = false;
2237 
2238   dirty_all();
2239   GetDocument().View()->CountObjectsNeedingLayout(dirty_count, total_count,
2240                                                   is_subtree);
2241   // 7 divs + body + html + layout view
2242   EXPECT_EQ(dirty_count, 10u);
2243   EXPECT_EQ(total_count, 10u);
2244 
2245   GetDocument().getElementById("e")->setAttribute(html_names::kStyleAttr,
2246                                                   "content-visibility: hidden");
2247   UpdateAllLifecyclePhasesForTest();
2248 
2249   // Note that the dirty_all call propagate the dirty bit from the unlocked
2250   // subtree all the way up to the layout view, so everything on the way up is
2251   // dirtied.
2252   dirty_all();
2253   GetDocument().View()->CountObjectsNeedingLayout(dirty_count, total_count,
2254                                                   is_subtree);
2255   // Element with 2 children is locked, and it itself isn't dirty (just the
2256   // children are). So, 10 - 3 = 7
2257   EXPECT_EQ(dirty_count, 7u);
2258   // We still see the locked element, so the total is 8.
2259   EXPECT_EQ(total_count, 8u);
2260 
2261   GetDocument().getElementById("a")->setAttribute(html_names::kStyleAttr,
2262                                                   "content-visibility: hidden");
2263   UpdateAllLifecyclePhasesForTest();
2264 
2265   // Note that this dirty_all call is now not propagating the dirty bits at all,
2266   // since they are stopped at the top level div.
2267   dirty_all();
2268   GetDocument().View()->CountObjectsNeedingLayout(dirty_count, total_count,
2269                                                   is_subtree);
2270   // Top level element is locked and the dirty bits were not propagated, so we
2271   // expect 0 dirty elements. The total should be 4 ('a' + body + html + layout
2272   // view);
2273   EXPECT_EQ(dirty_count, 0u);
2274   EXPECT_EQ(total_count, 4u);
2275 }
2276 
TEST_F(DisplayLockContextRenderingTest,PaintDirtyBitsNotPropagatedAcrossBoundary)2277 TEST_F(DisplayLockContextRenderingTest,
2278        PaintDirtyBitsNotPropagatedAcrossBoundary) {
2279   SetHtmlInnerHTML(R"HTML(
2280     <style>
2281     .locked { content-visibility: hidden; }
2282     div { contain: paint; }
2283     </style>
2284     <div id=parent>
2285       <div id=lockable>
2286         <div id=child>
2287           <div id=grandchild></div>
2288         </div>
2289       </div>
2290     </div>
2291   )HTML");
2292 
2293   auto* parent = GetDocument().getElementById("parent");
2294   auto* lockable = GetDocument().getElementById("lockable");
2295   auto* child = GetDocument().getElementById("child");
2296   auto* grandchild = GetDocument().getElementById("grandchild");
2297 
2298   auto* parent_box = parent->GetLayoutBoxModelObject();
2299   auto* lockable_box = lockable->GetLayoutBoxModelObject();
2300   auto* child_box = child->GetLayoutBoxModelObject();
2301   auto* grandchild_box = grandchild->GetLayoutBoxModelObject();
2302 
2303   ASSERT_TRUE(parent_box);
2304   ASSERT_TRUE(lockable_box);
2305   ASSERT_TRUE(child_box);
2306   ASSERT_TRUE(grandchild_box);
2307 
2308   ASSERT_TRUE(parent_box->HasSelfPaintingLayer());
2309   ASSERT_TRUE(lockable_box->HasSelfPaintingLayer());
2310   ASSERT_TRUE(child_box->HasSelfPaintingLayer());
2311   ASSERT_TRUE(grandchild_box->HasSelfPaintingLayer());
2312 
2313   auto* parent_layer = parent_box->Layer();
2314   auto* lockable_layer = lockable_box->Layer();
2315   auto* child_layer = child_box->Layer();
2316   auto* grandchild_layer = grandchild_box->Layer();
2317 
2318   EXPECT_FALSE(parent_layer->SelfOrDescendantNeedsRepaint());
2319   EXPECT_FALSE(lockable_layer->SelfOrDescendantNeedsRepaint());
2320   EXPECT_FALSE(child_layer->SelfOrDescendantNeedsRepaint());
2321   EXPECT_FALSE(grandchild_layer->SelfOrDescendantNeedsRepaint());
2322 
2323   lockable->classList().Add("locked");
2324   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
2325 
2326   // Lockable layer needs repainting after locking.
2327   EXPECT_FALSE(parent_layer->SelfNeedsRepaint());
2328   EXPECT_TRUE(lockable_layer->SelfNeedsRepaint());
2329   EXPECT_FALSE(child_layer->SelfNeedsRepaint());
2330   EXPECT_FALSE(grandchild_layer->SelfNeedsRepaint());
2331 
2332   // Breadcrumbs are set from the lockable layer.
2333   EXPECT_TRUE(parent_layer->DescendantNeedsRepaint());
2334   EXPECT_FALSE(lockable_layer->DescendantNeedsRepaint());
2335   EXPECT_FALSE(child_layer->DescendantNeedsRepaint());
2336   EXPECT_FALSE(grandchild_layer->DescendantNeedsRepaint());
2337 
2338   UpdateAllLifecyclePhasesForTest();
2339 
2340   // Everything is clean.
2341   EXPECT_FALSE(parent_layer->SelfNeedsRepaint());
2342   EXPECT_FALSE(lockable_layer->SelfNeedsRepaint());
2343   EXPECT_FALSE(child_layer->SelfNeedsRepaint());
2344   EXPECT_FALSE(grandchild_layer->SelfNeedsRepaint());
2345 
2346   // Breadcrumbs are clean as well.
2347   EXPECT_FALSE(parent_layer->DescendantNeedsRepaint());
2348   EXPECT_FALSE(lockable_layer->DescendantNeedsRepaint());
2349   EXPECT_FALSE(child_layer->DescendantNeedsRepaint());
2350   EXPECT_FALSE(grandchild_layer->DescendantNeedsRepaint());
2351 
2352   grandchild_layer->SetNeedsRepaint();
2353 
2354   // Grandchild needs repaint, so everything else should be clean.
2355   EXPECT_FALSE(parent_layer->SelfNeedsRepaint());
2356   EXPECT_FALSE(lockable_layer->SelfNeedsRepaint());
2357   EXPECT_FALSE(child_layer->SelfNeedsRepaint());
2358   EXPECT_TRUE(grandchild_layer->SelfNeedsRepaint());
2359 
2360   // Breadcrumbs are set from the lockable layer but are stopped at the locked
2361   // boundary.
2362   EXPECT_FALSE(parent_layer->DescendantNeedsRepaint());
2363   EXPECT_TRUE(lockable_layer->DescendantNeedsRepaint());
2364   EXPECT_TRUE(child_layer->DescendantNeedsRepaint());
2365   EXPECT_FALSE(grandchild_layer->DescendantNeedsRepaint());
2366 
2367   // Updating the lifecycle does not clean the dirty bits.
2368   UpdateAllLifecyclePhasesForTest();
2369 
2370   EXPECT_FALSE(parent_layer->SelfNeedsRepaint());
2371   EXPECT_FALSE(lockable_layer->SelfNeedsRepaint());
2372   EXPECT_FALSE(child_layer->SelfNeedsRepaint());
2373   EXPECT_TRUE(grandchild_layer->SelfNeedsRepaint());
2374 
2375   EXPECT_FALSE(parent_layer->DescendantNeedsRepaint());
2376   EXPECT_TRUE(lockable_layer->DescendantNeedsRepaint());
2377   EXPECT_TRUE(child_layer->DescendantNeedsRepaint());
2378   EXPECT_FALSE(grandchild_layer->DescendantNeedsRepaint());
2379 
2380   // Unlocking causes lockable to repaint itself.
2381   lockable->classList().Remove("locked");
2382   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
2383 
2384   EXPECT_FALSE(parent_layer->SelfNeedsRepaint());
2385   EXPECT_TRUE(lockable_layer->SelfNeedsRepaint());
2386   EXPECT_FALSE(child_layer->SelfNeedsRepaint());
2387   EXPECT_TRUE(grandchild_layer->SelfNeedsRepaint());
2388 
2389   EXPECT_TRUE(parent_layer->DescendantNeedsRepaint());
2390   EXPECT_TRUE(lockable_layer->DescendantNeedsRepaint());
2391   EXPECT_TRUE(child_layer->DescendantNeedsRepaint());
2392   EXPECT_FALSE(grandchild_layer->DescendantNeedsRepaint());
2393 
2394   UpdateAllLifecyclePhasesForTest();
2395 
2396   // Everything should be clean.
2397   EXPECT_FALSE(parent_layer->SelfNeedsRepaint());
2398   EXPECT_FALSE(lockable_layer->SelfNeedsRepaint());
2399   EXPECT_FALSE(child_layer->SelfNeedsRepaint());
2400   EXPECT_FALSE(grandchild_layer->SelfNeedsRepaint());
2401 
2402   // Breadcrumbs are clean as well.
2403   EXPECT_FALSE(parent_layer->DescendantNeedsRepaint());
2404   EXPECT_FALSE(lockable_layer->DescendantNeedsRepaint());
2405   EXPECT_FALSE(child_layer->DescendantNeedsRepaint());
2406   EXPECT_FALSE(grandchild_layer->DescendantNeedsRepaint());
2407 }
2408 
TEST_F(DisplayLockContextRenderingTest,NestedLockDoesNotInvalidateOnHideOrShow)2409 TEST_F(DisplayLockContextRenderingTest,
2410        NestedLockDoesNotInvalidateOnHideOrShow) {
2411   SetHtmlInnerHTML(R"HTML(
2412     <style>
2413       .auto { content-visibility: auto; }
2414       .hidden { content-visibility: hidden; }
2415       .item { height: 10px; }
2416       /* this is important to not invalidate layout when we hide the element! */
2417       #outer { contain: style layout; }
2418     </style>
2419     <div id=outer>
2420       <div id=unrelated>
2421         <div id=inner class=auto>Content</div>
2422       </div>
2423     </div>
2424   )HTML");
2425 
2426   auto* inner_element = GetDocument().getElementById("inner");
2427   auto* unrelated_element = GetDocument().getElementById("unrelated");
2428   auto* outer_element = GetDocument().getElementById("outer");
2429 
2430   // Ensure that the visibility switch happens. This would also clear the
2431   // layout.
2432   UpdateAllLifecyclePhasesForTest();
2433   EXPECT_FALSE(outer_element->GetLayoutObject()->NeedsLayout());
2434   EXPECT_FALSE(outer_element->GetLayoutObject()->SelfNeedsLayout());
2435   EXPECT_FALSE(unrelated_element->GetLayoutObject()->NeedsLayout());
2436   EXPECT_FALSE(unrelated_element->GetLayoutObject()->SelfNeedsLayout());
2437   EXPECT_FALSE(inner_element->GetLayoutObject()->NeedsLayout());
2438   EXPECT_FALSE(inner_element->GetLayoutObject()->SelfNeedsLayout());
2439 
2440   // Verify lock state.
2441   auto* inner_context = inner_element->GetDisplayLockContext();
2442   ASSERT_TRUE(inner_context);
2443   EXPECT_FALSE(inner_context->IsLocked());
2444 
2445   // Lock outer.
2446   outer_element->setAttribute(html_names::kClassAttr, "hidden");
2447   // Ensure the lock processes (but don't run intersection observation tasks
2448   // yet).
2449   UpdateAllLifecyclePhasesForTest();
2450 
2451   // Verify the lock exists.
2452   auto* outer_context = outer_element->GetDisplayLockContext();
2453   ASSERT_TRUE(outer_context);
2454   EXPECT_TRUE(outer_context->IsLocked());
2455 
2456   // Everything should be layout clean.
2457   EXPECT_FALSE(outer_element->GetLayoutObject()->NeedsLayout());
2458   EXPECT_FALSE(outer_element->GetLayoutObject()->SelfNeedsLayout());
2459   EXPECT_FALSE(unrelated_element->GetLayoutObject()->NeedsLayout());
2460   EXPECT_FALSE(unrelated_element->GetLayoutObject()->SelfNeedsLayout());
2461   EXPECT_FALSE(inner_element->GetLayoutObject()->NeedsLayout());
2462   EXPECT_FALSE(inner_element->GetLayoutObject()->SelfNeedsLayout());
2463 
2464   // Inner context should not be observing the lifecycle.
2465   EXPECT_FALSE(IsObservingLifecycle(inner_context));
2466 
2467   // Process any visibility changes.
2468   RunStartOfLifecycleTasks();
2469 
2470   // Run the following checks a few times since we should be observing
2471   // lifecycle.
2472   for (int i = 0; i < 3; ++i) {
2473     // It shouldn't change the fact that we're layout clean.
2474     EXPECT_FALSE(outer_element->GetLayoutObject()->NeedsLayout());
2475     EXPECT_FALSE(outer_element->GetLayoutObject()->SelfNeedsLayout());
2476     EXPECT_FALSE(unrelated_element->GetLayoutObject()->NeedsLayout());
2477     EXPECT_FALSE(unrelated_element->GetLayoutObject()->SelfNeedsLayout());
2478     EXPECT_FALSE(inner_element->GetLayoutObject()->NeedsLayout());
2479     EXPECT_FALSE(inner_element->GetLayoutObject()->SelfNeedsLayout());
2480 
2481     // Because we skipped hiding the element, inner_context should be observing
2482     // lifecycle.
2483     EXPECT_TRUE(IsObservingLifecycle(inner_context));
2484 
2485     UpdateAllLifecyclePhasesForTest();
2486   }
2487 
2488   // Unlock outer.
2489   outer_element->setAttribute(html_names::kClassAttr, "");
2490   // Ensure the lock processes (but don't run intersection observation tasks
2491   // yet).
2492   UpdateAllLifecyclePhasesForTest();
2493 
2494   // Note that although we're not nested, we're still observing the lifecycle
2495   // because we don't yet know whether we should or should not hide and we only
2496   // make this decision _before_ the lifecycle actually unlocked outer.
2497   EXPECT_TRUE(IsObservingLifecycle(inner_context));
2498 
2499   // Verify the lock is gone.
2500   EXPECT_FALSE(outer_context->IsLocked());
2501 
2502   // Everything should be layout clean.
2503   EXPECT_FALSE(outer_element->GetLayoutObject()->NeedsLayout());
2504   EXPECT_FALSE(outer_element->GetLayoutObject()->SelfNeedsLayout());
2505   EXPECT_FALSE(unrelated_element->GetLayoutObject()->NeedsLayout());
2506   EXPECT_FALSE(unrelated_element->GetLayoutObject()->SelfNeedsLayout());
2507   EXPECT_FALSE(inner_element->GetLayoutObject()->NeedsLayout());
2508   EXPECT_FALSE(inner_element->GetLayoutObject()->SelfNeedsLayout());
2509 
2510   // Process visibility changes.
2511   RunStartOfLifecycleTasks();
2512 
2513   // We now should know we're visible and so we're not observing the lifecycle.
2514   EXPECT_FALSE(IsObservingLifecycle(inner_context));
2515 
2516   // Also we should still be activated and unlocked.
2517   EXPECT_FALSE(inner_context->IsLocked());
2518 
2519   // Everything should be layout clean.
2520   EXPECT_FALSE(outer_element->GetLayoutObject()->NeedsLayout());
2521   EXPECT_FALSE(outer_element->GetLayoutObject()->SelfNeedsLayout());
2522   EXPECT_FALSE(unrelated_element->GetLayoutObject()->NeedsLayout());
2523   EXPECT_FALSE(unrelated_element->GetLayoutObject()->SelfNeedsLayout());
2524   EXPECT_FALSE(inner_element->GetLayoutObject()->NeedsLayout());
2525   EXPECT_FALSE(inner_element->GetLayoutObject()->SelfNeedsLayout());
2526 }
2527 
TEST_F(DisplayLockContextRenderingTest,NestedLockDoesHideWhenItIsOffscreen)2528 TEST_F(DisplayLockContextRenderingTest, NestedLockDoesHideWhenItIsOffscreen) {
2529   SetHtmlInnerHTML(R"HTML(
2530     <style>
2531       .auto { content-visibility: auto; }
2532       .hidden { content-visibility: hidden; }
2533       .item { height: 10px; }
2534       /* this is important to not invalidate layout when we hide the element! */
2535       #outer { contain: style layout; }
2536       .spacer { height: 10000px; }
2537     </style>
2538     <div id=future_spacer></div>
2539     <div id=outer>
2540       <div id=unrelated>
2541         <div id=inner class=auto>Content</div>
2542       </div>
2543     </div>
2544   )HTML");
2545 
2546   auto* inner_element = GetDocument().getElementById("inner");
2547   auto* unrelated_element = GetDocument().getElementById("unrelated");
2548   auto* outer_element = GetDocument().getElementById("outer");
2549 
2550   // Ensure that the visibility switch happens. This would also clear the
2551   // layout.
2552   UpdateAllLifecyclePhasesForTest();
2553   EXPECT_FALSE(outer_element->GetLayoutObject()->NeedsLayout());
2554   EXPECT_FALSE(outer_element->GetLayoutObject()->SelfNeedsLayout());
2555   EXPECT_FALSE(unrelated_element->GetLayoutObject()->NeedsLayout());
2556   EXPECT_FALSE(unrelated_element->GetLayoutObject()->SelfNeedsLayout());
2557   EXPECT_FALSE(inner_element->GetLayoutObject()->NeedsLayout());
2558   EXPECT_FALSE(inner_element->GetLayoutObject()->SelfNeedsLayout());
2559 
2560   // Verify lock state.
2561   auto* inner_context = inner_element->GetDisplayLockContext();
2562   ASSERT_TRUE(inner_context);
2563   EXPECT_FALSE(inner_context->IsLocked());
2564 
2565   // Lock outer.
2566   outer_element->setAttribute(html_names::kClassAttr, "hidden");
2567   // Ensure the lock processes (but don't run intersection observation tasks
2568   // yet).
2569   UpdateAllLifecyclePhasesForTest();
2570 
2571   // Verify the lock exists.
2572   auto* outer_context = outer_element->GetDisplayLockContext();
2573   ASSERT_TRUE(outer_context);
2574   EXPECT_TRUE(outer_context->IsLocked());
2575 
2576   // Everything should be layout clean.
2577   EXPECT_FALSE(outer_element->GetLayoutObject()->NeedsLayout());
2578   EXPECT_FALSE(outer_element->GetLayoutObject()->SelfNeedsLayout());
2579   EXPECT_FALSE(unrelated_element->GetLayoutObject()->NeedsLayout());
2580   EXPECT_FALSE(unrelated_element->GetLayoutObject()->SelfNeedsLayout());
2581   EXPECT_FALSE(inner_element->GetLayoutObject()->NeedsLayout());
2582   EXPECT_FALSE(inner_element->GetLayoutObject()->SelfNeedsLayout());
2583 
2584   // Inner context should not be observing the lifecycle.
2585   EXPECT_FALSE(IsObservingLifecycle(inner_context));
2586 
2587   // Process any visibility changes.
2588   RunStartOfLifecycleTasks();
2589 
2590   // It shouldn't change the fact that we're layout clean.
2591   EXPECT_FALSE(outer_element->GetLayoutObject()->NeedsLayout());
2592   EXPECT_FALSE(outer_element->GetLayoutObject()->SelfNeedsLayout());
2593   EXPECT_FALSE(unrelated_element->GetLayoutObject()->NeedsLayout());
2594   EXPECT_FALSE(unrelated_element->GetLayoutObject()->SelfNeedsLayout());
2595   EXPECT_FALSE(inner_element->GetLayoutObject()->NeedsLayout());
2596   EXPECT_FALSE(inner_element->GetLayoutObject()->SelfNeedsLayout());
2597 
2598   // Let future spacer become a real spacer!
2599   GetDocument()
2600       .getElementById("future_spacer")
2601       ->setAttribute(html_names::kClassAttr, "spacer");
2602 
2603   UpdateAllLifecyclePhasesForTest();
2604 
2605   // Because we skipped hiding the element, inner_context should be observing
2606   // lifecycle.
2607   EXPECT_TRUE(IsObservingLifecycle(inner_context));
2608 
2609   // Unlock outer.
2610   outer_element->setAttribute(html_names::kClassAttr, "");
2611   // Ensure the lock processes (but don't run intersection observation tasks
2612   // yet).
2613   UpdateAllLifecyclePhasesForTest();
2614 
2615   // Note that although we're not nested, we're still observing the lifecycle
2616   // because we don't yet know whether we should or should not hide and we only
2617   // make this decision _before_ the lifecycle actually unlocked outer.
2618   EXPECT_TRUE(IsObservingLifecycle(inner_context));
2619 
2620   // Verify the lock is gone.
2621   EXPECT_FALSE(outer_context->IsLocked());
2622 
2623   // Everything should be layout clean.
2624   EXPECT_FALSE(outer_element->GetLayoutObject()->NeedsLayout());
2625   EXPECT_FALSE(outer_element->GetLayoutObject()->SelfNeedsLayout());
2626   EXPECT_FALSE(unrelated_element->GetLayoutObject()->NeedsLayout());
2627   EXPECT_FALSE(unrelated_element->GetLayoutObject()->SelfNeedsLayout());
2628   EXPECT_FALSE(inner_element->GetLayoutObject()->NeedsLayout());
2629   EXPECT_FALSE(inner_element->GetLayoutObject()->SelfNeedsLayout());
2630 
2631   // Process any visibility changes.
2632   RunStartOfLifecycleTasks();
2633 
2634   // We're still invisible, and we don't know that we're not nested so we're
2635   // still observing the lifecycle.
2636   EXPECT_TRUE(IsObservingLifecycle(inner_context));
2637 
2638   // We're unlocked for now.
2639   EXPECT_FALSE(inner_context->IsLocked());
2640 
2641   // Everything should be layout clean.
2642   EXPECT_FALSE(outer_element->GetLayoutObject()->NeedsLayout());
2643   EXPECT_FALSE(outer_element->GetLayoutObject()->SelfNeedsLayout());
2644   EXPECT_FALSE(unrelated_element->GetLayoutObject()->NeedsLayout());
2645   EXPECT_FALSE(unrelated_element->GetLayoutObject()->SelfNeedsLayout());
2646   EXPECT_FALSE(inner_element->GetLayoutObject()->NeedsLayout());
2647   EXPECT_FALSE(inner_element->GetLayoutObject()->SelfNeedsLayout());
2648 
2649   UpdateAllLifecyclePhasesForTest();
2650 
2651   // We figured out that we're actually invisible so no need to observe the
2652   // lifecycle.
2653   EXPECT_FALSE(IsObservingLifecycle(inner_context));
2654 
2655   // We're locked.
2656   EXPECT_TRUE(inner_context->IsLocked());
2657 }
2658 
TEST_F(DisplayLockContextRenderingTest,LockedCanvasWithFallbackHasFocusableStyle)2659 TEST_F(DisplayLockContextRenderingTest,
2660        LockedCanvasWithFallbackHasFocusableStyle) {
2661   SetHtmlInnerHTML(R"HTML(
2662     <style>
2663       .auto { content-visibility: auto; }
2664       .spacer { height: 3000px; }
2665     </style>
2666     <div class=spacer></div>
2667     <div class=auto>
2668       <canvas>
2669         <div id=target tabindex=0></div>
2670       </canvas>
2671     </div>
2672   )HTML");
2673 
2674   auto* target = GetDocument().getElementById("target");
2675   EXPECT_TRUE(target->IsFocusable());
2676 }
2677 
TEST_F(DisplayLockContextRenderingTest,ForcedUnlockBookkeeping)2678 TEST_F(DisplayLockContextRenderingTest, ForcedUnlockBookkeeping) {
2679   SetHtmlInnerHTML(R"HTML(
2680     <style>
2681       .hidden { content-visibility: hidden; }
2682       .inline { display: inline; }
2683     </style>
2684     <div id=target class=hidden></div>
2685   )HTML");
2686 
2687   auto* target = GetDocument().getElementById("target");
2688   auto* context = target->GetDisplayLockContext();
2689 
2690   ASSERT_TRUE(context);
2691   EXPECT_TRUE(context->IsLocked());
2692   EXPECT_EQ(
2693       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 1);
2694 
2695   target->classList().Add("inline");
2696   UpdateAllLifecyclePhasesForTest();
2697 
2698   EXPECT_FALSE(context->IsLocked());
2699   EXPECT_EQ(
2700       GetDocument().GetDisplayLockDocumentState().LockedDisplayLockCount(), 0);
2701 }
2702 
TEST_F(DisplayLockContextRenderingTest,LayoutRootIsSkippedIfLocked)2703 TEST_F(DisplayLockContextRenderingTest, LayoutRootIsSkippedIfLocked) {
2704   SetHtmlInnerHTML(R"HTML(
2705     <style>
2706       .hidden { content-visibility: hidden; }
2707       .contained { contain: strict; }
2708       .positioned { position: absolute; top: 0; left: 0; }
2709     </style>
2710     <div id=hide>
2711       <div class=contained>
2712         <div id=new_parent class="contained positioned">
2713           <div>
2714             <div id=target></div>
2715           </div>
2716         </div>
2717       </div>
2718     </div>
2719   )HTML");
2720 
2721   // Lock an ancestor.
2722   auto* hide = GetDocument().getElementById("hide");
2723   hide->classList().Add("hidden");
2724   UpdateAllLifecyclePhasesForTest();
2725 
2726   auto* target = GetDocument().getElementById("target");
2727   auto* new_parent = GetDocument().getElementById("new_parent");
2728 
2729   // Reparent elements which will invalidate layout without needing to process
2730   // style (which is blocked by the display-lock).
2731   new_parent->appendChild(target);
2732 
2733   // Note that we don't check target here, since it doesn't have a layout object
2734   // after being re-parented.
2735   EXPECT_TRUE(new_parent->GetLayoutObject()->NeedsLayout());
2736 
2737   // Updating the lifecycle should not update new_parent, since it is in a
2738   // locked subtree.
2739   UpdateAllLifecyclePhasesForTest();
2740   EXPECT_TRUE(new_parent->GetLayoutObject()->NeedsLayout());
2741 
2742   // Unlocking and updating should update everything.
2743   hide->classList().Remove("hidden");
2744   UpdateAllLifecyclePhasesForTest();
2745 
2746   EXPECT_FALSE(hide->GetLayoutObject()->NeedsLayout());
2747   EXPECT_FALSE(target->GetLayoutObject()->NeedsLayout());
2748   EXPECT_FALSE(new_parent->GetLayoutObject()->NeedsLayout());
2749 }
2750 
TEST_F(DisplayLockContextRenderingTest,LayoutRootIsProcessedIfLockedAndForced)2751 TEST_F(DisplayLockContextRenderingTest,
2752        LayoutRootIsProcessedIfLockedAndForced) {
2753   SetHtmlInnerHTML(R"HTML(
2754     <style>
2755       .hidden { content-visibility: hidden; }
2756       .contained { contain: strict; }
2757       .positioned { position: absolute; top: 0; left: 0; }
2758     </style>
2759     <div id=hide>
2760       <div class=contained>
2761         <div id=new_parent class="contained positioned">
2762           <div>
2763             <div id=target></div>
2764           </div>
2765         </div>
2766       </div>
2767     </div>
2768   )HTML");
2769 
2770   // Lock an ancestor.
2771   auto* hide = GetDocument().getElementById("hide");
2772   hide->classList().Add("hidden");
2773   UpdateAllLifecyclePhasesForTest();
2774 
2775   auto* target = GetDocument().getElementById("target");
2776   auto* new_parent = GetDocument().getElementById("new_parent");
2777 
2778   // Reparent elements which will invalidate layout without needing to process
2779   // style (which is blocked by the display-lock).
2780   new_parent->appendChild(target);
2781 
2782   // Note that we don't check target here, since it doesn't have a layout object
2783   // after being re-parented.
2784   EXPECT_TRUE(new_parent->GetLayoutObject()->NeedsLayout());
2785 
2786   {
2787     auto scope = GetScopedForcedUpdate(hide, true /* include self */);
2788 
2789     // Updating the lifecycle should update target and new_parent, since it is
2790     // in a locked but forced subtree.
2791     UpdateAllLifecyclePhasesForTest();
2792     EXPECT_FALSE(target->GetLayoutObject()->NeedsLayout());
2793     EXPECT_FALSE(new_parent->GetLayoutObject()->NeedsLayout());
2794   }
2795 
2796   // Unlocking and updating should update everything.
2797   hide->classList().Remove("hidden");
2798   UpdateAllLifecyclePhasesForTest();
2799 
2800   EXPECT_FALSE(hide->GetLayoutObject()->NeedsLayout());
2801   EXPECT_FALSE(target->GetLayoutObject()->NeedsLayout());
2802   EXPECT_FALSE(new_parent->GetLayoutObject()->NeedsLayout());
2803 }
2804 
TEST_F(DisplayLockContextRenderingTest,ContainStrictChild)2805 TEST_F(DisplayLockContextRenderingTest, ContainStrictChild) {
2806   SetHtmlInnerHTML(R"HTML(
2807     <style>
2808       .hidden { content-visibility: hidden; }
2809       .contained { contain: strict; }
2810       #target { backface-visibility: hidden; }
2811     </style>
2812     <div id=hide>
2813       <div id=container class=contained>
2814         <div id=target></div>
2815       </div>
2816     </div>
2817   )HTML");
2818 
2819   // Lock an ancestor.
2820   auto* hide = GetDocument().getElementById("hide");
2821   hide->classList().Add("hidden");
2822 
2823   // This should not DCHECK.
2824   UpdateAllLifecyclePhasesForTest();
2825 
2826   hide->classList().Remove("hidden");
2827   UpdateAllLifecyclePhasesForTest();
2828 }
2829 
TEST_F(DisplayLockContextRenderingTest,UseCounter)2830 TEST_F(DisplayLockContextRenderingTest, UseCounter) {
2831   SetHtmlInnerHTML(R"HTML(
2832     <style>
2833       .auto { content-visibility: auto; }
2834       .hidden { content-visibility: hidden; }
2835       .matchable { content-visibility: hidden-matchable; }
2836     </style>
2837     <div id=e1></div>
2838     <div id=e2></div>
2839     <div id=e3></div>
2840   )HTML");
2841 
2842   EXPECT_FALSE(GetDocument().IsUseCounted(WebFeature::kContentVisibilityAuto));
2843   EXPECT_FALSE(
2844       GetDocument().IsUseCounted(WebFeature::kContentVisibilityHidden));
2845   EXPECT_FALSE(GetDocument().IsUseCounted(
2846       WebFeature::kContentVisibilityHiddenMatchable));
2847 
2848   GetDocument().getElementById("e1")->classList().Add("auto");
2849   UpdateAllLifecyclePhasesForTest();
2850 
2851   EXPECT_TRUE(GetDocument().IsUseCounted(WebFeature::kContentVisibilityAuto));
2852   EXPECT_FALSE(
2853       GetDocument().IsUseCounted(WebFeature::kContentVisibilityHidden));
2854   EXPECT_FALSE(GetDocument().IsUseCounted(
2855       WebFeature::kContentVisibilityHiddenMatchable));
2856 
2857   GetDocument().getElementById("e2")->classList().Add("hidden");
2858   UpdateAllLifecyclePhasesForTest();
2859 
2860   EXPECT_TRUE(GetDocument().IsUseCounted(WebFeature::kContentVisibilityAuto));
2861   EXPECT_TRUE(GetDocument().IsUseCounted(WebFeature::kContentVisibilityHidden));
2862   EXPECT_FALSE(GetDocument().IsUseCounted(
2863       WebFeature::kContentVisibilityHiddenMatchable));
2864 
2865   GetDocument().getElementById("e3")->classList().Add("matchable");
2866   UpdateAllLifecyclePhasesForTest();
2867 
2868   EXPECT_TRUE(GetDocument().IsUseCounted(WebFeature::kContentVisibilityAuto));
2869   EXPECT_TRUE(GetDocument().IsUseCounted(WebFeature::kContentVisibilityHidden));
2870   EXPECT_TRUE(GetDocument().IsUseCounted(
2871       WebFeature::kContentVisibilityHiddenMatchable));
2872 }
2873 
TEST_F(DisplayLockContextRenderingTest,CompositingRootIsSkippedIfLocked)2874 TEST_F(DisplayLockContextRenderingTest, CompositingRootIsSkippedIfLocked) {
2875   SetHtmlInnerHTML(R"HTML(
2876     <style>
2877       .hidden { content-visibility: hidden; }
2878       .contained { contain: strict; }
2879       #target { backface-visibility: hidden; }
2880     </style>
2881     <div id=hide>
2882       <div id=container class=contained>
2883         <div id=target></div>
2884       </div>
2885     </div>
2886   )HTML");
2887 
2888   // Lock an ancestor.
2889   auto* hide = GetDocument().getElementById("hide");
2890   hide->classList().Add("hidden");
2891   UpdateAllLifecyclePhasesForTest();
2892 
2893   auto* target = GetDocument().getElementById("target");
2894   ASSERT_TRUE(target->GetLayoutObject());
2895   auto* target_box = target->GetLayoutBoxModelObject();
2896   ASSERT_TRUE(target_box);
2897   EXPECT_TRUE(target_box->Layer());
2898   EXPECT_TRUE(target_box->HasSelfPaintingLayer());
2899   auto* target_layer = target_box->Layer();
2900 
2901   target_layer->SetNeedsCompositingInputsUpdate();
2902   EXPECT_TRUE(target_layer->NeedsCompositingInputsUpdate());
2903 
2904   auto* container = GetDocument().getElementById("container");
2905   ASSERT_TRUE(container->GetLayoutObject());
2906   auto* container_box = container->GetLayoutBoxModelObject();
2907   ASSERT_TRUE(container_box);
2908   EXPECT_TRUE(container_box->Layer());
2909   EXPECT_TRUE(container_box->HasSelfPaintingLayer());
2910   auto* container_layer = container_box->Layer();
2911 
2912   auto* compositor = target_layer->Compositor();
2913   ASSERT_TRUE(compositor);
2914 
2915   EXPECT_EQ(compositor->GetCompositingInputsRoot(), container_layer);
2916 
2917   UpdateAllLifecyclePhasesForTest();
2918 
2919   EXPECT_EQ(compositor->GetCompositingInputsRoot(), container_layer);
2920   EXPECT_TRUE(target_layer->NeedsCompositingInputsUpdate());
2921 
2922   hide->classList().Remove("hidden");
2923 
2924   UpdateAllLifecyclePhasesForTest();
2925 
2926   EXPECT_FALSE(compositor->GetCompositingInputsRoot());
2927   EXPECT_FALSE(target_layer->NeedsCompositingInputsUpdate());
2928 }
2929 
TEST_F(DisplayLockContextRenderingTest,CompositingRootIsProcessedIfLockedButForced)2930 TEST_F(DisplayLockContextRenderingTest,
2931        CompositingRootIsProcessedIfLockedButForced) {
2932   SetHtmlInnerHTML(R"HTML(
2933     <style>
2934       .hidden { content-visibility: hidden; }
2935       .contained { contain: strict; }
2936       #target { backface-visibility: hidden; }
2937     </style>
2938     <div id=hide>
2939       <div class=contained>
2940         <div id=container class=contained>
2941           <div id=target></div>
2942         </div>
2943       </div>
2944     </div>
2945   )HTML");
2946 
2947   // Lock an ancestor.
2948   auto* hide = GetDocument().getElementById("hide");
2949   hide->classList().Add("hidden");
2950   UpdateAllLifecyclePhasesForTest();
2951 
2952   auto* target = GetDocument().getElementById("target");
2953   ASSERT_TRUE(target->GetLayoutObject());
2954   auto* target_box = To<LayoutBoxModelObject>(target->GetLayoutObject());
2955   ASSERT_TRUE(target_box);
2956   EXPECT_TRUE(target_box->Layer());
2957   EXPECT_TRUE(target_box->HasSelfPaintingLayer());
2958   auto* target_layer = target_box->Layer();
2959 
2960   target_layer->SetNeedsCompositingInputsUpdate();
2961   EXPECT_TRUE(target_layer->NeedsCompositingInputsUpdate());
2962 
2963   auto* container = GetDocument().getElementById("container");
2964   ASSERT_TRUE(container->GetLayoutObject());
2965   auto* container_box = container->GetLayoutBoxModelObject();
2966   ASSERT_TRUE(container_box);
2967   EXPECT_TRUE(container_box->Layer());
2968   EXPECT_TRUE(container_box->HasSelfPaintingLayer());
2969   auto* container_layer = container_box->Layer();
2970 
2971   auto* compositor = target_layer->Compositor();
2972   ASSERT_TRUE(compositor);
2973 
2974   EXPECT_EQ(compositor->GetCompositingInputsRoot(), container_layer);
2975 
2976   {
2977     auto scope = GetScopedForcedUpdate(hide, true /* include self */);
2978     UpdateAllLifecyclePhasesForTest();
2979   }
2980 
2981   EXPECT_FALSE(compositor->GetCompositingInputsRoot());
2982   EXPECT_FALSE(target_layer->NeedsCompositingInputsUpdate());
2983 
2984   hide->classList().Remove("hidden");
2985 
2986   UpdateAllLifecyclePhasesForTest();
2987 
2988   EXPECT_FALSE(compositor->GetCompositingInputsRoot());
2989   EXPECT_FALSE(target_layer->NeedsCompositingInputsUpdate());
2990 }
2991 
TEST_F(DisplayLockContextRenderingTest,NeedsLayoutTreeUpdateForNodeRespectsForcedLocks)2992 TEST_F(DisplayLockContextRenderingTest,
2993        NeedsLayoutTreeUpdateForNodeRespectsForcedLocks) {
2994   SetHtmlInnerHTML(R"HTML(
2995     <style>
2996       .hidden { content-visibility: hidden; }
2997       .contained { contain: strict; }
2998       .backface_hidden { backface-visibility: hidden; }
2999     </style>
3000     <div id=hide>
3001       <div id=container class=contained>
3002         <div id=target></div>
3003       </div>
3004     </div>
3005   )HTML");
3006 
3007   // Lock an ancestor.
3008   auto* hide = GetDocument().getElementById("hide");
3009   hide->classList().Add("hidden");
3010   UpdateAllLifecyclePhasesForTest();
3011 
3012   auto* target = GetDocument().getElementById("target");
3013   target->classList().Add("backface_hidden");
3014 
3015   auto scope = GetScopedForcedUpdate(hide, true /* include self */);
3016   EXPECT_TRUE(GetDocument().NeedsLayoutTreeUpdateForNode(*target));
3017 }
3018 
TEST_F(DisplayLockContextRenderingTest,InnerScrollerAutoVisibilityMargin)3019 TEST_F(DisplayLockContextRenderingTest, InnerScrollerAutoVisibilityMargin) {
3020   SetHtmlInnerHTML(R"HTML(
3021     <style>
3022       .auto { content-visibility: auto; }
3023       #scroller { height: 300px; overflow: scroll }
3024       #target { height: 10px; width: 10px; }
3025       .spacer { height: 3000px }
3026     </style>
3027     <div id=scroller>
3028       <div class=spacer></div>
3029       <div id=target class=auto></div>
3030     </div>
3031   )HTML");
3032 
3033   UpdateAllLifecyclePhasesForTest();
3034   auto* target = GetDocument().getElementById("target");
3035   ASSERT_TRUE(target->GetDisplayLockContext());
3036   EXPECT_TRUE(target->GetDisplayLockContext()->IsLocked());
3037 
3038   auto* scroller = GetDocument().getElementById("scroller");
3039   // 2600 is spacer (3000) minus scroller height (300) minus 100 for some extra
3040   // padding.
3041   scroller->setScrollTop(2600);
3042   UpdateAllLifecyclePhasesForTest();
3043 
3044   // Since the intersection observation is delivered on the next frame, run
3045   // another lifecycle.
3046   UpdateAllLifecyclePhasesForTest();
3047 
3048   EXPECT_FALSE(target->GetDisplayLockContext()->IsLocked());
3049 }
3050 
TEST_F(DisplayLockContextRenderingTest,AutoReachesStableStateOnContentSmallerThanLockedSize)3051 TEST_F(DisplayLockContextRenderingTest,
3052        AutoReachesStableStateOnContentSmallerThanLockedSize) {
3053   SetHtmlInnerHTML(R"HTML(
3054     <style>
3055       .spacer { height: 20000px; }
3056       .auto {
3057         content-visibility: auto;
3058         contain-intrinsic-size: 1px 20000px;
3059       }
3060       .auto > div {
3061         height: 3000px;
3062       }
3063     </style>
3064 
3065     <div class=spacer></div>
3066     <div id=e1 class=auto><div>content</div></div>
3067     <div id=e2 class=auto><div>content</div></div>
3068     <div class=spacer></div>
3069   )HTML");
3070 
3071   UpdateAllLifecyclePhasesForTest();
3072 
3073   GetDocument().scrollingElement()->setScrollTop(29000);
3074 
3075   Element* element = GetDocument().getElementById("e1");
3076 
3077   // Note that this test also unlock/relocks #e2 but we only care about #e1
3078   // settling into a steady state.
3079 
3080   // Initially we start with locked in the viewport.
3081   UpdateAllLifecyclePhasesForTest();
3082   EXPECT_TRUE(element->GetDisplayLockContext()->IsLocked());
3083   EXPECT_EQ(GetDocument().scrollingElement()->scrollTop(), 29000.);
3084 
3085   // It gets unlocked because it's in the viewport.
3086   UpdateAllLifecyclePhasesForTest();
3087   EXPECT_FALSE(element->GetDisplayLockContext()->IsLocked());
3088   EXPECT_EQ(GetDocument().scrollingElement()->scrollTop(), 29000.);
3089 
3090   // By unlocking it, it shrinks so next time it gets relocked.
3091   UpdateAllLifecyclePhasesForTest();
3092   EXPECT_TRUE(element->GetDisplayLockContext()->IsLocked());
3093   EXPECT_EQ(GetDocument().scrollingElement()->scrollTop(), 29000.);
3094 
3095   // It again gets unlocked and shrink. This time scroll anchoring puts it right
3096   // off the edge of the screen.
3097   UpdateAllLifecyclePhasesForTest();
3098   EXPECT_FALSE(element->GetDisplayLockContext()->IsLocked());
3099   EXPECT_EQ(GetDocument().scrollingElement()->scrollTop(), 23008.);
3100 
3101   // On the next update we select the following element as an anchor and the
3102   // scroll offset doesn't change.
3103   UpdateAllLifecyclePhasesForTest();
3104   EXPECT_FALSE(element->GetDisplayLockContext()->IsLocked());
3105   EXPECT_EQ(GetDocument().scrollingElement()->scrollTop(), 23008.);
3106 
3107   // Subsequent updates are in a stable state.
3108   for (int i = 0; i < 5; ++i) {
3109     UpdateAllLifecyclePhasesForTest();
3110     EXPECT_FALSE(element->GetDisplayLockContext()->IsLocked());
3111     EXPECT_EQ(GetDocument().scrollingElement()->scrollTop(), 23008.);
3112   }
3113 }
3114 
TEST_F(DisplayLockContextRenderingTest,AutoReachesStableStateOnContentSmallerThanLockedSizeInLtr)3115 TEST_F(DisplayLockContextRenderingTest,
3116        AutoReachesStableStateOnContentSmallerThanLockedSizeInLtr) {
3117   SetHtmlInnerHTML(R"HTML(
3118     <style>
3119       body { writing-mode: vertical-lr }
3120       .spacer { block-size: 20000px; }
3121       .auto {
3122         content-visibility: auto;
3123         contain-intrinsic-size: 20000px 1px;
3124       }
3125       .auto > div {
3126         block-size: 3000px;
3127       }
3128     </style>
3129 
3130     <div class=spacer></div>
3131     <div id=e1 class=auto><div>content</div></div>
3132     <div id=e2 class=auto><div>content</div></div>
3133     <div class=spacer></div>
3134   )HTML");
3135 
3136   UpdateAllLifecyclePhasesForTest();
3137 
3138   GetDocument().scrollingElement()->setScrollLeft(29000);
3139 
3140   Element* element = GetDocument().getElementById("e1");
3141 
3142   // Note that this test also unlock/relocks #e2 but we only care about #e1
3143   // settling into a steady state.
3144 
3145   // Initially we start with locked in the viewport.
3146   UpdateAllLifecyclePhasesForTest();
3147   EXPECT_TRUE(element->GetDisplayLockContext()->IsLocked());
3148   EXPECT_EQ(GetDocument().scrollingElement()->scrollLeft(), 29000.);
3149 
3150   // It gets unlocked because it's in the viewport.
3151   UpdateAllLifecyclePhasesForTest();
3152   EXPECT_FALSE(element->GetDisplayLockContext()->IsLocked());
3153   EXPECT_EQ(GetDocument().scrollingElement()->scrollLeft(), 29000.);
3154 
3155   // By unlocking it, it shrinks so next time it gets relocked.
3156   UpdateAllLifecyclePhasesForTest();
3157   EXPECT_TRUE(element->GetDisplayLockContext()->IsLocked());
3158   EXPECT_EQ(GetDocument().scrollingElement()->scrollLeft(), 29000.);
3159 
3160   // It again gets unlocked and shrink. This time scroll anchoring puts it right
3161   // off the edge of the screen.
3162   UpdateAllLifecyclePhasesForTest();
3163   EXPECT_FALSE(element->GetDisplayLockContext()->IsLocked());
3164   EXPECT_EQ(GetDocument().scrollingElement()->scrollLeft(), 23008.);
3165 
3166   // On the next update we select the following element as an anchor and the
3167   // scroll offset doesn't change.
3168   UpdateAllLifecyclePhasesForTest();
3169   EXPECT_FALSE(element->GetDisplayLockContext()->IsLocked());
3170   EXPECT_EQ(GetDocument().scrollingElement()->scrollLeft(), 23008.);
3171 
3172   // Subsequent updates are in a stable state.
3173   for (int i = 0; i < 5; ++i) {
3174     UpdateAllLifecyclePhasesForTest();
3175     EXPECT_FALSE(element->GetDisplayLockContext()->IsLocked());
3176     EXPECT_EQ(GetDocument().scrollingElement()->scrollLeft(), 23008.);
3177   }
3178 }
3179 
TEST_F(DisplayLockContextRenderingTest,AutoReachesStableStateOnContentSmallerThanLockedSizeInRtl)3180 TEST_F(DisplayLockContextRenderingTest,
3181        AutoReachesStableStateOnContentSmallerThanLockedSizeInRtl) {
3182   SetHtmlInnerHTML(R"HTML(
3183     <style>
3184       body { writing-mode: vertical-rl }
3185       .spacer { block-size: 20000px; }
3186       .auto {
3187         content-visibility: auto;
3188         contain-intrinsic-size: 20000px 1px;
3189       }
3190       .auto > div {
3191         block-size: 3000px;
3192       }
3193     </style>
3194 
3195     <div class=spacer></div>
3196     <div id=e1 class=auto><div>content</div></div>
3197     <div id=e2 class=auto><div>content</div></div>
3198     <div class=spacer></div>
3199   )HTML");
3200 
3201   UpdateAllLifecyclePhasesForTest();
3202 
3203   GetDocument().scrollingElement()->setScrollLeft(-29000);
3204 
3205   Element* element = GetDocument().getElementById("e1");
3206 
3207   // Note that this test also unlock/relocks #e2 but we only care about #e1
3208   // settling into a steady state.
3209 
3210   // Initially we start with locked in the viewport.
3211   UpdateAllLifecyclePhasesForTest();
3212   EXPECT_TRUE(element->GetDisplayLockContext()->IsLocked());
3213   EXPECT_EQ(GetDocument().scrollingElement()->scrollLeft(), -29000.);
3214 
3215   // It gets unlocked because it's in the viewport.
3216   UpdateAllLifecyclePhasesForTest();
3217   EXPECT_FALSE(element->GetDisplayLockContext()->IsLocked());
3218   EXPECT_EQ(GetDocument().scrollingElement()->scrollLeft(), -29000.);
3219 
3220   // By unlocking it, it shrinks so next time it gets relocked.
3221   UpdateAllLifecyclePhasesForTest();
3222   EXPECT_TRUE(element->GetDisplayLockContext()->IsLocked());
3223   EXPECT_EQ(GetDocument().scrollingElement()->scrollLeft(), -29000.);
3224 
3225   // It again gets unlocked and shrink. This time scroll anchoring puts it right
3226   // off the edge of the screen.
3227   UpdateAllLifecyclePhasesForTest();
3228   EXPECT_FALSE(element->GetDisplayLockContext()->IsLocked());
3229   EXPECT_EQ(GetDocument().scrollingElement()->scrollLeft(), -23008.);
3230 
3231   // On the next update we select the following element as an anchor and the
3232   // scroll offset doesn't change.
3233   UpdateAllLifecyclePhasesForTest();
3234   EXPECT_FALSE(element->GetDisplayLockContext()->IsLocked());
3235   EXPECT_EQ(GetDocument().scrollingElement()->scrollLeft(), -23008.);
3236 
3237   // Subsequent updates are in a stable state.
3238   for (int i = 0; i < 5; ++i) {
3239     UpdateAllLifecyclePhasesForTest();
3240     EXPECT_FALSE(element->GetDisplayLockContext()->IsLocked());
3241     EXPECT_EQ(GetDocument().scrollingElement()->scrollLeft(), -23008.);
3242   }
3243 }
3244 
TEST_F(DisplayLockContextRenderingTest,FirstAutoFramePaintsInViewport)3245 TEST_F(DisplayLockContextRenderingTest, FirstAutoFramePaintsInViewport) {
3246   SetHtmlInnerHTML(R"HTML(
3247     <style>
3248       .spacer { height: 10000px }
3249       .auto {
3250         content-visibility: auto;
3251         contain-intrinsic-size: 1px 200px;
3252       }
3253       .auto > div { height: 100px }
3254     </style>
3255 
3256     <div id=visible><div>content</div></div>
3257     <div class=spacer></div>
3258     <div id=hidden><div>content</div></div>
3259   )HTML");
3260 
3261   auto* visible = GetDocument().getElementById("visible");
3262   auto* hidden = GetDocument().getElementById("hidden");
3263 
3264   visible->classList().Add("auto");
3265   hidden->classList().Add("auto");
3266 
3267   UpdateAllLifecyclePhasesForTest();
3268 
3269   EXPECT_FALSE(visible->GetDisplayLockContext()->IsLocked());
3270   EXPECT_TRUE(hidden->GetDisplayLockContext()->IsLocked());
3271 
3272   EXPECT_FALSE(visible->GetLayoutObject()->SelfNeedsLayout());
3273   EXPECT_FALSE(hidden->GetLayoutObject()->SelfNeedsLayout());
3274 
3275   auto* visible_rect = visible->getBoundingClientRect();
3276   auto* hidden_rect = hidden->getBoundingClientRect();
3277 
3278   EXPECT_FLOAT_EQ(visible_rect->height(), 100);
3279   EXPECT_FLOAT_EQ(hidden_rect->height(), 200);
3280 }
3281 
3282 class DisplayLockContextLegacyRenderingTest
3283     : public RenderingTest,
3284       private ScopedCSSContentVisibilityHiddenMatchableForTest,
3285       private ScopedLayoutNGForTest {
3286  public:
DisplayLockContextLegacyRenderingTest()3287   DisplayLockContextLegacyRenderingTest()
3288       : RenderingTest(MakeGarbageCollected<SingleChildLocalFrameClient>()),
3289         ScopedCSSContentVisibilityHiddenMatchableForTest(true),
3290         ScopedLayoutNGForTest(false) {}
3291 };
3292 
TEST_F(DisplayLockContextLegacyRenderingTest,QuirksHiddenParentBlocksChildLayout)3293 TEST_F(DisplayLockContextLegacyRenderingTest,
3294        QuirksHiddenParentBlocksChildLayout) {
3295   GetDocument().SetCompatibilityMode(Document::kQuirksMode);
3296   SetHtmlInnerHTML(R"HTML(
3297     <style>
3298       .hidden { content-visibility: hidden; }
3299       #grandparent { height: 100px; }
3300       #parent { height: auto; }
3301       #item { height: 10%; }
3302     </style>
3303     <div id=grandparent>
3304       <div id=parent>
3305         <div>
3306           <div id=item></div>
3307         </div>
3308       </div>
3309     </div>
3310   )HTML");
3311 
3312   auto* grandparent = GetDocument().getElementById("grandparent");
3313   auto* parent = GetDocument().getElementById("parent");
3314 
3315   auto* grandparent_box = To<LayoutBox>(grandparent->GetLayoutObject());
3316   auto* item_box = GetLayoutBoxByElementId("item");
3317 
3318   ASSERT_TRUE(grandparent_box);
3319   ASSERT_TRUE(parent->GetLayoutObject());
3320   ASSERT_TRUE(item_box);
3321 
3322   EXPECT_EQ(item_box->PercentHeightContainer(), grandparent_box);
3323   parent->classList().Add("hidden");
3324   grandparent->setAttribute(html_names::kStyleAttr, "height: 150px");
3325 
3326   // This shouldn't DCHECK. We are allowed to have dirty percent height
3327   // descendants in quirks mode since they can cross a display-lock boundary.
3328   UpdateAllLifecyclePhasesForTest();
3329 }
3330 
TEST_F(DisplayLockContextTest,GraphicsLayerBitsNotCheckedInLockedSubtree)3331 TEST_F(DisplayLockContextTest, GraphicsLayerBitsNotCheckedInLockedSubtree) {
3332   ResizeAndFocus();
3333   GetDocument().GetSettings()->SetPreferCompositingToLCDTextEnabled(true);
3334 
3335   SetHtmlInnerHTML(R"HTML(
3336     <style>
3337     div {
3338       width: 100px;
3339       height: 100px;
3340       contain: style layout;
3341       will-change: transform;
3342     }
3343     .locked {
3344       content-visibility: hidden;
3345     }
3346     </style>
3347     <div id=container>
3348       <div>
3349         <div id=target></div>
3350       </div>
3351     </div>
3352   )HTML");
3353 
3354   // Check if the result is correct if we update the contents.
3355   auto* container = GetDocument().getElementById("container");
3356   auto* target = GetDocument().getElementById("target");
3357   auto* target_box = target->GetLayoutBoxModelObject();
3358   ASSERT_TRUE(target_box);
3359   EXPECT_TRUE(target_box->Layer());
3360   EXPECT_TRUE(target_box->HasSelfPaintingLayer());
3361   auto* target_layer = target_box->Layer();
3362   ASSERT_TRUE(target_layer->HasCompositedLayerMapping());
3363 
3364   container->classList().Add("locked");
3365   target_layer->GetCompositedLayerMapping()->SetNeedsGraphicsLayerUpdate(
3366       kGraphicsLayerUpdateLocal);
3367   // This should not DCHECK.
3368   UpdateAllLifecyclePhasesForTest();
3369 
3370   EXPECT_TRUE(
3371       target_layer->GetCompositedLayerMapping()->NeedsGraphicsLayerUpdate());
3372 
3373   container->classList().Remove("locked");
3374   UpdateAllLifecyclePhasesForTest();
3375 
3376   EXPECT_FALSE(
3377       target_layer->GetCompositedLayerMapping()->NeedsGraphicsLayerUpdate());
3378 }
3379 }  // namespace blink
3380