1/*
2 * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
3 * 1999 Lars Knoll <knoll@kde.org>
4 * 1999 Antti Koivisto <koivisto@kde.org>
5 * 2000 Dirk Mueller <mueller@kde.org>
6 * Copyright (C) 2004-2017 Apple Inc. All rights reserved.
7 * (C) 2006 Graham Dennis (graham.dennis@gmail.com)
8 * (C) 2006 Alexey Proskuryakov (ap@nypop.com)
9 * Copyright (C) 2009 Google Inc. All rights reserved.
10 *
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Library General Public
13 * License as published by the Free Software Foundation; either
14 * version 2 of the License, or (at your option) any later version.
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Library General Public License for more details.
20 *
21 * You should have received a copy of the GNU Library General Public License
22 * along with this library; see the file COPYING.LIB. If not, write to
23 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 * Boston, MA 02110-1301, USA.
25 */
26
27#include "config.h"
28#include "FrameView.h"
29
30#include "AXObjectCache.h"
31#include "BackForwardController.h"
32#include "CSSAnimationController.h"
33#include "CachedImage.h"
34#include "CachedResourceLoader.h"
35#include "Chrome.h"
36#include "ChromeClient.h"
37#include "DOMWindow.h"
38#include "DebugPageOverlays.h"
39#include "DeprecatedGlobalSettings.h"
40#include "DocumentLoader.h"
41#include "DocumentMarkerController.h"
42#include "EventHandler.h"
43#include "EventNames.h"
44#include "FloatRect.h"
45#include "FocusController.h"
46#include "Frame.h"
47#include "FrameLoader.h"
48#include "FrameLoaderClient.h"
49#include "FrameSelection.h"
50#include "FrameTree.h"
51#include "GraphicsContext.h"
52#include "HTMLBodyElement.h"
53#include "HTMLEmbedElement.h"
54#include "HTMLFrameElement.h"
55#include "HTMLFrameSetElement.h"
56#include "HTMLHtmlElement.h"
57#include "HTMLIFrameElement.h"
58#include "HTMLNames.h"
59#include "HTMLObjectElement.h"
60#include "HTMLParserIdioms.h"
61#include "HTMLPlugInImageElement.h"
62#include "ImageDocument.h"
63#include "InspectorClient.h"
64#include "InspectorController.h"
65#include "InspectorInstrumentation.h"
66#include "Logging.h"
67#include "MemoryCache.h"
68#include "OverflowEvent.h"
69#include "Page.h"
70#include "PageCache.h"
71#include "PageOverlayController.h"
72#include "ProgressTracker.h"
73#include "RenderEmbeddedObject.h"
74#include "RenderFullScreen.h"
75#include "RenderIFrame.h"
76#include "RenderInline.h"
77#include "RenderLayer.h"
78#include "RenderLayerBacking.h"
79#include "RenderLayerCompositor.h"
80#include "RenderSVGRoot.h"
81#include "RenderScrollbar.h"
82#include "RenderScrollbarPart.h"
83#include "RenderStyle.h"
84#include "RenderText.h"
85#include "RenderTheme.h"
86#include "RenderView.h"
87#include "RenderWidget.h"
88#include "ResizeObserver.h"
89#include "RuntimeEnabledFeatures.h"
90#include "SVGDocument.h"
91#include "SVGSVGElement.h"
92#include "ScriptRunner.h"
93#include "ScriptedAnimationController.h"
94#include "ScrollAnimator.h"
95#include "ScrollingCoordinator.h"
96#include "Settings.h"
97#include "StyleResolver.h"
98#include "StyleScope.h"
99#include "TextResourceDecoder.h"
100#include "TiledBacking.h"
101#include "VisualViewport.h"
102#include "WheelEventTestTrigger.h"
103#include <wtf/text/TextStream.h>
104
105#include <wtf/IsoMallocInlines.h>
106#include <wtf/MemoryPressureHandler.h>
107#include <wtf/Ref.h>
108#include <wtf/SetForScope.h>
109#include <wtf/SystemTracing.h>
110
111#if USE(COORDINATED_GRAPHICS)
112#include "TiledBackingStore.h"
113#endif
114
115#if ENABLE(CSS_SCROLL_SNAP)
116#include "AxisScrollSnapOffsets.h"
117#endif
118
119#if PLATFORM(IOS_FAMILY)
120#include "DocumentLoader.h"
121#include "LegacyTileCache.h"
122#endif
123
124#if PLATFORM(MAC)
125#include "LocalDefaultSystemAppearance.h"
126#endif
127
128#define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(frame().page() && frame().page()->isAlwaysOnLoggingAllowed(), Layout, "%p - FrameView::" fmt, this, ##__VA_ARGS__)
129
130namespace WebCore {
131
132using namespace HTMLNames;
133
134WTF_MAKE_ISO_ALLOCATED_IMPL(FrameView);
135
136MonotonicTime FrameView::sCurrentPaintTimeStamp { };
137
138// The maximum number of updateEmbeddedObjects iterations that should be done before returning.
139static const unsigned maxUpdateEmbeddedObjectsIterations = 2;
140
141static constexpr unsigned defaultSignificantRenderedTextCharacterThreshold = 3000;
142static constexpr float defaultSignificantRenderedTextMeanLength = 50;
143static constexpr unsigned mainArticleSignificantRenderedTextCharacterThreshold = 1500;
144static constexpr float mainArticleSignificantRenderedTextMeanLength = 25;
145
146static OptionSet<RenderLayer::UpdateLayerPositionsFlag> updateLayerPositionFlags(RenderLayer* layer, bool isRelayoutingSubtree, bool didFullRepaint)
147{
148 auto flags = RenderLayer::updateLayerPositionsDefaultFlags();
149 if (didFullRepaint) {
150 flags.remove(RenderLayer::CheckForRepaint);
151 flags.add(RenderLayer::NeedsFullRepaintInBacking);
152 }
153 if (isRelayoutingSubtree && layer->enclosingPaginationLayer(RenderLayer::IncludeCompositedPaginatedLayers))
154 flags.add(RenderLayer::UpdatePagination);
155 return flags;
156}
157
158Pagination::Mode paginationModeForRenderStyle(const RenderStyle& style)
159{
160 Overflow overflow = style.overflowY();
161 if (overflow != Overflow::PagedX && overflow != Overflow::PagedY)
162 return Pagination::Unpaginated;
163
164 bool isHorizontalWritingMode = style.isHorizontalWritingMode();
165 TextDirection textDirection = style.direction();
166 WritingMode writingMode = style.writingMode();
167
168 // paged-x always corresponds to LeftToRightPaginated or RightToLeftPaginated. If the WritingMode
169 // is horizontal, then we use TextDirection to choose between those options. If the WritingMode
170 // is vertical, then the direction of the verticality dictates the choice.
171 if (overflow == Overflow::PagedX) {
172 if ((isHorizontalWritingMode && textDirection == TextDirection::LTR) || writingMode == LeftToRightWritingMode)
173 return Pagination::LeftToRightPaginated;
174 return Pagination::RightToLeftPaginated;
175 }
176
177 // paged-y always corresponds to TopToBottomPaginated or BottomToTopPaginated. If the WritingMode
178 // is horizontal, then the direction of the horizontality dictates the choice. If the WritingMode
179 // is vertical, then we use TextDirection to choose between those options.
180 if (writingMode == TopToBottomWritingMode || (!isHorizontalWritingMode && textDirection == TextDirection::RTL))
181 return Pagination::TopToBottomPaginated;
182 return Pagination::BottomToTopPaginated;
183}
184
185FrameView::FrameView(Frame& frame)
186 : m_frame(frame)
187 , m_layoutContext(*this)
188 , m_updateEmbeddedObjectsTimer(*this, &FrameView::updateEmbeddedObjectsTimerFired)
189 , m_updateWidgetPositionsTimer(*this, &FrameView::updateWidgetPositionsTimerFired)
190 , m_delayedScrollEventTimer(*this, &FrameView::sendScrollEvent)
191 , m_delayedScrollToFocusedElementTimer(*this, &FrameView::scrollToFocusedElementTimerFired)
192 , m_speculativeTilingEnableTimer(*this, &FrameView::speculativeTilingEnableTimerFired)
193{
194 init();
195
196#if ENABLE(RUBBER_BANDING)
197 ScrollElasticity verticalElasticity = ScrollElasticityNone;
198 ScrollElasticity horizontalElasticity = ScrollElasticityNone;
199 if (m_frame->isMainFrame()) {
200 verticalElasticity = m_frame->page() ? m_frame->page()->verticalScrollElasticity() : ScrollElasticityAllowed;
201 horizontalElasticity = m_frame->page() ? m_frame->page()->horizontalScrollElasticity() : ScrollElasticityAllowed;
202 } else if (m_frame->settings().rubberBandingForSubScrollableRegionsEnabled()) {
203 verticalElasticity = ScrollElasticityAutomatic;
204 horizontalElasticity = ScrollElasticityAutomatic;
205 }
206
207 ScrollableArea::setVerticalScrollElasticity(verticalElasticity);
208 ScrollableArea::setHorizontalScrollElasticity(horizontalElasticity);
209#endif
210}
211
212Ref<FrameView> FrameView::create(Frame& frame)
213{
214 Ref<FrameView> view = adoptRef(*new FrameView(frame));
215 if (frame.page() && frame.page()->isVisible())
216 view->show();
217 return view;
218}
219
220Ref<FrameView> FrameView::create(Frame& frame, const IntSize& initialSize)
221{
222 Ref<FrameView> view = adoptRef(*new FrameView(frame));
223 view->Widget::setFrameRect(IntRect(view->location(), initialSize));
224 if (frame.page() && frame.page()->isVisible())
225 view->show();
226 return view;
227}
228
229FrameView::~FrameView()
230{
231 removeFromAXObjectCache();
232 resetScrollbars();
233
234 // Custom scrollbars should already be destroyed at this point
235 ASSERT(!horizontalScrollbar() || !horizontalScrollbar()->isCustomScrollbar());
236 ASSERT(!verticalScrollbar() || !verticalScrollbar()->isCustomScrollbar());
237
238 setHasHorizontalScrollbar(false); // Remove native scrollbars now before we lose the connection to the HostWindow.
239 setHasVerticalScrollbar(false);
240
241 ASSERT(!m_scrollCorner);
242
243 ASSERT(frame().view() != this || !frame().contentRenderer());
244}
245
246void FrameView::reset()
247{
248 m_cannotBlitToWindow = false;
249 m_isOverlapped = false;
250 m_contentIsOpaque = false;
251 m_updateEmbeddedObjectsTimer.stop();
252 m_wasScrolledByUser = false;
253 m_delayedScrollEventTimer.stop();
254 m_shouldScrollToFocusedElement = false;
255 m_delayedScrollToFocusedElementTimer.stop();
256 m_lastViewportSize = IntSize();
257 m_lastZoomFactor = 1.0f;
258 m_isTrackingRepaints = false;
259 m_trackedRepaintRects.clear();
260 m_lastPaintTime = MonotonicTime();
261 m_paintBehavior = PaintBehavior::Normal;
262 m_isPainting = false;
263 m_needsDeferredScrollbarsUpdate = false;
264 m_maintainScrollPositionAnchor = nullptr;
265 resetLayoutMilestones();
266 layoutContext().reset();
267}
268
269void FrameView::resetLayoutMilestones()
270{
271 m_firstLayoutCallbackPending = false;
272 m_isVisuallyNonEmpty = false;
273 m_hasReachedSignificantRenderedTextThreshold = false;
274 m_renderedSignificantAmountOfText = false;
275 m_visuallyNonEmptyCharacterCount = 0;
276 m_visuallyNonEmptyPixelCount = 0;
277 m_textRendererCountForVisuallyNonEmptyCharacters = 0;
278}
279
280void FrameView::removeFromAXObjectCache()
281{
282 if (AXObjectCache* cache = axObjectCache()) {
283 if (HTMLFrameOwnerElement* owner = frame().ownerElement())
284 cache->childrenChanged(owner->renderer());
285 cache->remove(this);
286 }
287}
288
289void FrameView::resetScrollbars()
290{
291 // FIXME: Do we really need this?
292 layoutContext().resetFirstLayoutFlag();
293 // Reset the document's scrollbars back to our defaults before we yield the floor.
294 setScrollbarsSuppressed(true);
295 if (m_canHaveScrollbars)
296 setScrollbarModes(ScrollbarAuto, ScrollbarAuto);
297 else
298 setScrollbarModes(ScrollbarAlwaysOff, ScrollbarAlwaysOff);
299 setScrollbarsSuppressed(false);
300}
301
302void FrameView::resetScrollbarsAndClearContentsSize()
303{
304 resetScrollbars();
305
306 LOG(Layout, "FrameView %p resetScrollbarsAndClearContentsSize", this);
307
308 setScrollbarsSuppressed(true);
309 setContentsSize(IntSize());
310 setScrollbarsSuppressed(false);
311}
312
313void FrameView::init()
314{
315 reset();
316
317 m_margins = LayoutSize(-1, -1); // undefined
318 m_size = LayoutSize();
319
320 // Propagate the marginwidth/height and scrolling modes to the view.
321 Element* ownerElement = frame().ownerElement();
322 if (is<HTMLFrameElementBase>(ownerElement)) {
323 HTMLFrameElementBase& frameElement = downcast<HTMLFrameElementBase>(*ownerElement);
324 if (frameElement.scrollingMode() == ScrollbarAlwaysOff)
325 setCanHaveScrollbars(false);
326 LayoutUnit marginWidth = frameElement.marginWidth();
327 LayoutUnit marginHeight = frameElement.marginHeight();
328 if (marginWidth != -1)
329 setMarginWidth(marginWidth);
330 if (marginHeight != -1)
331 setMarginHeight(marginHeight);
332 }
333
334 Page* page = frame().page();
335 if (page && page->chrome().client().shouldPaintEntireContents())
336 setPaintsEntireContents(true);
337}
338
339void FrameView::prepareForDetach()
340{
341 detachCustomScrollbars();
342 // When the view is no longer associated with a frame, it needs to be removed from the ax object cache
343 // right now, otherwise it won't be able to reach the topDocument()'s axObject cache later.
344 removeFromAXObjectCache();
345
346 if (frame().page()) {
347 if (ScrollingCoordinator* scrollingCoordinator = frame().page()->scrollingCoordinator())
348 scrollingCoordinator->willDestroyScrollableArea(*this);
349 }
350}
351
352void FrameView::detachCustomScrollbars()
353{
354 Scrollbar* horizontalBar = horizontalScrollbar();
355 if (horizontalBar && horizontalBar->isCustomScrollbar())
356 setHasHorizontalScrollbar(false);
357
358 Scrollbar* verticalBar = verticalScrollbar();
359 if (verticalBar && verticalBar->isCustomScrollbar())
360 setHasVerticalScrollbar(false);
361
362 m_scrollCorner = nullptr;
363}
364
365void FrameView::recalculateScrollbarOverlayStyle()
366{
367 ScrollbarOverlayStyle oldOverlayStyle = scrollbarOverlayStyle();
368 Optional<ScrollbarOverlayStyle> clientOverlayStyle = frame().page() ? frame().page()->chrome().client().preferredScrollbarOverlayStyle() : WTF::nullopt;
369 if (clientOverlayStyle) {
370 if (clientOverlayStyle.value() != oldOverlayStyle)
371 setScrollbarOverlayStyle(clientOverlayStyle.value());
372 return;
373 }
374
375 ScrollbarOverlayStyle computedOverlayStyle = ScrollbarOverlayStyleDefault;
376
377 Color backgroundColor = documentBackgroundColor();
378 if (backgroundColor.isValid()) {
379 // Reduce the background color from RGB to a lightness value
380 // and determine which scrollbar style to use based on a lightness
381 // heuristic.
382 double hue, saturation, lightness;
383 backgroundColor.getHSL(hue, saturation, lightness);
384 if (lightness <= .5 && backgroundColor.isVisible())
385 computedOverlayStyle = ScrollbarOverlayStyleLight;
386 else if (!backgroundColor.isVisible() && useDarkAppearance())
387 computedOverlayStyle = ScrollbarOverlayStyleLight;
388 }
389
390 if (oldOverlayStyle != computedOverlayStyle)
391 setScrollbarOverlayStyle(computedOverlayStyle);
392}
393
394#if ENABLE(DARK_MODE_CSS)
395void FrameView::recalculateBaseBackgroundColor()
396{
397 bool usingDarkAppearance = useDarkAppearance();
398 if (m_usesDarkAppearance == usingDarkAppearance)
399 return;
400
401 m_usesDarkAppearance = usingDarkAppearance;
402 Optional<Color> backgroundColor;
403 if (m_isTransparent)
404 backgroundColor = Color(Color::transparent);
405 updateBackgroundRecursively(backgroundColor);
406}
407#endif
408
409void FrameView::clear()
410{
411 setCanBlitOnScroll(true);
412
413 reset();
414
415 setScrollbarsSuppressed(true);
416
417#if PLATFORM(IOS_FAMILY)
418 // To avoid flashes of white, disable tile updates immediately when view is cleared at the beginning of a page load.
419 // Tiling will be re-enabled from UIKit via [WAKWindow setTilingMode:] when we have content to draw.
420 if (LegacyTileCache* tileCache = legacyTileCache())
421 tileCache->setTilingMode(LegacyTileCache::Disabled);
422#endif
423}
424
425#if PLATFORM(IOS_FAMILY)
426void FrameView::didReplaceMultipartContent()
427{
428 // Re-enable tile updates that were disabled in clear().
429 if (LegacyTileCache* tileCache = legacyTileCache())
430 tileCache->setTilingMode(LegacyTileCache::Normal);
431}
432#endif
433
434bool FrameView::didFirstLayout() const
435{
436 return layoutContext().didFirstLayout();
437}
438
439void FrameView::invalidateRect(const IntRect& rect)
440{
441 if (!parent()) {
442 if (auto* page = frame().page())
443 page->chrome().invalidateContentsAndRootView(rect);
444 return;
445 }
446
447 auto* renderer = frame().ownerRenderer();
448 if (!renderer)
449 return;
450
451 IntRect repaintRect = rect;
452 repaintRect.moveBy(roundedIntPoint(renderer->contentBoxLocation()));
453 renderer->repaintRectangle(repaintRect);
454}
455
456void FrameView::setFrameRect(const IntRect& newRect)
457{
458 Ref<FrameView> protectedThis(*this);
459 IntRect oldRect = frameRect();
460 if (newRect == oldRect)
461 return;
462
463 // Every scroll that happens as the result of frame size change is programmatic.
464 auto oldScrollType = currentScrollType();
465 setCurrentScrollType(ScrollType::Programmatic);
466
467 ScrollView::setFrameRect(newRect);
468
469 updateScrollableAreaSet();
470
471 if (RenderView* renderView = this->renderView()) {
472 if (renderView->usesCompositing())
473 renderView->compositor().frameViewDidChangeSize();
474 }
475
476 if (frame().isMainFrame() && frame().page())
477 frame().page()->pageOverlayController().didChangeViewSize();
478
479 viewportContentsChanged();
480 setCurrentScrollType(oldScrollType);
481}
482
483bool FrameView::scheduleAnimation()
484{
485 auto* page = frame().page();
486 if (!page)
487 return false;
488 page->chrome().scheduleAnimation();
489 return true;
490}
491
492void FrameView::setMarginWidth(LayoutUnit w)
493{
494 // make it update the rendering area when set
495 m_margins.setWidth(w);
496}
497
498void FrameView::setMarginHeight(LayoutUnit h)
499{
500 // make it update the rendering area when set
501 m_margins.setHeight(h);
502}
503
504FrameFlattening FrameView::effectiveFrameFlattening() const
505{
506#if PLATFORM(IOS_FAMILY)
507 // On iOS when async frame scrolling is enabled, it does not make sense to use full frame flattening.
508 // In that case, we just consider that frame flattening is disabled. This allows people to test
509 // frame scrolling on iOS by enabling "Async Frame Scrolling" via the Safari menu.
510 if (frame().settings().asyncFrameScrollingEnabled() && frame().settings().frameFlattening() == FrameFlattening::FullyEnabled)
511 return FrameFlattening::Disabled;
512#endif
513 return frame().settings().frameFlattening();
514}
515
516bool FrameView::frameFlatteningEnabled() const
517{
518 return effectiveFrameFlattening() != FrameFlattening::Disabled;
519}
520
521bool FrameView::isFrameFlatteningValidForThisFrame() const
522{
523 if (!frameFlatteningEnabled())
524 return false;
525
526 HTMLFrameOwnerElement* owner = frame().ownerElement();
527 if (!owner)
528 return false;
529
530 // Frame flattening is valid only for <frame> and <iframe>.
531 return owner->hasTagName(frameTag) || owner->hasTagName(iframeTag);
532}
533
534bool FrameView::avoidScrollbarCreation() const
535{
536 // with frame flattening no subframe can have scrollbars
537 // but we also cannot turn scrollbars off as we determine
538 // our flattening policy using that.
539 return isFrameFlatteningValidForThisFrame();
540}
541
542void FrameView::setCanHaveScrollbars(bool canHaveScrollbars)
543{
544 m_canHaveScrollbars = canHaveScrollbars;
545 ScrollView::setCanHaveScrollbars(canHaveScrollbars);
546}
547
548void FrameView::updateCanHaveScrollbars()
549{
550 ScrollbarMode hMode;
551 ScrollbarMode vMode;
552 scrollbarModes(hMode, vMode);
553 if (hMode == ScrollbarAlwaysOff && vMode == ScrollbarAlwaysOff)
554 setCanHaveScrollbars(false);
555 else
556 setCanHaveScrollbars(true);
557}
558
559Ref<Scrollbar> FrameView::createScrollbar(ScrollbarOrientation orientation)
560{
561 // FIXME: We need to update the scrollbar dynamically as documents change (or as doc elements and bodies get discovered that have custom styles).
562 Document* doc = frame().document();
563
564 // Try the <body> element first as a scrollbar source.
565 HTMLElement* body = doc ? doc->bodyOrFrameset() : nullptr;
566 if (body && body->renderer() && body->renderer()->style().hasPseudoStyle(PseudoId::Scrollbar))
567 return RenderScrollbar::createCustomScrollbar(*this, orientation, body);
568
569 // If the <body> didn't have a custom style, then the root element might.
570 Element* docElement = doc ? doc->documentElement() : nullptr;
571 if (docElement && docElement->renderer() && docElement->renderer()->style().hasPseudoStyle(PseudoId::Scrollbar))
572 return RenderScrollbar::createCustomScrollbar(*this, orientation, docElement);
573
574 // If we have an owning iframe/frame element, then it can set the custom scrollbar also.
575 RenderWidget* frameRenderer = frame().ownerRenderer();
576 if (frameRenderer && frameRenderer->style().hasPseudoStyle(PseudoId::Scrollbar))
577 return RenderScrollbar::createCustomScrollbar(*this, orientation, nullptr, &frame());
578
579 // Nobody set a custom style, so we just use a native scrollbar.
580 return ScrollView::createScrollbar(orientation);
581}
582
583void FrameView::didRestoreFromPageCache()
584{
585 // When restoring from page cache, the main frame stays in place while subframes get swapped in.
586 // We update the scrollable area set to ensure that scrolling data structures get invalidated.
587 updateScrollableAreaSet();
588}
589
590void FrameView::willDestroyRenderTree()
591{
592 detachCustomScrollbars();
593 layoutContext().clearSubtreeLayoutRoot();
594}
595
596void FrameView::didDestroyRenderTree()
597{
598 ASSERT(!layoutContext().subtreeLayoutRoot());
599 ASSERT(m_widgetsInRenderTree.isEmpty());
600
601 // If the render tree is destroyed below FrameView::updateEmbeddedObjects(), there will still be a null sentinel in the set.
602 // Everything else should have removed itself as the tree was felled.
603 ASSERT(!m_embeddedObjectsToUpdate || m_embeddedObjectsToUpdate->isEmpty() || (m_embeddedObjectsToUpdate->size() == 1 && m_embeddedObjectsToUpdate->first() == nullptr));
604
605 ASSERT(!m_viewportConstrainedObjects || m_viewportConstrainedObjects->isEmpty());
606 ASSERT(!m_slowRepaintObjects || m_slowRepaintObjects->isEmpty());
607
608 ASSERT(!frame().animation().hasAnimations());
609}
610
611void FrameView::setContentsSize(const IntSize& size)
612{
613 if (size == contentsSize())
614 return;
615
616 layoutContext().disableSetNeedsLayout();
617
618 ScrollView::setContentsSize(size);
619 contentsResized();
620
621 Page* page = frame().page();
622 if (!page)
623 return;
624
625 updateScrollableAreaSet();
626
627 page->chrome().contentsSizeChanged(frame(), size); // Notify only.
628
629 if (frame().isMainFrame()) {
630 page->pageOverlayController().didChangeDocumentSize();
631 PageCache::singleton().markPagesForContentsSizeChanged(*page);
632 }
633 layoutContext().enableSetNeedsLayout();
634}
635
636void FrameView::adjustViewSize()
637{
638 RenderView* renderView = this->renderView();
639 if (!renderView)
640 return;
641
642 ASSERT(frame().view() == this);
643
644 const IntRect rect = renderView->documentRect();
645 const IntSize& size = rect.size();
646 ScrollView::setScrollOrigin(IntPoint(-rect.x(), -rect.y()), !frame().document()->printing(), size == contentsSize());
647
648 LOG_WITH_STREAM(Layout, stream << "FrameView " << this << " adjustViewSize: unscaled document rect changed to " << renderView->unscaledDocumentRect() << " (scaled to " << size << ")");
649
650 setContentsSize(size);
651}
652
653void FrameView::applyOverflowToViewport(const RenderElement& renderer, ScrollbarMode& hMode, ScrollbarMode& vMode)
654{
655 // Handle the overflow:hidden/scroll case for the body/html elements. WinIE treats
656 // overflow:hidden and overflow:scroll on <body> as applying to the document's
657 // scrollbars. The CSS2.1 draft states that HTML UAs should use the <html> or <body> element and XML/XHTML UAs should
658 // use the root element.
659
660 // To combat the inability to scroll on a page with overflow:hidden on the root when scaled, disregard hidden when
661 // there is a frameScaleFactor that is greater than one on the main frame. Also disregard hidden if there is a
662 // header or footer.
663
664 bool overrideHidden = frame().isMainFrame() && ((frame().frameScaleFactor() > 1) || headerHeight() || footerHeight());
665
666 Overflow overflowX = renderer.style().overflowX();
667 Overflow overflowY = renderer.style().overflowY();
668
669 if (is<RenderSVGRoot>(renderer)) {
670 // FIXME: evaluate if we can allow overflow for these cases too.
671 // Overflow is always hidden when stand-alone SVG documents are embedded.
672 if (downcast<RenderSVGRoot>(renderer).isEmbeddedThroughFrameContainingSVGDocument()) {
673 overflowX = Overflow::Hidden;
674 overflowY = Overflow::Hidden;
675 }
676 }
677
678 switch (overflowX) {
679 case Overflow::Hidden:
680 if (overrideHidden)
681 hMode = ScrollbarAuto;
682 else
683 hMode = ScrollbarAlwaysOff;
684 break;
685 case Overflow::Scroll:
686 hMode = ScrollbarAlwaysOn;
687 break;
688 case Overflow::Auto:
689 hMode = ScrollbarAuto;
690 break;
691 default:
692 // Don't set it at all.
693 ;
694 }
695
696 switch (overflowY) {
697 case Overflow::Hidden:
698 if (overrideHidden)
699 vMode = ScrollbarAuto;
700 else
701 vMode = ScrollbarAlwaysOff;
702 break;
703 case Overflow::Scroll:
704 vMode = ScrollbarAlwaysOn;
705 break;
706 case Overflow::Auto:
707 vMode = ScrollbarAuto;
708 break;
709 default:
710 // Don't set it at all. Values of Overflow::PagedX and Overflow::PagedY are handled by applyPaginationToViewPort().
711 ;
712 }
713}
714
715void FrameView::applyPaginationToViewport()
716{
717 auto* document = frame().document();
718 auto* documentElement = document ? document->documentElement() : nullptr;
719 if (!documentElement || !documentElement->renderer()) {
720 setPagination(Pagination());
721 return;
722 }
723
724 auto& documentRenderer = *documentElement->renderer();
725 auto* documentOrBodyRenderer = &documentRenderer;
726
727 auto* body = document->body();
728 if (body && body->renderer()) {
729 documentOrBodyRenderer = documentRenderer.style().overflowX() == Overflow::Visible && is<HTMLHtmlElement>(*documentElement) ?
730 body->renderer() : &documentRenderer;
731 }
732
733 Pagination pagination;
734 Overflow overflowY = documentOrBodyRenderer->style().overflowY();
735 if (overflowY == Overflow::PagedX || overflowY == Overflow::PagedY) {
736 pagination.mode = WebCore::paginationModeForRenderStyle(documentOrBodyRenderer->style());
737 GapLength columnGapLength = documentOrBodyRenderer->style().columnGap();
738 pagination.gap = 0;
739 if (!columnGapLength.isNormal()) {
740 if (auto* containerForPaginationGap = is<RenderBox>(documentOrBodyRenderer) ? downcast<RenderBox>(documentOrBodyRenderer) : documentOrBodyRenderer->containingBlock())
741 pagination.gap = valueForLength(columnGapLength.length(), containerForPaginationGap->availableLogicalWidth()).toUnsigned();
742 }
743 }
744 setPagination(pagination);
745}
746
747void FrameView::calculateScrollbarModesForLayout(ScrollbarMode& hMode, ScrollbarMode& vMode, ScrollbarModesCalculationStrategy strategy)
748{
749 m_viewportRendererType = ViewportRendererType::None;
750
751 const HTMLFrameOwnerElement* owner = frame().ownerElement();
752 if (owner && (owner->scrollingMode() == ScrollbarAlwaysOff)) {
753 hMode = ScrollbarAlwaysOff;
754 vMode = ScrollbarAlwaysOff;
755 return;
756 }
757
758 if (m_canHaveScrollbars || strategy == RulesFromWebContentOnly) {
759 hMode = ScrollbarAuto;
760 vMode = ScrollbarAuto;
761 } else {
762 hMode = ScrollbarAlwaysOff;
763 vMode = ScrollbarAlwaysOff;
764 }
765
766 if (layoutContext().subtreeLayoutRoot())
767 return;
768
769 auto* document = frame().document();
770 if (!document)
771 return;
772
773 auto* documentElement = document->documentElement();
774 if (!documentElement)
775 return;
776
777 auto* bodyOrFrameset = document->bodyOrFrameset();
778 auto* rootRenderer = documentElement->renderer();
779 if (!bodyOrFrameset || !bodyOrFrameset->renderer()) {
780 if (rootRenderer) {
781 applyOverflowToViewport(*rootRenderer, hMode, vMode);
782 m_viewportRendererType = ViewportRendererType::Document;
783 }
784 return;
785 }
786
787 if (is<HTMLFrameSetElement>(*bodyOrFrameset) && !frameFlatteningEnabled()) {
788 vMode = ScrollbarAlwaysOff;
789 hMode = ScrollbarAlwaysOff;
790 return;
791 }
792
793 if (is<HTMLBodyElement>(*bodyOrFrameset) && rootRenderer) {
794 // It's sufficient to just check the X overflow,
795 // since it's illegal to have visible in only one direction.
796 if (rootRenderer->style().overflowX() == Overflow::Visible && is<HTMLHtmlElement>(documentElement)) {
797 auto* bodyRenderer = bodyOrFrameset->renderer();
798 if (bodyRenderer) {
799 applyOverflowToViewport(*bodyRenderer, hMode, vMode);
800 m_viewportRendererType = ViewportRendererType::Body;
801 }
802 } else {
803 applyOverflowToViewport(*rootRenderer, hMode, vMode);
804 m_viewportRendererType = ViewportRendererType::Document;
805 }
806 }
807}
808
809void FrameView::willRecalcStyle()
810{
811 RenderView* renderView = this->renderView();
812 if (!renderView)
813 return;
814
815 renderView->compositor().willRecalcStyle();
816}
817
818bool FrameView::updateCompositingLayersAfterStyleChange()
819{
820 // If we expect to update compositing after an incipient layout, don't do so here.
821 if (!renderView() || needsLayout() || layoutContext().isInLayout())
822 return false;
823 return renderView()->compositor().didRecalcStyleWithNoPendingLayout();
824}
825
826void FrameView::updateCompositingLayersAfterLayout()
827{
828 RenderView* renderView = this->renderView();
829 if (!renderView)
830 return;
831
832 renderView->compositor().updateCompositingLayers(CompositingUpdateType::AfterLayout);
833}
834
835GraphicsLayer* FrameView::layerForHorizontalScrollbar() const
836{
837 RenderView* renderView = this->renderView();
838 if (!renderView)
839 return nullptr;
840 return renderView->compositor().layerForHorizontalScrollbar();
841}
842
843GraphicsLayer* FrameView::layerForVerticalScrollbar() const
844{
845 RenderView* renderView = this->renderView();
846 if (!renderView)
847 return nullptr;
848 return renderView->compositor().layerForVerticalScrollbar();
849}
850
851GraphicsLayer* FrameView::layerForScrollCorner() const
852{
853 RenderView* renderView = this->renderView();
854 if (!renderView)
855 return nullptr;
856 return renderView->compositor().layerForScrollCorner();
857}
858
859TiledBacking* FrameView::tiledBacking() const
860{
861 RenderView* renderView = this->renderView();
862 if (!renderView)
863 return nullptr;
864
865 RenderLayerBacking* backing = renderView->layer()->backing();
866 if (!backing)
867 return nullptr;
868
869 return backing->tiledBacking();
870}
871
872ScrollingNodeID FrameView::scrollingNodeID() const
873{
874 RenderView* renderView = this->renderView();
875 if (!renderView)
876 return 0;
877
878 RenderLayerBacking* backing = renderView->layer()->backing();
879 if (!backing)
880 return 0;
881
882 return backing->scrollingNodeIDForRole(ScrollCoordinationRole::Scrolling);
883}
884
885ScrollableArea* FrameView::scrollableAreaForScrollLayerID(uint64_t nodeID) const
886{
887 RenderView* renderView = this->renderView();
888 if (!renderView)
889 return nullptr;
890
891 return renderView->compositor().scrollableAreaForScrollLayerID(nodeID);
892}
893
894#if ENABLE(RUBBER_BANDING)
895GraphicsLayer* FrameView::layerForOverhangAreas() const
896{
897 RenderView* renderView = this->renderView();
898 if (!renderView)
899 return nullptr;
900 return renderView->compositor().layerForOverhangAreas();
901}
902
903GraphicsLayer* FrameView::setWantsLayerForTopOverHangArea(bool wantsLayer) const
904{
905 RenderView* renderView = this->renderView();
906 if (!renderView)
907 return nullptr;
908
909 return renderView->compositor().updateLayerForTopOverhangArea(wantsLayer);
910}
911
912GraphicsLayer* FrameView::setWantsLayerForBottomOverHangArea(bool wantsLayer) const
913{
914 RenderView* renderView = this->renderView();
915 if (!renderView)
916 return nullptr;
917
918 return renderView->compositor().updateLayerForBottomOverhangArea(wantsLayer);
919}
920
921#endif // ENABLE(RUBBER_BANDING)
922
923#if ENABLE(CSS_SCROLL_SNAP)
924void FrameView::updateSnapOffsets()
925{
926 if (!frame().document())
927 return;
928
929 // FIXME: Should we allow specifying snap points through <html> tags too?
930 HTMLElement* body = frame().document()->bodyOrFrameset();
931 if (!renderView() || !body || !body->renderer())
932 return;
933
934 updateSnapOffsetsForScrollableArea(*this, *body, *renderView(), body->renderer()->style());
935}
936
937bool FrameView::isScrollSnapInProgress() const
938{
939 if (scrollbarsSuppressed())
940 return false;
941
942 // If the scrolling thread updates the scroll position for this FrameView, then we should return
943 // ScrollingCoordinator::isScrollSnapInProgress().
944 if (Page* page = frame().page()) {
945 if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) {
946 if (!scrollingCoordinator->shouldUpdateScrollLayerPositionSynchronously(*this))
947 return scrollingCoordinator->isScrollSnapInProgress();
948 }
949 }
950
951 // If the main thread updates the scroll position for this FrameView, we should return
952 // ScrollAnimator::isScrollSnapInProgress().
953 if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
954 return scrollAnimator->isScrollSnapInProgress();
955
956 return false;
957}
958
959void FrameView::updateScrollingCoordinatorScrollSnapProperties() const
960{
961 renderView()->compositor().updateScrollSnapPropertiesWithFrameView(*this);
962}
963#endif
964
965bool FrameView::flushCompositingStateForThisFrame(const Frame& rootFrameForFlush)
966{
967 RenderView* renderView = this->renderView();
968 if (!renderView)
969 return true; // We don't want to keep trying to update layers if we have no renderer.
970
971 ASSERT(frame().view() == this);
972
973 // If we sync compositing layers when a layout is pending, we may cause painting of compositing
974 // layer content to occur before layout has happened, which will cause paintContents() to bail.
975 if (needsLayout())
976 return false;
977
978#if PLATFORM(IOS_FAMILY)
979 if (LegacyTileCache* tileCache = legacyTileCache())
980 tileCache->doPendingRepaints();
981#endif
982
983 renderView->compositor().flushPendingLayerChanges(&rootFrameForFlush == m_frame.ptr());
984
985 return true;
986}
987
988void FrameView::setNeedsOneShotDrawingSynchronization()
989{
990 if (Page* page = frame().page())
991 page->chrome().client().setNeedsOneShotDrawingSynchronization();
992}
993
994GraphicsLayer* FrameView::graphicsLayerForPlatformWidget(PlatformWidget platformWidget)
995{
996 // To find the Widget that corresponds with platformWidget we have to do a linear
997 // search of our child widgets.
998 const Widget* foundWidget = nullptr;
999 for (auto& widget : children()) {
1000 if (widget->platformWidget() != platformWidget)
1001 continue;
1002 foundWidget = widget.ptr();
1003 break;
1004 }
1005
1006 if (!foundWidget)
1007 return nullptr;
1008
1009 auto* renderWidget = RenderWidget::find(*foundWidget);
1010 if (!renderWidget)
1011 return nullptr;
1012
1013 auto* widgetLayer = renderWidget->layer();
1014 if (!widgetLayer || !widgetLayer->isComposited())
1015 return nullptr;
1016
1017 return widgetLayer->backing()->parentForSublayers();
1018}
1019
1020void FrameView::scheduleLayerFlushAllowingThrottling()
1021{
1022 RenderView* view = this->renderView();
1023 if (!view)
1024 return;
1025 view->compositor().scheduleLayerFlush(true /* canThrottle */);
1026}
1027
1028LayoutRect FrameView::fixedScrollableAreaBoundsInflatedForScrolling(const LayoutRect& uninflatedBounds) const
1029{
1030 LayoutPoint scrollPosition;
1031 LayoutSize topLeftExpansion;
1032 LayoutSize bottomRightExpansion;
1033
1034 if (frame().settings().visualViewportEnabled()) {
1035 // FIXME: this is wrong under zooming; uninflatedBounds is scaled but the scroll positions are not.
1036 scrollPosition = layoutViewportRect().location();
1037 topLeftExpansion = scrollPosition - unscaledMinimumScrollPosition();
1038 bottomRightExpansion = unscaledMaximumScrollPosition() - scrollPosition;
1039 } else {
1040 scrollPosition = scrollPositionRespectingCustomFixedPosition();
1041 topLeftExpansion = scrollPosition - minimumScrollPosition();
1042 bottomRightExpansion = maximumScrollPosition() - scrollPosition;
1043 }
1044
1045 return LayoutRect(uninflatedBounds.location() - topLeftExpansion, uninflatedBounds.size() + topLeftExpansion + bottomRightExpansion);
1046}
1047
1048LayoutPoint FrameView::scrollPositionRespectingCustomFixedPosition() const
1049{
1050#if PLATFORM(IOS_FAMILY)
1051 if (!frame().settings().visualViewportEnabled())
1052 return useCustomFixedPositionLayoutRect() ? customFixedPositionLayoutRect().location() : scrollPosition();
1053#endif
1054
1055 return scrollPositionForFixedPosition();
1056}
1057
1058int FrameView::headerHeight() const
1059{
1060 if (!frame().isMainFrame())
1061 return 0;
1062 Page* page = frame().page();
1063 return page ? page->headerHeight() : 0;
1064}
1065
1066int FrameView::footerHeight() const
1067{
1068 if (!frame().isMainFrame())
1069 return 0;
1070 Page* page = frame().page();
1071 return page ? page->footerHeight() : 0;
1072}
1073
1074float FrameView::topContentInset(TopContentInsetType contentInsetTypeToReturn) const
1075{
1076 if (platformWidget() && contentInsetTypeToReturn == TopContentInsetType::WebCoreOrPlatformContentInset)
1077 return platformTopContentInset();
1078
1079 if (!frame().isMainFrame())
1080 return 0;
1081
1082 Page* page = frame().page();
1083 return page ? page->topContentInset() : 0;
1084}
1085
1086void FrameView::topContentInsetDidChange(float newTopContentInset)
1087{
1088 RenderView* renderView = this->renderView();
1089 if (!renderView)
1090 return;
1091
1092 if (platformWidget())
1093 platformSetTopContentInset(newTopContentInset);
1094
1095 layoutContext().layout();
1096 // Every scroll that happens as the result of content inset change is programmatic.
1097 auto oldScrollType = currentScrollType();
1098 setCurrentScrollType(ScrollType::Programmatic);
1099
1100 updateScrollbars(scrollPosition());
1101 if (renderView->usesCompositing())
1102 renderView->compositor().frameViewDidChangeSize();
1103
1104 if (TiledBacking* tiledBacking = this->tiledBacking())
1105 tiledBacking->setTopContentInset(newTopContentInset);
1106
1107 setCurrentScrollType(oldScrollType);
1108}
1109
1110void FrameView::topContentDirectionDidChange()
1111{
1112 m_needsDeferredScrollbarsUpdate = true;
1113}
1114
1115void FrameView::handleDeferredScrollbarsUpdateAfterDirectionChange()
1116{
1117 if (!m_needsDeferredScrollbarsUpdate)
1118 return;
1119
1120 m_needsDeferredScrollbarsUpdate = false;
1121
1122 updateScrollbars(scrollPosition());
1123 positionScrollbarLayers();
1124}
1125
1126// Sometimes (for plug-ins) we need to eagerly go into compositing mode.
1127void FrameView::enterCompositingMode()
1128{
1129 if (RenderView* renderView = this->renderView()) {
1130 renderView->compositor().enableCompositingMode();
1131 if (!needsLayout())
1132 renderView->compositor().scheduleCompositingLayerUpdate();
1133 }
1134}
1135
1136bool FrameView::isEnclosedInCompositingLayer() const
1137{
1138 auto frameOwnerRenderer = frame().ownerRenderer();
1139 if (frameOwnerRenderer && frameOwnerRenderer->containerForRepaint())
1140 return true;
1141
1142 if (FrameView* parentView = parentFrameView())
1143 return parentView->isEnclosedInCompositingLayer();
1144 return false;
1145}
1146
1147bool FrameView::flushCompositingStateIncludingSubframes()
1148{
1149#if PLATFORM(COCOA)
1150 InspectorInstrumentation::willComposite(frame());
1151#endif
1152
1153 bool allFramesFlushed = flushCompositingStateForThisFrame(frame());
1154
1155 for (Frame* child = frame().tree().firstRenderedChild(); child; child = child->tree().traverseNextRendered(m_frame.ptr())) {
1156 if (!child->view())
1157 continue;
1158 bool flushed = child->view()->flushCompositingStateForThisFrame(frame());
1159 allFramesFlushed &= flushed;
1160 }
1161 return allFramesFlushed;
1162}
1163
1164bool FrameView::isSoftwareRenderable() const
1165{
1166 RenderView* renderView = this->renderView();
1167 return !renderView || !renderView->compositor().has3DContent();
1168}
1169
1170void FrameView::setIsInWindow(bool isInWindow)
1171{
1172 if (RenderView* renderView = this->renderView())
1173 renderView->setIsInWindow(isInWindow);
1174}
1175
1176void FrameView::forceLayoutParentViewIfNeeded()
1177{
1178 RenderWidget* ownerRenderer = frame().ownerRenderer();
1179 if (!ownerRenderer)
1180 return;
1181
1182 RenderBox* contentBox = embeddedContentBox();
1183 if (!contentBox)
1184 return;
1185
1186 auto& svgRoot = downcast<RenderSVGRoot>(*contentBox);
1187 if (svgRoot.everHadLayout() && !svgRoot.needsLayout())
1188 return;
1189
1190 LOG(Layout, "FrameView %p forceLayoutParentViewIfNeeded scheduling layout on parent FrameView %p", this, &ownerRenderer->view().frameView());
1191
1192 // If the embedded SVG document appears the first time, the ownerRenderer has already finished
1193 // layout without knowing about the existence of the embedded SVG document, because RenderReplaced
1194 // embeddedContentBox() returns nullptr, as long as the embedded document isn't loaded yet. Before
1195 // bothering to lay out the SVG document, mark the ownerRenderer needing layout and ask its
1196 // FrameView for a layout. After that the RenderEmbeddedObject (ownerRenderer) carries the
1197 // correct size, which RenderSVGRoot::computeReplacedLogicalWidth/Height rely on, when laying
1198 // out for the first time, or when the RenderSVGRoot size has changed dynamically (eg. via <script>).
1199
1200 ownerRenderer->setNeedsLayoutAndPrefWidthsRecalc();
1201 ownerRenderer->view().frameView().layoutContext().scheduleLayout();
1202}
1203
1204void FrameView::markRootOrBodyRendererDirty() const
1205{
1206 auto& document = *frame().document();
1207 RenderBox* rootRenderer = document.documentElement() ? document.documentElement()->renderBox() : nullptr;
1208 auto* body = document.bodyOrFrameset();
1209 RenderBox* bodyRenderer = rootRenderer && body ? body->renderBox() : nullptr;
1210 if (bodyRenderer && bodyRenderer->stretchesToViewport())
1211 bodyRenderer->setChildNeedsLayout();
1212 else if (rootRenderer && rootRenderer->stretchesToViewport())
1213 rootRenderer->setChildNeedsLayout();
1214}
1215
1216void FrameView::adjustScrollbarsForLayout(bool isFirstLayout)
1217{
1218 ScrollbarMode hMode;
1219 ScrollbarMode vMode;
1220 calculateScrollbarModesForLayout(hMode, vMode);
1221 if (isFirstLayout && !layoutContext().isLayoutNested()) {
1222 setScrollbarsSuppressed(true);
1223 // Set the initial vMode to AlwaysOn if we're auto.
1224 if (vMode == ScrollbarAuto)
1225 setVerticalScrollbarMode(ScrollbarAlwaysOn); // This causes a vertical scrollbar to appear.
1226 // Set the initial hMode to AlwaysOff if we're auto.
1227 if (hMode == ScrollbarAuto)
1228 setHorizontalScrollbarMode(ScrollbarAlwaysOff); // This causes a horizontal scrollbar to disappear.
1229 ASSERT(frame().page());
1230 if (frame().page()->expectsWheelEventTriggers())
1231 scrollAnimator().setWheelEventTestTrigger(frame().page()->testTrigger());
1232 setScrollbarModes(hMode, vMode);
1233 setScrollbarsSuppressed(false, true);
1234 } else if (hMode != horizontalScrollbarMode() || vMode != verticalScrollbarMode())
1235 setScrollbarModes(hMode, vMode);
1236}
1237
1238void FrameView::willDoLayout(WeakPtr<RenderElement> layoutRoot)
1239{
1240 bool subtreeLayout = !is<RenderView>(*layoutRoot);
1241 if (subtreeLayout)
1242 return;
1243
1244 if (auto* body = frame().document()->bodyOrFrameset()) {
1245 if (is<HTMLFrameSetElement>(*body) && !frameFlatteningEnabled() && body->renderer())
1246 body->renderer()->setChildNeedsLayout();
1247 }
1248 auto firstLayout = !layoutContext().didFirstLayout();
1249 if (firstLayout) {
1250 m_lastViewportSize = sizeForResizeEvent();
1251 m_lastZoomFactor = layoutRoot->style().zoom();
1252 m_firstLayoutCallbackPending = true;
1253 }
1254 adjustScrollbarsForLayout(firstLayout);
1255
1256 auto oldSize = m_size;
1257 LayoutSize newSize = layoutSize();
1258 if (oldSize != newSize) {
1259 m_size = newSize;
1260 LOG(Layout, " layout size changed from %.3fx%.3f to %.3fx%.3f", oldSize.width().toFloat(), oldSize.height().toFloat(), newSize.width().toFloat(), newSize.height().toFloat());
1261 layoutContext().setNeedsFullRepaint();
1262 if (!firstLayout)
1263 markRootOrBodyRendererDirty();
1264 }
1265 forceLayoutParentViewIfNeeded();
1266}
1267
1268void FrameView::didLayout(WeakPtr<RenderElement> layoutRoot)
1269{
1270 renderView()->releaseProtectedRenderWidgets();
1271 auto* layoutRootEnclosingLayer = layoutRoot->enclosingLayer();
1272 layoutRootEnclosingLayer->updateLayerPositionsAfterLayout(renderView()->layer(), updateLayerPositionFlags(layoutRootEnclosingLayer, !is<RenderView>(*layoutRoot), layoutContext().needsFullRepaint()));
1273
1274 updateCompositingLayersAfterLayout();
1275
1276#if PLATFORM(COCOA) || PLATFORM(WIN) || PLATFORM(GTK)
1277 if (auto* cache = frame().document()->existingAXObjectCache())
1278 cache->postNotification(layoutRoot.get(), AXObjectCache::AXLayoutComplete);
1279#endif
1280
1281 frame().document()->invalidateRenderingDependentRegions(Document::AnnotationsAction::Update);
1282
1283 updateCanBlitOnScrollRecursively();
1284
1285 handleDeferredScrollUpdateAfterContentSizeChange();
1286
1287 handleDeferredScrollbarsUpdateAfterDirectionChange();
1288
1289 if (frame().document()->hasListenerType(Document::OVERFLOWCHANGED_LISTENER))
1290 updateOverflowStatus(layoutWidth() < contentsWidth(), layoutHeight() < contentsHeight());
1291
1292 frame().document()->markers().invalidateRectsForAllMarkers();
1293}
1294
1295bool FrameView::shouldDeferScrollUpdateAfterContentSizeChange()
1296{
1297 return (layoutContext().layoutPhase() < FrameViewLayoutContext::LayoutPhase::InPostLayout) && (layoutContext().layoutPhase() != FrameViewLayoutContext::LayoutPhase::OutsideLayout);
1298}
1299
1300RenderBox* FrameView::embeddedContentBox() const
1301{
1302 RenderView* renderView = this->renderView();
1303 if (!renderView)
1304 return nullptr;
1305
1306 RenderObject* firstChild = renderView->firstChild();
1307
1308 // Curently only embedded SVG documents participate in the size-negotiation logic.
1309 if (is<RenderSVGRoot>(firstChild))
1310 return downcast<RenderSVGRoot>(firstChild);
1311
1312 return nullptr;
1313}
1314
1315void FrameView::addEmbeddedObjectToUpdate(RenderEmbeddedObject& embeddedObject)
1316{
1317 if (!m_embeddedObjectsToUpdate)
1318 m_embeddedObjectsToUpdate = std::make_unique<ListHashSet<RenderEmbeddedObject*>>();
1319
1320 HTMLFrameOwnerElement& element = embeddedObject.frameOwnerElement();
1321 if (is<HTMLObjectElement>(element) || is<HTMLEmbedElement>(element)) {
1322 // Tell the DOM element that it needs a widget update.
1323 HTMLPlugInImageElement& pluginElement = downcast<HTMLPlugInImageElement>(element);
1324 if (!pluginElement.needsCheckForSizeChange())
1325 pluginElement.setNeedsWidgetUpdate(true);
1326 }
1327
1328 m_embeddedObjectsToUpdate->add(&embeddedObject);
1329}
1330
1331void FrameView::removeEmbeddedObjectToUpdate(RenderEmbeddedObject& embeddedObject)
1332{
1333 if (!m_embeddedObjectsToUpdate)
1334 return;
1335
1336 m_embeddedObjectsToUpdate->remove(&embeddedObject);
1337}
1338
1339void FrameView::setMediaType(const String& mediaType)
1340{
1341 m_mediaType = mediaType;
1342}
1343
1344String FrameView::mediaType() const
1345{
1346 // See if we have an override type.
1347 String overrideType = frame().loader().client().overrideMediaType();
1348 InspectorInstrumentation::applyEmulatedMedia(frame(), overrideType);
1349 if (!overrideType.isNull())
1350 return overrideType;
1351 return m_mediaType;
1352}
1353
1354void FrameView::adjustMediaTypeForPrinting(bool printing)
1355{
1356 if (printing) {
1357 if (m_mediaTypeWhenNotPrinting.isNull())
1358 m_mediaTypeWhenNotPrinting = mediaType();
1359 setMediaType("print");
1360 } else {
1361 if (!m_mediaTypeWhenNotPrinting.isNull())
1362 setMediaType(m_mediaTypeWhenNotPrinting);
1363 m_mediaTypeWhenNotPrinting = String();
1364 }
1365}
1366
1367bool FrameView::useSlowRepaints(bool considerOverlap) const
1368{
1369 bool mustBeSlow = hasSlowRepaintObjects() || (platformWidget() && hasViewportConstrainedObjects());
1370
1371 // FIXME: WidgetMac.mm makes the assumption that useSlowRepaints ==
1372 // m_contentIsOpaque, so don't take the fast path for composited layers
1373 // if they are a platform widget in order to get painting correctness
1374 // for transparent layers. See the comment in WidgetMac::paint.
1375 if (usesCompositedScrolling() && !platformWidget())
1376 return mustBeSlow;
1377
1378 bool isOverlapped = m_isOverlapped && considerOverlap;
1379
1380 if (mustBeSlow || m_cannotBlitToWindow || isOverlapped || !m_contentIsOpaque)
1381 return true;
1382
1383 if (FrameView* parentView = parentFrameView())
1384 return parentView->useSlowRepaints(considerOverlap);
1385
1386 return false;
1387}
1388
1389bool FrameView::useSlowRepaintsIfNotOverlapped() const
1390{
1391 return useSlowRepaints(false);
1392}
1393
1394void FrameView::updateCanBlitOnScrollRecursively()
1395{
1396 for (auto* frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) {
1397 if (FrameView* view = frame->view())
1398 view->setCanBlitOnScroll(!view->useSlowRepaints());
1399 }
1400}
1401
1402bool FrameView::usesCompositedScrolling() const
1403{
1404 RenderView* renderView = this->renderView();
1405 if (renderView && renderView->isComposited()) {
1406 GraphicsLayer* layer = renderView->layer()->backing()->graphicsLayer();
1407 if (layer && layer->drawsContent())
1408 return true;
1409 }
1410
1411 return false;
1412}
1413
1414bool FrameView::usesAsyncScrolling() const
1415{
1416#if ENABLE(ASYNC_SCROLLING)
1417 if (Page* page = frame().page()) {
1418 if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator())
1419 return scrollingCoordinator->coordinatesScrollingForFrameView(*this);
1420 }
1421#endif
1422 return false;
1423}
1424
1425bool FrameView::usesMockScrollAnimator() const
1426{
1427 return DeprecatedGlobalSettings::usesMockScrollAnimator();
1428}
1429
1430void FrameView::logMockScrollAnimatorMessage(const String& message) const
1431{
1432 Document* document = frame().document();
1433 if (!document)
1434 return;
1435 StringBuilder builder;
1436 if (frame().isMainFrame())
1437 builder.appendLiteral("Main");
1438 builder.appendLiteral("FrameView: ");
1439 builder.append(message);
1440 document->addConsoleMessage(MessageSource::Other, MessageLevel::Debug, builder.toString());
1441}
1442
1443void FrameView::setCannotBlitToWindow()
1444{
1445 m_cannotBlitToWindow = true;
1446 updateCanBlitOnScrollRecursively();
1447}
1448
1449void FrameView::addSlowRepaintObject(RenderElement& renderer)
1450{
1451 bool hadSlowRepaintObjects = hasSlowRepaintObjects();
1452
1453 if (!m_slowRepaintObjects)
1454 m_slowRepaintObjects = std::make_unique<HashSet<const RenderElement*>>();
1455
1456 m_slowRepaintObjects->add(&renderer);
1457 if (hadSlowRepaintObjects)
1458 return;
1459
1460 updateCanBlitOnScrollRecursively();
1461
1462 if (auto* page = frame().page()) {
1463 if (auto* scrollingCoordinator = page->scrollingCoordinator())
1464 scrollingCoordinator->frameViewHasSlowRepaintObjectsDidChange(*this);
1465 }
1466}
1467
1468void FrameView::removeSlowRepaintObject(RenderElement& renderer)
1469{
1470 if (!m_slowRepaintObjects)
1471 return;
1472
1473 m_slowRepaintObjects->remove(&renderer);
1474 if (!m_slowRepaintObjects->isEmpty())
1475 return;
1476
1477 m_slowRepaintObjects = nullptr;
1478 updateCanBlitOnScrollRecursively();
1479
1480 if (auto* page = frame().page()) {
1481 if (auto* scrollingCoordinator = page->scrollingCoordinator())
1482 scrollingCoordinator->frameViewHasSlowRepaintObjectsDidChange(*this);
1483 }
1484}
1485
1486void FrameView::addViewportConstrainedObject(RenderLayerModelObject* object)
1487{
1488 if (!m_viewportConstrainedObjects)
1489 m_viewportConstrainedObjects = std::make_unique<ViewportConstrainedObjectSet>();
1490
1491 if (!m_viewportConstrainedObjects->contains(object)) {
1492 m_viewportConstrainedObjects->add(object);
1493 if (platformWidget())
1494 updateCanBlitOnScrollRecursively();
1495
1496 if (Page* page = frame().page()) {
1497 if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator())
1498 scrollingCoordinator->frameViewFixedObjectsDidChange(*this);
1499 }
1500 }
1501}
1502
1503void FrameView::removeViewportConstrainedObject(RenderLayerModelObject* object)
1504{
1505 if (m_viewportConstrainedObjects && m_viewportConstrainedObjects->remove(object)) {
1506 if (Page* page = frame().page()) {
1507 if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator())
1508 scrollingCoordinator->frameViewFixedObjectsDidChange(*this);
1509 }
1510
1511 // FIXME: In addFixedObject() we only call this if there's a platform widget,
1512 // why isn't the same check being made here?
1513 updateCanBlitOnScrollRecursively();
1514 }
1515}
1516
1517LayoutSize FrameView::expandedLayoutViewportSize(const LayoutSize& baseLayoutViewportSize, const LayoutSize& documentSize, double heightExpansionFactor)
1518{
1519 if (!heightExpansionFactor)
1520 return baseLayoutViewportSize;
1521
1522 auto documentHeight = documentSize.height();
1523 auto layoutViewportHeight = baseLayoutViewportSize.height();
1524 if (layoutViewportHeight > documentHeight)
1525 return baseLayoutViewportSize;
1526
1527 return { baseLayoutViewportSize.width(), std::min<LayoutUnit>(documentHeight, (1 + heightExpansionFactor) * layoutViewportHeight) };
1528}
1529
1530LayoutRect FrameView::computeUpdatedLayoutViewportRect(const LayoutRect& layoutViewport, const LayoutRect& documentRect, const LayoutSize& unobscuredContentSize, const LayoutRect& unobscuredContentRect, const LayoutSize& baseLayoutViewportSize, const LayoutPoint& stableLayoutViewportOriginMin, const LayoutPoint& stableLayoutViewportOriginMax, LayoutViewportConstraint constraint)
1531{
1532 LayoutRect layoutViewportRect = layoutViewport;
1533
1534 // The layout viewport is never smaller than baseLayoutViewportSize, and never be smaller than the unobscuredContentRect.
1535 LayoutSize constrainedSize = baseLayoutViewportSize;
1536 layoutViewportRect.setSize(constrainedSize.expandedTo(unobscuredContentSize));
1537
1538 LayoutPoint layoutViewportOrigin = computeLayoutViewportOrigin(unobscuredContentRect, stableLayoutViewportOriginMin, stableLayoutViewportOriginMax, layoutViewportRect, StickToViewportBounds);
1539
1540 // FIXME: Is this equivalent to calling computeLayoutViewportOrigin() with StickToDocumentBounds?
1541 if (constraint == LayoutViewportConstraint::ConstrainedToDocumentRect) {
1542 // The max stable layout viewport origin really depends on the size of the layout viewport itself, so we need to adjust the location of the layout viewport one final time to make sure it does not end up out of bounds of the document.
1543 // Without this adjustment (and with using the non-constrained unobscuredContentRect's size as the size of the layout viewport) the layout viewport can be pushed past the bounds of the document during rubber-banding, and cannot be pushed
1544 // back in until the user scrolls back in the other direction.
1545 layoutViewportOrigin.setX(clampTo<float>(layoutViewportOrigin.x().toFloat(), 0, documentRect.width() - layoutViewportRect.width()));
1546 layoutViewportOrigin.setY(clampTo<float>(layoutViewportOrigin.y().toFloat(), 0, documentRect.height() - layoutViewportRect.height()));
1547 }
1548 layoutViewportRect.setLocation(layoutViewportOrigin);
1549
1550 return layoutViewportRect;
1551}
1552
1553// visualViewport and layoutViewport are both in content coordinates (unzoomed).
1554LayoutPoint FrameView::computeLayoutViewportOrigin(const LayoutRect& visualViewport, const LayoutPoint& stableLayoutViewportOriginMin, const LayoutPoint& stableLayoutViewportOriginMax, const LayoutRect& layoutViewport, ScrollBehaviorForFixedElements fixedBehavior)
1555{
1556 LayoutPoint layoutViewportOrigin = layoutViewport.location();
1557 bool allowRubberBanding = fixedBehavior == StickToViewportBounds;
1558
1559 if (visualViewport.width() > layoutViewport.width()) {
1560 layoutViewportOrigin.setX(visualViewport.x());
1561 if (!allowRubberBanding) {
1562 if (layoutViewportOrigin.x() < stableLayoutViewportOriginMin.x())
1563 layoutViewportOrigin.setX(stableLayoutViewportOriginMin.x());
1564 else if (layoutViewportOrigin.x() > stableLayoutViewportOriginMax.x())
1565 layoutViewportOrigin.setX(stableLayoutViewportOriginMax.x());
1566 }
1567 } else {
1568 bool rubberbandingAtLeft = allowRubberBanding && visualViewport.x() < stableLayoutViewportOriginMin.x();
1569 bool rubberbandingAtRight = allowRubberBanding && (visualViewport.maxX() - layoutViewport.width()) > stableLayoutViewportOriginMax.x();
1570
1571 if (visualViewport.x() < layoutViewport.x() || rubberbandingAtLeft)
1572 layoutViewportOrigin.setX(visualViewport.x());
1573
1574 if (visualViewport.maxX() > layoutViewport.maxX() || rubberbandingAtRight)
1575 layoutViewportOrigin.setX(visualViewport.maxX() - layoutViewport.width());
1576
1577 if (!rubberbandingAtLeft && layoutViewportOrigin.x() < stableLayoutViewportOriginMin.x())
1578 layoutViewportOrigin.setX(stableLayoutViewportOriginMin.x());
1579
1580 if (!rubberbandingAtRight && layoutViewportOrigin.x() > stableLayoutViewportOriginMax.x())
1581 layoutViewportOrigin.setX(stableLayoutViewportOriginMax.x());
1582 }
1583
1584 if (visualViewport.height() > layoutViewport.height()) {
1585 layoutViewportOrigin.setY(visualViewport.y());
1586 if (!allowRubberBanding) {
1587 if (layoutViewportOrigin.y() < stableLayoutViewportOriginMin.y())
1588 layoutViewportOrigin.setY(stableLayoutViewportOriginMin.y());
1589 else if (layoutViewportOrigin.y() > stableLayoutViewportOriginMax.y())
1590 layoutViewportOrigin.setY(stableLayoutViewportOriginMax.y());
1591 }
1592 } else {
1593 bool rubberbandingAtTop = allowRubberBanding && visualViewport.y() < stableLayoutViewportOriginMin.y();
1594 bool rubberbandingAtBottom = allowRubberBanding && (visualViewport.maxY() - layoutViewport.height()) > stableLayoutViewportOriginMax.y();
1595
1596 if (visualViewport.y() < layoutViewport.y() || rubberbandingAtTop)
1597 layoutViewportOrigin.setY(visualViewport.y());
1598
1599 if (visualViewport.maxY() > layoutViewport.maxY() || rubberbandingAtBottom)
1600 layoutViewportOrigin.setY(visualViewport.maxY() - layoutViewport.height());
1601
1602 if (!rubberbandingAtTop && layoutViewportOrigin.y() < stableLayoutViewportOriginMin.y())
1603 layoutViewportOrigin.setY(stableLayoutViewportOriginMin.y());
1604
1605 if (!rubberbandingAtBottom && layoutViewportOrigin.y() > stableLayoutViewportOriginMax.y())
1606 layoutViewportOrigin.setY(stableLayoutViewportOriginMax.y());
1607 }
1608
1609 return layoutViewportOrigin;
1610}
1611
1612void FrameView::setBaseLayoutViewportOrigin(LayoutPoint origin, TriggerLayoutOrNot layoutTriggering)
1613{
1614 ASSERT(frame().settings().visualViewportEnabled());
1615
1616 if (origin == m_layoutViewportOrigin)
1617 return;
1618
1619 m_layoutViewportOrigin = origin;
1620 if (layoutTriggering == TriggerLayoutOrNot::Yes)
1621 setViewportConstrainedObjectsNeedLayout();
1622
1623 if (TiledBacking* tiledBacking = this->tiledBacking()) {
1624 FloatRect layoutViewport = layoutViewportRect();
1625 layoutViewport.moveBy(unscaledScrollOrigin()); // tiledBacking deals in top-left relative coordinates.
1626 tiledBacking->setLayoutViewportRect(layoutViewport);
1627 }
1628}
1629
1630void FrameView::setLayoutViewportOverrideRect(Optional<LayoutRect> rect, TriggerLayoutOrNot layoutTriggering)
1631{
1632 if (rect == m_layoutViewportOverrideRect)
1633 return;
1634
1635 LayoutRect oldRect = layoutViewportRect();
1636 m_layoutViewportOverrideRect = rect;
1637
1638 // Triggering layout on height changes is necessary to make bottom-fixed elements behave correctly.
1639 if (oldRect.height() != layoutViewportRect().height())
1640 layoutTriggering = TriggerLayoutOrNot::Yes;
1641
1642 LOG_WITH_STREAM(Scrolling, stream << "\nFrameView " << this << " setLayoutViewportOverrideRect() - changing override layout viewport from " << oldRect << " to " << m_layoutViewportOverrideRect.valueOr(LayoutRect()) << " layoutTriggering " << (layoutTriggering == TriggerLayoutOrNot::Yes ? "yes" : "no"));
1643
1644 if (oldRect != layoutViewportRect() && layoutTriggering == TriggerLayoutOrNot::Yes)
1645 setViewportConstrainedObjectsNeedLayout();
1646}
1647
1648void FrameView::setVisualViewportOverrideRect(Optional<LayoutRect> rect)
1649{
1650 m_visualViewportOverrideRect = rect;
1651}
1652
1653LayoutSize FrameView::baseLayoutViewportSize() const
1654{
1655 return renderView() ? renderView()->size() : size();
1656}
1657
1658void FrameView::updateLayoutViewport()
1659{
1660 if (!frame().settings().visualViewportEnabled())
1661 return;
1662
1663 // Don't update the layout viewport if we're in the middle of adjusting scrollbars. We'll get another call
1664 // as a post-layout task.
1665 if (layoutContext().layoutPhase() == FrameViewLayoutContext::LayoutPhase::InViewSizeAdjust)
1666 return;
1667
1668 LayoutRect layoutViewport = layoutViewportRect();
1669
1670 LOG_WITH_STREAM(Scrolling, stream << "\nFrameView " << this << " updateLayoutViewport() totalContentSize " << totalContentsSize() << " unscaledDocumentRect " << (renderView() ? renderView()->unscaledDocumentRect() : IntRect()) << " header height " << headerHeight() << " footer height " << footerHeight() << " fixed behavior " << scrollBehaviorForFixedElements());
1671 LOG_WITH_STREAM(Scrolling, stream << "layoutViewport: " << layoutViewport);
1672 LOG_WITH_STREAM(Scrolling, stream << "visualViewport: " << visualViewportRect() << " (is override " << (bool)m_visualViewportOverrideRect << ")");
1673 LOG_WITH_STREAM(Scrolling, stream << "stable origins: min: " << minStableLayoutViewportOrigin() << " max: "<< maxStableLayoutViewportOrigin());
1674
1675 if (m_layoutViewportOverrideRect) {
1676 if (currentScrollType() == ScrollType::Programmatic) {
1677 LOG_WITH_STREAM(Scrolling, stream << "computing new override layout viewport because of programmatic scrolling");
1678 LayoutPoint newOrigin = computeLayoutViewportOrigin(visualViewportRect(), minStableLayoutViewportOrigin(), maxStableLayoutViewportOrigin(), layoutViewport, StickToDocumentBounds);
1679 setLayoutViewportOverrideRect(LayoutRect(newOrigin, m_layoutViewportOverrideRect.value().size()));
1680 }
1681 if (frame().settings().visualViewportAPIEnabled()) {
1682 if (auto* window = frame().window())
1683 window->visualViewport().update();
1684 }
1685 return;
1686 }
1687
1688 LayoutPoint newLayoutViewportOrigin = computeLayoutViewportOrigin(visualViewportRect(), minStableLayoutViewportOrigin(), maxStableLayoutViewportOrigin(), layoutViewport, scrollBehaviorForFixedElements());
1689 if (newLayoutViewportOrigin != m_layoutViewportOrigin) {
1690 setBaseLayoutViewportOrigin(newLayoutViewportOrigin);
1691 LOG_WITH_STREAM(Scrolling, stream << "layoutViewport changed to " << layoutViewportRect());
1692 }
1693 if (frame().settings().visualViewportAPIEnabled()) {
1694 if (auto* window = frame().window())
1695 window->visualViewport().update();
1696 }
1697}
1698
1699LayoutPoint FrameView::minStableLayoutViewportOrigin() const
1700{
1701 return unscaledMinimumScrollPosition();
1702}
1703
1704LayoutPoint FrameView::maxStableLayoutViewportOrigin() const
1705{
1706 LayoutPoint maxPosition = unscaledMaximumScrollPosition();
1707 maxPosition = (maxPosition - LayoutSize(0, headerHeight() + footerHeight())).expandedTo({ });
1708 return maxPosition;
1709}
1710
1711IntPoint FrameView::unscaledScrollOrigin() const
1712{
1713 if (RenderView* renderView = this->renderView())
1714 return -renderView->unscaledDocumentRect().location(); // Akin to code in adjustViewSize().
1715
1716 return { };
1717}
1718
1719LayoutRect FrameView::layoutViewportRect() const
1720{
1721 if (m_layoutViewportOverrideRect)
1722 return m_layoutViewportOverrideRect.value();
1723
1724 // Size of initial containing block, anchored at scroll position, in document coordinates (unchanged by scale factor).
1725 return LayoutRect(m_layoutViewportOrigin, baseLayoutViewportSize());
1726}
1727
1728// visibleContentRect is in the bounds of the scroll view content. That consists of an
1729// optional header, the document, and an optional footer. Only the document is scaled,
1730// so we have to compute the visible part of the document in unscaled document coordinates.
1731// On iOS, pageScaleFactor is always 1 here, and we never have headers and footers.
1732LayoutRect FrameView::visibleDocumentRect(const FloatRect& visibleContentRect, float headerHeight, float footerHeight, const FloatSize& totalContentsSize, float pageScaleFactor)
1733{
1734 float contentsHeight = totalContentsSize.height() - headerHeight - footerHeight;
1735
1736 float rubberBandTop = std::min<float>(visibleContentRect.y(), 0);
1737 float visibleScaledDocumentTop = std::max<float>(visibleContentRect.y() - headerHeight, 0) + rubberBandTop;
1738
1739 float rubberBandBottom = std::min<float>((totalContentsSize.height() - visibleContentRect.y()) - visibleContentRect.height(), 0);
1740 float visibleScaledDocumentBottom = std::min<float>(visibleContentRect.maxY() - headerHeight, contentsHeight) - rubberBandBottom;
1741
1742 FloatRect visibleDocumentRect = visibleContentRect;
1743 visibleDocumentRect.setY(visibleScaledDocumentTop);
1744 visibleDocumentRect.setHeight(std::max<float>(visibleScaledDocumentBottom - visibleScaledDocumentTop, 0));
1745 visibleDocumentRect.scale(1 / pageScaleFactor);
1746
1747 return LayoutRect(visibleDocumentRect);
1748}
1749
1750LayoutRect FrameView::visualViewportRect() const
1751{
1752 if (m_visualViewportOverrideRect)
1753 return m_visualViewportOverrideRect.value();
1754
1755 FloatRect visibleContentRect = this->visibleContentRect(LegacyIOSDocumentVisibleRect);
1756 return visibleDocumentRect(visibleContentRect, headerHeight(), footerHeight(), totalContentsSize(), frameScaleFactor());
1757}
1758
1759LayoutRect FrameView::viewportConstrainedVisibleContentRect() const
1760{
1761 ASSERT(!frame().settings().visualViewportEnabled());
1762
1763#if PLATFORM(IOS_FAMILY)
1764 if (useCustomFixedPositionLayoutRect())
1765 return customFixedPositionLayoutRect();
1766#endif
1767 LayoutRect viewportRect = visibleContentRect();
1768
1769 viewportRect.setLocation(scrollPositionForFixedPosition());
1770 return viewportRect;
1771}
1772
1773LayoutRect FrameView::rectForFixedPositionLayout() const
1774{
1775 if (frame().settings().visualViewportEnabled())
1776 return layoutViewportRect();
1777
1778 return viewportConstrainedVisibleContentRect();
1779}
1780
1781float FrameView::frameScaleFactor() const
1782{
1783 return frame().frameScaleFactor();
1784}
1785
1786LayoutPoint FrameView::scrollPositionForFixedPosition() const
1787{
1788 if (frame().settings().visualViewportEnabled())
1789 return layoutViewportRect().location();
1790
1791 return scrollPositionForFixedPosition(visibleContentRect(), totalContentsSize(), scrollPosition(), scrollOrigin(), frameScaleFactor(), fixedElementsLayoutRelativeToFrame(), scrollBehaviorForFixedElements(), headerHeight(), footerHeight());
1792}
1793
1794LayoutPoint FrameView::scrollPositionForFixedPosition(const LayoutRect& visibleContentRect, const LayoutSize& totalContentsSize, const LayoutPoint& scrollPosition, const LayoutPoint& scrollOrigin, float frameScaleFactor, bool fixedElementsLayoutRelativeToFrame, ScrollBehaviorForFixedElements behaviorForFixed, int headerHeight, int footerHeight)
1795{
1796 LayoutPoint position;
1797 if (behaviorForFixed == StickToDocumentBounds)
1798 position = ScrollableArea::constrainScrollPositionForOverhang(visibleContentRect, totalContentsSize, scrollPosition, scrollOrigin, headerHeight, footerHeight);
1799 else {
1800 position = scrollPosition;
1801 position.setY(position.y() - headerHeight);
1802 }
1803
1804 LayoutSize maxSize = totalContentsSize - visibleContentRect.size();
1805
1806 float dragFactorX = (fixedElementsLayoutRelativeToFrame || !maxSize.width()) ? 1 : (totalContentsSize.width() - visibleContentRect.width() * frameScaleFactor) / maxSize.width();
1807 float dragFactorY = (fixedElementsLayoutRelativeToFrame || !maxSize.height()) ? 1 : (totalContentsSize.height() - visibleContentRect.height() * frameScaleFactor) / maxSize.height();
1808
1809 return LayoutPoint(position.x() * dragFactorX / frameScaleFactor, position.y() * dragFactorY / frameScaleFactor);
1810}
1811
1812float FrameView::yPositionForInsetClipLayer(const FloatPoint& scrollPosition, float topContentInset)
1813{
1814 if (!topContentInset)
1815 return 0;
1816
1817 // The insetClipLayer should not move for negative scroll values.
1818 float scrollY = std::max<float>(0, scrollPosition.y());
1819
1820 if (scrollY >= topContentInset)
1821 return 0;
1822
1823 return topContentInset - scrollY;
1824}
1825
1826float FrameView::yPositionForHeaderLayer(const FloatPoint& scrollPosition, float topContentInset)
1827{
1828 if (!topContentInset)
1829 return 0;
1830
1831 float scrollY = std::max<float>(0, scrollPosition.y());
1832
1833 if (scrollY >= topContentInset)
1834 return topContentInset;
1835
1836 return scrollY;
1837}
1838
1839float FrameView::yPositionForFooterLayer(const FloatPoint& scrollPosition, float topContentInset, float totalContentsHeight, float footerHeight)
1840{
1841 return yPositionForHeaderLayer(scrollPosition, topContentInset) + totalContentsHeight - footerHeight;
1842}
1843
1844FloatPoint FrameView::positionForRootContentLayer(const FloatPoint& scrollPosition, const FloatPoint& scrollOrigin, float topContentInset, float headerHeight)
1845{
1846 return FloatPoint(0, yPositionForHeaderLayer(scrollPosition, topContentInset) + headerHeight) - toFloatSize(scrollOrigin);
1847}
1848
1849FloatPoint FrameView::positionForRootContentLayer() const
1850{
1851 return positionForRootContentLayer(scrollPosition(), scrollOrigin(), topContentInset(), headerHeight());
1852}
1853
1854#if PLATFORM(IOS_FAMILY)
1855LayoutRect FrameView::rectForViewportConstrainedObjects(const LayoutRect& visibleContentRect, const LayoutSize& totalContentsSize, float frameScaleFactor, bool fixedElementsLayoutRelativeToFrame, ScrollBehaviorForFixedElements scrollBehavior)
1856{
1857 if (fixedElementsLayoutRelativeToFrame)
1858 return visibleContentRect;
1859
1860 if (totalContentsSize.isEmpty())
1861 return visibleContentRect;
1862
1863 // We impose an lower limit on the size (so an upper limit on the scale) of
1864 // the rect used to position fixed objects so that they don't crowd into the
1865 // center of the screen at larger scales.
1866 const LayoutUnit maxContentWidthForZoomThreshold = 1024_lu;
1867 float zoomedOutScale = frameScaleFactor * visibleContentRect.width() / std::min(maxContentWidthForZoomThreshold, totalContentsSize.width());
1868 float constraintThresholdScale = 1.5 * zoomedOutScale;
1869 float maxPostionedObjectsRectScale = std::min(frameScaleFactor, constraintThresholdScale);
1870
1871 LayoutRect viewportConstrainedObjectsRect = visibleContentRect;
1872
1873 if (frameScaleFactor > constraintThresholdScale) {
1874 FloatRect contentRect(FloatPoint(), totalContentsSize);
1875 FloatRect viewportRect = visibleContentRect;
1876
1877 // Scale the rect up from a point that is relative to its position in the viewport.
1878 FloatSize sizeDelta = contentRect.size() - viewportRect.size();
1879
1880 FloatPoint scaleOrigin;
1881 scaleOrigin.setX(contentRect.x() + sizeDelta.width() > 0 ? contentRect.width() * (viewportRect.x() - contentRect.x()) / sizeDelta.width() : 0);
1882 scaleOrigin.setY(contentRect.y() + sizeDelta.height() > 0 ? contentRect.height() * (viewportRect.y() - contentRect.y()) / sizeDelta.height() : 0);
1883
1884 AffineTransform rescaleTransform = AffineTransform::translation(scaleOrigin.x(), scaleOrigin.y());
1885 rescaleTransform.scale(frameScaleFactor / maxPostionedObjectsRectScale, frameScaleFactor / maxPostionedObjectsRectScale);
1886 rescaleTransform = CGAffineTransformTranslate(rescaleTransform, -scaleOrigin.x(), -scaleOrigin.y());
1887
1888 viewportConstrainedObjectsRect = enclosingLayoutRect(rescaleTransform.mapRect(visibleContentRect));
1889 }
1890
1891 if (scrollBehavior == StickToDocumentBounds) {
1892 LayoutRect documentBounds(LayoutPoint(), totalContentsSize);
1893 viewportConstrainedObjectsRect.intersect(documentBounds);
1894 }
1895
1896 return viewportConstrainedObjectsRect;
1897}
1898
1899LayoutRect FrameView::viewportConstrainedObjectsRect() const
1900{
1901 return rectForViewportConstrainedObjects(visibleContentRect(), totalContentsSize(), frame().frameScaleFactor(), fixedElementsLayoutRelativeToFrame(), scrollBehaviorForFixedElements());
1902}
1903#endif
1904
1905ScrollPosition FrameView::minimumScrollPosition() const
1906{
1907 ScrollPosition minimumPosition = ScrollView::minimumScrollPosition();
1908
1909 if (frame().isMainFrame() && m_scrollPinningBehavior == PinToBottom)
1910 minimumPosition.setY(maximumScrollPosition().y());
1911
1912 return minimumPosition;
1913}
1914
1915ScrollPosition FrameView::maximumScrollPosition() const
1916{
1917 ScrollPosition maximumPosition = ScrollView::maximumScrollPosition();
1918
1919 if (frame().isMainFrame() && m_scrollPinningBehavior == PinToTop)
1920 maximumPosition.setY(minimumScrollPosition().y());
1921
1922 return maximumPosition;
1923}
1924
1925ScrollPosition FrameView::unscaledMinimumScrollPosition() const
1926{
1927 if (RenderView* renderView = this->renderView()) {
1928 IntRect unscaledDocumentRect = renderView->unscaledDocumentRect();
1929 ScrollPosition minimumPosition = unscaledDocumentRect.location();
1930
1931 if (frame().isMainFrame() && m_scrollPinningBehavior == PinToBottom)
1932 minimumPosition.setY(unscaledMaximumScrollPosition().y());
1933
1934 return minimumPosition;
1935 }
1936
1937 return minimumScrollPosition();
1938}
1939
1940ScrollPosition FrameView::unscaledMaximumScrollPosition() const
1941{
1942 if (RenderView* renderView = this->renderView()) {
1943 IntRect unscaledDocumentRect = renderView->unscaledDocumentRect();
1944 unscaledDocumentRect.expand(0, headerHeight() + footerHeight());
1945 ScrollPosition maximumPosition = ScrollPosition(unscaledDocumentRect.maxXMaxYCorner() - visibleSize()).expandedTo({ 0, 0 });
1946 if (frame().isMainFrame() && m_scrollPinningBehavior == PinToTop)
1947 maximumPosition.setY(unscaledMinimumScrollPosition().y());
1948
1949 return maximumPosition;
1950 }
1951
1952 return maximumScrollPosition();
1953}
1954
1955void FrameView::viewportContentsChanged()
1956{
1957 if (!frame().view()) {
1958 // The frame is being destroyed.
1959 return;
1960 }
1961
1962 if (auto* page = frame().page())
1963 page->updateValidationBubbleStateIfNeeded();
1964
1965 // When the viewport contents changes (scroll, resize, style recalc, layout, ...),
1966 // check if we should resume animated images or unthrottle DOM timers.
1967 applyRecursivelyWithVisibleRect([] (FrameView& frameView, const IntRect& visibleRect) {
1968 frameView.resumeVisibleImageAnimations(visibleRect);
1969 frameView.updateScriptedAnimationsAndTimersThrottlingState(visibleRect);
1970
1971 if (auto* renderView = frameView.frame().contentRenderer())
1972 renderView->updateVisibleViewportRect(visibleRect);
1973 });
1974}
1975
1976IntRect FrameView::visualViewportRectExpandedByContentInsets() const
1977{
1978 FloatRect unobscuredContentRect = this->visualViewportRect();
1979 if (auto* page = frame().page())
1980 unobscuredContentRect.expand(page->contentInsets());
1981 return IntRect(unobscuredContentRect);
1982}
1983
1984bool FrameView::fixedElementsLayoutRelativeToFrame() const
1985{
1986 return frame().settings().fixedElementsLayoutRelativeToFrame();
1987}
1988
1989IntPoint FrameView::lastKnownMousePosition() const
1990{
1991 return frame().eventHandler().lastKnownMousePosition();
1992}
1993
1994bool FrameView::isHandlingWheelEvent() const
1995{
1996 return frame().eventHandler().isHandlingWheelEvent();
1997}
1998
1999bool FrameView::shouldSetCursor() const
2000{
2001 Page* page = frame().page();
2002 return page && page->isVisible() && page->focusController().isActive();
2003}
2004
2005#if ENABLE(DARK_MODE_CSS)
2006RenderObject* FrameView::rendererForColorScheme() const
2007{
2008 auto* document = frame().document();
2009 auto* documentElement = document ? document->documentElement() : nullptr;
2010 auto* documentElementRenderer = documentElement ? documentElement->renderer() : nullptr;
2011 if (documentElementRenderer && documentElementRenderer->style().hasExplicitlySetColorScheme())
2012 return documentElementRenderer;
2013 auto* bodyElement = document ? document->bodyOrFrameset() : nullptr;
2014 return bodyElement ? bodyElement->renderer() : nullptr;
2015}
2016#endif
2017
2018bool FrameView::useDarkAppearance() const
2019{
2020#if ENABLE(DARK_MODE_CSS)
2021 if (auto* renderer = rendererForColorScheme())
2022 return renderer->useDarkAppearance();
2023#endif
2024 if (auto* document = frame().document())
2025 return document->useDarkAppearance(nullptr);
2026 return false;
2027}
2028
2029OptionSet<StyleColor::Options> FrameView::styleColorOptions() const
2030{
2031#if ENABLE(DARK_MODE_CSS)
2032 if (auto* renderer = rendererForColorScheme())
2033 return renderer->styleColorOptions();
2034#endif
2035 if (auto* document = frame().document())
2036 return document->styleColorOptions(nullptr);
2037 return { };
2038}
2039
2040bool FrameView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect)
2041{
2042 if (!m_viewportConstrainedObjects || m_viewportConstrainedObjects->isEmpty()) {
2043 frame().page()->chrome().scroll(scrollDelta, rectToScroll, clipRect);
2044 return true;
2045 }
2046
2047 bool isCompositedContentLayer = usesCompositedScrolling();
2048
2049 // Get the rects of the fixed objects visible in the rectToScroll
2050 Region regionToUpdate;
2051 for (auto& renderer : *m_viewportConstrainedObjects) {
2052 if (!renderer->style().hasViewportConstrainedPosition())
2053 continue;
2054 if (renderer->isComposited())
2055 continue;
2056
2057 // Fixed items should always have layers.
2058 ASSERT(renderer->hasLayer());
2059 RenderLayer* layer = downcast<RenderBoxModelObject>(*renderer).layer();
2060
2061 if (layer->viewportConstrainedNotCompositedReason() == RenderLayer::NotCompositedForBoundsOutOfView
2062 || layer->viewportConstrainedNotCompositedReason() == RenderLayer::NotCompositedForNoVisibleContent) {
2063 // Don't invalidate for invisible fixed layers.
2064 continue;
2065 }
2066
2067 if (layer->hasAncestorWithFilterOutsets()) {
2068 // If the fixed layer has a blur/drop-shadow filter applied on at least one of its parents, we cannot
2069 // scroll using the fast path, otherwise the outsets of the filter will be moved around the page.
2070 return false;
2071 }
2072
2073 // FIXME: use pixel snapping instead of enclosing when ScrollView has finished transitioning from IntRect to Float/LayoutRect.
2074 IntRect updateRect = enclosingIntRect(layer->repaintRectIncludingNonCompositingDescendants());
2075 updateRect = contentsToRootView(updateRect);
2076 if (!isCompositedContentLayer)
2077 updateRect.intersect(rectToScroll);
2078 if (!updateRect.isEmpty())
2079 regionToUpdate.unite(updateRect);
2080 }
2081
2082 // 1) scroll
2083 frame().page()->chrome().scroll(scrollDelta, rectToScroll, clipRect);
2084
2085 // 2) update the area of fixed objects that has been invalidated
2086 for (auto& updateRect : regionToUpdate.rects()) {
2087 IntRect scrolledRect = updateRect;
2088 scrolledRect.move(scrollDelta);
2089 updateRect.unite(scrolledRect);
2090 if (isCompositedContentLayer) {
2091 updateRect = rootViewToContents(updateRect);
2092 ASSERT(renderView());
2093 renderView()->layer()->setBackingNeedsRepaintInRect(updateRect);
2094 continue;
2095 }
2096 updateRect.intersect(rectToScroll);
2097 frame().page()->chrome().invalidateContentsAndRootView(updateRect);
2098 }
2099
2100 return true;
2101}
2102
2103void FrameView::scrollContentsSlowPath(const IntRect& updateRect)
2104{
2105 repaintSlowRepaintObjects();
2106
2107 if (!usesCompositedScrolling() && isEnclosedInCompositingLayer()) {
2108 if (RenderWidget* frameRenderer = frame().ownerRenderer()) {
2109 LayoutRect rect(frameRenderer->borderLeft() + frameRenderer->paddingLeft(), frameRenderer->borderTop() + frameRenderer->paddingTop(),
2110 visibleWidth(), visibleHeight());
2111 frameRenderer->repaintRectangle(rect);
2112 return;
2113 }
2114 }
2115
2116 ScrollView::scrollContentsSlowPath(updateRect);
2117}
2118
2119void FrameView::repaintSlowRepaintObjects()
2120{
2121 if (!m_slowRepaintObjects)
2122 return;
2123
2124 // Renderers with fixed backgrounds may be in compositing layers, so we need to explicitly
2125 // repaint them after scrolling.
2126 for (auto& renderer : *m_slowRepaintObjects)
2127 renderer->repaintSlowRepaintObject();
2128}
2129
2130// Note that this gets called at painting time.
2131void FrameView::setIsOverlapped(bool isOverlapped)
2132{
2133 if (isOverlapped == m_isOverlapped)
2134 return;
2135
2136 m_isOverlapped = isOverlapped;
2137 updateCanBlitOnScrollRecursively();
2138}
2139
2140void FrameView::setContentIsOpaque(bool contentIsOpaque)
2141{
2142 if (contentIsOpaque == m_contentIsOpaque)
2143 return;
2144
2145 m_contentIsOpaque = contentIsOpaque;
2146 updateCanBlitOnScrollRecursively();
2147}
2148
2149void FrameView::restoreScrollbar()
2150{
2151 setScrollbarsSuppressed(false);
2152}
2153
2154bool FrameView::scrollToFragment(const URL& url)
2155{
2156 String fragmentIdentifier = url.fragmentIdentifier();
2157 if (scrollToAnchor(fragmentIdentifier))
2158 return true;
2159
2160 // Try again after decoding the ref, based on the document's encoding.
2161 if (TextResourceDecoder* decoder = frame().document()->decoder()) {
2162 if (scrollToAnchor(decodeURLEscapeSequences(fragmentIdentifier, decoder->encoding())))
2163 return true;
2164 }
2165
2166 resetScrollAnchor();
2167 return false;
2168}
2169
2170bool FrameView::scrollToAnchor(const String& fragmentIdentifier)
2171{
2172 LOG(Scrolling, "FrameView::scrollToAnchor %s", fragmentIdentifier.utf8().data());
2173
2174 // If our URL has no ref, then we have no place we need to jump to.
2175 if (fragmentIdentifier.isNull())
2176 return false;
2177
2178 ASSERT(frame().document());
2179 auto& document = *frame().document();
2180
2181 if (!document.haveStylesheetsLoaded()) {
2182 document.setGotoAnchorNeededAfterStylesheetsLoad(true);
2183 return false;
2184 }
2185
2186 document.setGotoAnchorNeededAfterStylesheetsLoad(false);
2187
2188 Element* anchorElement = document.findAnchor(fragmentIdentifier);
2189
2190 LOG(Scrolling, " anchorElement is %p", anchorElement);
2191
2192 // Setting to null will clear the current target.
2193 document.setCSSTarget(anchorElement);
2194
2195 if (is<SVGDocument>(document)) {
2196 if (fragmentIdentifier.isEmpty())
2197 return false;
2198 if (auto rootElement = SVGDocument::rootElement(document)) {
2199 if (rootElement->scrollToFragment(fragmentIdentifier))
2200 return true;
2201 // If SVG failed to scrollToAnchor() and anchorElement is null, no other scrolling will be possible.
2202 if (!anchorElement)
2203 return false;
2204 }
2205 } else if (!anchorElement && !(fragmentIdentifier.isEmpty() || equalLettersIgnoringASCIICase(fragmentIdentifier, "top"))) {
2206 // Implement the rule that "" and "top" both mean top of page as in other browsers.
2207 return false;
2208 }
2209
2210 ContainerNode* scrollPositionAnchor = anchorElement;
2211 if (!scrollPositionAnchor)
2212 scrollPositionAnchor = frame().document();
2213 maintainScrollPositionAtAnchor(scrollPositionAnchor);
2214
2215 // If the anchor accepts keyboard focus, move focus there to aid users relying on keyboard navigation.
2216 if (anchorElement) {
2217 if (anchorElement->isFocusable())
2218 document.setFocusedElement(anchorElement);
2219 else {
2220 document.setFocusedElement(nullptr);
2221 document.setFocusNavigationStartingNode(anchorElement);
2222 }
2223 }
2224
2225 return true;
2226}
2227
2228void FrameView::maintainScrollPositionAtAnchor(ContainerNode* anchorNode)
2229{
2230 LOG(Scrolling, "FrameView::maintainScrollPositionAtAnchor at %p", anchorNode);
2231
2232 m_maintainScrollPositionAnchor = anchorNode;
2233 if (!m_maintainScrollPositionAnchor)
2234 return;
2235 m_shouldScrollToFocusedElement = false;
2236 m_delayedScrollToFocusedElementTimer.stop();
2237
2238 // We need to update the layout before scrolling, otherwise we could
2239 // really mess things up if an anchor scroll comes at a bad moment.
2240 frame().document()->updateStyleIfNeeded();
2241 // Only do a layout if changes have occurred that make it necessary.
2242 RenderView* renderView = this->renderView();
2243 if (renderView && renderView->needsLayout())
2244 layoutContext().layout();
2245 else
2246 scrollToAnchor();
2247}
2248
2249void FrameView::scrollElementToRect(const Element& element, const IntRect& rect)
2250{
2251 frame().document()->updateLayoutIgnorePendingStylesheets();
2252
2253 LayoutRect bounds;
2254 if (RenderElement* renderer = element.renderer())
2255 bounds = renderer->absoluteAnchorRect();
2256 int centeringOffsetX = (rect.width() - bounds.width()) / 2;
2257 int centeringOffsetY = (rect.height() - bounds.height()) / 2;
2258 setScrollPosition(IntPoint(bounds.x() - centeringOffsetX - rect.x(), bounds.y() - centeringOffsetY - rect.y()));
2259}
2260
2261void FrameView::setScrollPosition(const ScrollPosition& scrollPosition)
2262{
2263 LOG_WITH_STREAM(Scrolling, stream << "FrameView::setScrollPosition " << scrollPosition << " , clearing anchor");
2264
2265 auto oldScrollType = currentScrollType();
2266 setCurrentScrollType(ScrollType::Programmatic);
2267
2268 m_maintainScrollPositionAnchor = nullptr;
2269 m_shouldScrollToFocusedElement = false;
2270 m_delayedScrollToFocusedElementTimer.stop();
2271 Page* page = frame().page();
2272 if (page && page->expectsWheelEventTriggers())
2273 scrollAnimator().setWheelEventTestTrigger(page->testTrigger());
2274 ScrollView::setScrollPosition(scrollPosition);
2275
2276 setCurrentScrollType(oldScrollType);
2277}
2278
2279void FrameView::resetScrollAnchor()
2280{
2281 ASSERT(frame().document());
2282 auto& document = *frame().document();
2283
2284 // If CSS target was set previously, we want to set it to 0, recalc
2285 // and possibly repaint because :target pseudo class may have been
2286 // set (see bug 11321).
2287 document.setCSSTarget(nullptr);
2288
2289 if (is<SVGDocument>(document)) {
2290 if (auto rootElement = SVGDocument::rootElement(document)) {
2291 // We need to update the layout before resetScrollAnchor(), otherwise we
2292 // could really mess things up if resetting the anchor comes at a bad moment.
2293 document.updateStyleIfNeeded();
2294 rootElement->resetScrollAnchor();
2295 }
2296 }
2297}
2298
2299void FrameView::scheduleScrollToFocusedElement(SelectionRevealMode selectionRevealMode)
2300{
2301 if (selectionRevealMode == SelectionRevealMode::DoNotReveal)
2302 return;
2303
2304 m_selectionRevealModeForFocusedElement = selectionRevealMode;
2305 if (m_shouldScrollToFocusedElement)
2306 return;
2307 m_shouldScrollToFocusedElement = true;
2308 m_delayedScrollToFocusedElementTimer.startOneShot(0_s);
2309}
2310
2311void FrameView::scrollToFocusedElementImmediatelyIfNeeded()
2312{
2313 if (!m_shouldScrollToFocusedElement)
2314 return;
2315
2316 m_delayedScrollToFocusedElementTimer.stop();
2317 scrollToFocusedElementInternal();
2318}
2319
2320void FrameView::scrollToFocusedElementTimerFired()
2321{
2322 auto protectedThis = makeRef(*this);
2323 scrollToFocusedElementInternal();
2324}
2325
2326void FrameView::scrollToFocusedElementInternal()
2327{
2328 RELEASE_ASSERT(m_shouldScrollToFocusedElement);
2329 auto document = makeRefPtr(frame().document());
2330 if (!document)
2331 return;
2332
2333 document->updateLayoutIgnorePendingStylesheets();
2334 if (!m_shouldScrollToFocusedElement)
2335 return; // Updating the layout may have ran scripts.
2336 m_shouldScrollToFocusedElement = false;
2337
2338 auto focusedElement = makeRefPtr(document->focusedElement());
2339 if (!focusedElement)
2340 return;
2341 auto updateTarget = focusedElement->focusAppearanceUpdateTarget();
2342 if (!updateTarget)
2343 return;
2344
2345 auto* renderer = updateTarget->renderer();
2346 if (!renderer || renderer->isWidget())
2347 return;
2348
2349 bool insideFixed;
2350 LayoutRect absoluteBounds = renderer->absoluteAnchorRect(&insideFixed);
2351 renderer->scrollRectToVisible(absoluteBounds, insideFixed, { m_selectionRevealModeForFocusedElement, ScrollAlignment::alignCenterIfNeeded, ScrollAlignment::alignCenterIfNeeded, ShouldAllowCrossOriginScrolling::No });
2352}
2353
2354void FrameView::contentsResized()
2355{
2356 // For non-delegated scrolling, updateTiledBackingAdaptiveSizing() is called via addedOrRemovedScrollbar() which occurs less often.
2357 if (delegatesScrolling())
2358 updateTiledBackingAdaptiveSizing();
2359}
2360
2361void FrameView::delegatesScrollingDidChange()
2362{
2363 RenderView* renderView = this->renderView();
2364 if (!renderView)
2365 return;
2366
2367 RenderLayerCompositor& compositor = renderView->compositor();
2368 // When we switch to delegatesScrolling mode, we should destroy the scrolling/clipping layers in RenderLayerCompositor.
2369 if (compositor.usesCompositing()) {
2370 ASSERT(compositor.usesCompositing());
2371 compositor.enableCompositingMode(false);
2372 compositor.clearBackingForAllLayers();
2373 }
2374}
2375
2376#if USE(COORDINATED_GRAPHICS)
2377void FrameView::setFixedVisibleContentRect(const IntRect& visibleContentRect)
2378{
2379 bool visibleContentSizeDidChange = false;
2380 if (visibleContentRect.size() != this->fixedVisibleContentRect().size()) {
2381 // When the viewport size changes or the content is scaled, we need to
2382 // reposition the fixed and sticky positioned elements.
2383 setViewportConstrainedObjectsNeedLayout();
2384 visibleContentSizeDidChange = true;
2385 }
2386
2387 IntPoint oldPosition = scrollPosition();
2388 ScrollView::setFixedVisibleContentRect(visibleContentRect);
2389 IntPoint newPosition = scrollPosition();
2390 if (oldPosition != newPosition) {
2391 updateLayerPositionsAfterScrolling();
2392 if (frame().settings().acceleratedCompositingForFixedPositionEnabled())
2393 updateCompositingLayersAfterScrolling();
2394 scrollAnimator().setCurrentPosition(newPosition);
2395 scrollPositionChanged(oldPosition, newPosition);
2396 }
2397 if (visibleContentSizeDidChange) {
2398 // Update the scroll-bars to calculate new page-step size.
2399 updateScrollbars(scrollPosition());
2400 }
2401 didChangeScrollOffset();
2402}
2403#endif
2404
2405void FrameView::setViewportConstrainedObjectsNeedLayout()
2406{
2407 if (!hasViewportConstrainedObjects())
2408 return;
2409
2410 for (auto& renderer : *m_viewportConstrainedObjects) {
2411 renderer->setNeedsLayout();
2412 if (renderer->hasLayer()) {
2413 auto* layer = downcast<RenderBoxModelObject>(*renderer).layer();
2414 layer->setNeedsCompositingGeometryUpdate();
2415 }
2416 }
2417}
2418
2419void FrameView::didChangeScrollOffset()
2420{
2421 if (auto* page = frame().page())
2422 page->pageOverlayController().didScrollFrame(frame());
2423 frame().loader().client().didChangeScrollOffset();
2424}
2425
2426void FrameView::scrollOffsetChangedViaPlatformWidgetImpl(const ScrollOffset& oldOffset, const ScrollOffset& newOffset)
2427{
2428 updateLayerPositionsAfterScrolling();
2429 updateCompositingLayersAfterScrolling();
2430 repaintSlowRepaintObjects();
2431 scrollPositionChanged(scrollPositionFromOffset(oldOffset), scrollPositionFromOffset(newOffset));
2432
2433 if (auto* renderView = this->renderView()) {
2434 if (renderView->usesCompositing())
2435 renderView->compositor().didChangeVisibleRect();
2436 }
2437}
2438
2439// These scroll positions are affected by zooming.
2440void FrameView::scrollPositionChanged(const ScrollPosition& oldPosition, const ScrollPosition& newPosition)
2441{
2442 UNUSED_PARAM(oldPosition);
2443 UNUSED_PARAM(newPosition);
2444
2445 Page* page = frame().page();
2446 Seconds throttlingDelay = page ? page->chrome().client().eventThrottlingDelay() : 0_s;
2447
2448 if (throttlingDelay == 0_s) {
2449 m_delayedScrollEventTimer.stop();
2450 sendScrollEvent();
2451 } else if (!m_delayedScrollEventTimer.isActive())
2452 m_delayedScrollEventTimer.startOneShot(throttlingDelay);
2453
2454 if (RenderView* renderView = this->renderView()) {
2455 if (renderView->usesCompositing())
2456 renderView->compositor().frameViewDidScroll();
2457 }
2458
2459 LOG_WITH_STREAM(Scrolling, stream << "FrameView " << this << " scrollPositionChanged from " << oldPosition << " to " << newPosition << " (scale " << frameScaleFactor() << " )");
2460 updateLayoutViewport();
2461 viewportContentsChanged();
2462}
2463
2464void FrameView::applyRecursivelyWithVisibleRect(const WTF::Function<void (FrameView& frameView, const IntRect& visibleRect)>& apply)
2465{
2466 IntRect windowClipRect = this->windowClipRect();
2467 auto visibleRect = windowToContents(windowClipRect);
2468 apply(*this, visibleRect);
2469
2470 // Recursive call for subframes. We cache the current FrameView's windowClipRect to avoid recomputing it for every subframe.
2471 SetForScope<IntRect*> windowClipRectCache(m_cachedWindowClipRect, &windowClipRect);
2472 for (Frame* childFrame = frame().tree().firstChild(); childFrame; childFrame = childFrame->tree().nextSibling()) {
2473 if (auto* childView = childFrame->view())
2474 childView->applyRecursivelyWithVisibleRect(apply);
2475 }
2476}
2477
2478void FrameView::resumeVisibleImageAnimations(const IntRect& visibleRect)
2479{
2480 if (visibleRect.isEmpty())
2481 return;
2482
2483 if (auto* renderView = frame().contentRenderer())
2484 renderView->resumePausedImageAnimationsIfNeeded(visibleRect);
2485}
2486
2487void FrameView::updateScriptedAnimationsAndTimersThrottlingState(const IntRect& visibleRect)
2488{
2489 if (frame().isMainFrame())
2490 return;
2491
2492 auto* document = frame().document();
2493 if (!document)
2494 return;
2495
2496 // We don't throttle zero-size or display:none frames because those are usually utility frames.
2497 bool shouldThrottle = visibleRect.isEmpty() && !m_size.isEmpty() && frame().ownerRenderer();
2498
2499 if (auto* scriptedAnimationController = document->scriptedAnimationController()) {
2500 if (shouldThrottle)
2501 scriptedAnimationController->addThrottlingReason(ScriptedAnimationController::ThrottlingReason::OutsideViewport);
2502 else
2503 scriptedAnimationController->removeThrottlingReason(ScriptedAnimationController::ThrottlingReason::OutsideViewport);
2504 }
2505
2506 document->setTimerThrottlingEnabled(shouldThrottle);
2507}
2508
2509
2510void FrameView::resumeVisibleImageAnimationsIncludingSubframes()
2511{
2512 applyRecursivelyWithVisibleRect([] (FrameView& frameView, const IntRect& visibleRect) {
2513 frameView.resumeVisibleImageAnimations(visibleRect);
2514 });
2515}
2516
2517void FrameView::updateLayerPositionsAfterScrolling()
2518{
2519 // If we're scrolling as a result of updating the view size after layout, we'll update widgets and layer positions soon anyway.
2520 if (layoutContext().layoutPhase() == FrameViewLayoutContext::LayoutPhase::InViewSizeAdjust)
2521 return;
2522
2523 if (!layoutContext().isLayoutNested() && hasViewportConstrainedObjects()) {
2524 if (RenderView* renderView = this->renderView()) {
2525 updateWidgetPositions();
2526 renderView->layer()->updateLayerPositionsAfterDocumentScroll();
2527 }
2528 }
2529}
2530
2531bool FrameView::shouldUpdateCompositingLayersAfterScrolling() const
2532{
2533#if ENABLE(ASYNC_SCROLLING)
2534 // If the scrolling thread is updating the fixed elements, then the FrameView should not update them as well.
2535
2536 Page* page = frame().page();
2537 if (!page)
2538 return true;
2539
2540 if (&page->mainFrame() != m_frame.ptr())
2541 return true;
2542
2543 ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator();
2544 if (!scrollingCoordinator)
2545 return true;
2546
2547 if (scrollingCoordinator->shouldUpdateScrollLayerPositionSynchronously(*this))
2548 return true;
2549
2550 if (currentScrollType() == ScrollType::Programmatic)
2551 return true;
2552
2553 return false;
2554#endif
2555 return true;
2556}
2557
2558void FrameView::updateCompositingLayersAfterScrolling()
2559{
2560 ASSERT(layoutContext().layoutPhase() >= FrameViewLayoutContext::LayoutPhase::InPostLayout || layoutContext().layoutPhase() == FrameViewLayoutContext::LayoutPhase::OutsideLayout);
2561
2562 if (!shouldUpdateCompositingLayersAfterScrolling())
2563 return;
2564
2565 if (!layoutContext().isLayoutNested() && hasViewportConstrainedObjects()) {
2566 if (RenderView* renderView = this->renderView())
2567 renderView->compositor().updateCompositingLayers(CompositingUpdateType::OnScroll);
2568 }
2569}
2570
2571bool FrameView::isRubberBandInProgress() const
2572{
2573 if (scrollbarsSuppressed())
2574 return false;
2575
2576 // If the scrolling thread updates the scroll position for this FrameView, then we should return
2577 // ScrollingCoordinator::isRubberBandInProgress().
2578 if (Page* page = frame().page()) {
2579 if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) {
2580 if (!scrollingCoordinator->shouldUpdateScrollLayerPositionSynchronously(*this))
2581 return scrollingCoordinator->isRubberBandInProgress();
2582 }
2583 }
2584
2585 // If the main thread updates the scroll position for this FrameView, we should return
2586 // ScrollAnimator::isRubberBandInProgress().
2587 if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
2588 return scrollAnimator->isRubberBandInProgress();
2589
2590 return false;
2591}
2592
2593bool FrameView::requestScrollPositionUpdate(const ScrollPosition& position)
2594{
2595 LOG_WITH_STREAM(Scrolling, stream << "FrameView::requestScrollPositionUpdate " << position);
2596
2597#if ENABLE(ASYNC_SCROLLING)
2598 if (TiledBacking* tiledBacking = this->tiledBacking())
2599 tiledBacking->prepopulateRect(FloatRect(position, visibleContentRect().size()));
2600#endif
2601
2602#if ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS)
2603 if (Page* page = frame().page()) {
2604 if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator())
2605 return scrollingCoordinator->requestScrollPositionUpdate(*this, position);
2606 }
2607#else
2608 UNUSED_PARAM(position);
2609#endif
2610
2611 return false;
2612}
2613
2614HostWindow* FrameView::hostWindow() const
2615{
2616 auto* page = frame().page();
2617 if (!page)
2618 return nullptr;
2619 return &page->chrome();
2620}
2621
2622void FrameView::addTrackedRepaintRect(const FloatRect& r)
2623{
2624 if (!m_isTrackingRepaints || r.isEmpty())
2625 return;
2626
2627 FloatRect repaintRect = r;
2628 repaintRect.moveBy(-scrollPosition());
2629 m_trackedRepaintRects.append(repaintRect);
2630}
2631
2632void FrameView::repaintContentRectangle(const IntRect& r)
2633{
2634 ASSERT(!frame().ownerElement());
2635
2636 if (!shouldUpdate())
2637 return;
2638
2639 ScrollView::repaintContentRectangle(r);
2640}
2641
2642static unsigned countRenderedCharactersInRenderObjectWithThreshold(const RenderElement& renderer, unsigned threshold)
2643{
2644 unsigned count = 0;
2645 for (const RenderObject* descendant = &renderer; descendant; descendant = descendant->nextInPreOrder()) {
2646 if (is<RenderText>(*descendant)) {
2647 count += downcast<RenderText>(*descendant).text().length();
2648 if (count >= threshold)
2649 break;
2650 }
2651 }
2652 return count;
2653}
2654
2655bool FrameView::renderedCharactersExceed(unsigned threshold)
2656{
2657 if (!frame().contentRenderer())
2658 return false;
2659 return countRenderedCharactersInRenderObjectWithThreshold(*frame().contentRenderer(), threshold) >= threshold;
2660}
2661
2662void FrameView::availableContentSizeChanged(AvailableSizeChangeReason reason)
2663{
2664 if (Document* document = frame().document()) {
2665 // FIXME: Merge this logic with m_setNeedsLayoutWasDeferred and find a more appropriate
2666 // way of handling potential recursive layouts when the viewport is resized to accomodate
2667 // the content but the content always overflows the viewport. See webkit.org/b/165781.
2668 if (!(layoutContext().layoutPhase() == FrameViewLayoutContext::LayoutPhase::InViewSizeAdjust && useFixedLayout()))
2669 document->updateViewportUnitsOnResize();
2670 }
2671
2672 updateLayoutViewport();
2673 setNeedsLayoutAfterViewConfigurationChange();
2674 ScrollView::availableContentSizeChanged(reason);
2675}
2676
2677bool FrameView::shouldLayoutAfterContentsResized() const
2678{
2679 return !useFixedLayout() || useCustomFixedPositionLayoutRect();
2680}
2681
2682void FrameView::updateContentsSize()
2683{
2684 // We check to make sure the view is attached to a frame() as this method can
2685 // be triggered before the view is attached by Frame::createView(...) setting
2686 // various values such as setScrollBarModes(...) for example. An ASSERT is
2687 // triggered when a view is layout before being attached to a frame().
2688 if (!frame().view())
2689 return;
2690
2691#if PLATFORM(IOS_FAMILY)
2692 if (RenderView* root = m_frame->contentRenderer()) {
2693 if (useCustomFixedPositionLayoutRect() && hasViewportConstrainedObjects()) {
2694 setViewportConstrainedObjectsNeedLayout();
2695 // We must eagerly enter compositing mode because fixed position elements
2696 // will not have been made compositing via a preceding style change before
2697 // m_useCustomFixedPositionLayoutRect was true.
2698 root->compositor().enableCompositingMode();
2699 }
2700 }
2701#endif
2702
2703 if (shouldLayoutAfterContentsResized() && needsLayout())
2704 layoutContext().layout();
2705
2706 if (RenderView* renderView = this->renderView()) {
2707 if (renderView->usesCompositing())
2708 renderView->compositor().frameViewDidChangeSize();
2709 }
2710}
2711
2712void FrameView::addedOrRemovedScrollbar()
2713{
2714 if (RenderView* renderView = this->renderView()) {
2715 if (renderView->usesCompositing())
2716 renderView->compositor().frameViewDidAddOrRemoveScrollbars();
2717 }
2718
2719 updateTiledBackingAdaptiveSizing();
2720}
2721
2722TiledBacking::Scrollability FrameView::computeScrollability() const
2723{
2724 auto* page = frame().page();
2725
2726 // Use smaller square tiles if the Window is not active to facilitate app napping.
2727 if (!page || !page->isWindowActive())
2728 return TiledBacking::HorizontallyScrollable | TiledBacking::VerticallyScrollable;
2729
2730 bool horizontallyScrollable;
2731 bool verticallyScrollable;
2732 bool clippedByAncestorView = static_cast<bool>(m_viewExposedRect);
2733
2734#if PLATFORM(IOS_FAMILY)
2735 if (page)
2736 clippedByAncestorView |= page->enclosedInScrollableAncestorView();
2737#endif
2738
2739 if (delegatesScrolling()) {
2740 IntSize documentSize = contentsSize();
2741 IntSize visibleSize = this->visibleSize();
2742
2743 horizontallyScrollable = clippedByAncestorView || documentSize.width() > visibleSize.width();
2744 verticallyScrollable = clippedByAncestorView || documentSize.height() > visibleSize.height();
2745 } else {
2746 horizontallyScrollable = clippedByAncestorView || horizontalScrollbar();
2747 verticallyScrollable = clippedByAncestorView || verticalScrollbar();
2748 }
2749
2750 TiledBacking::Scrollability scrollability = TiledBacking::NotScrollable;
2751 if (horizontallyScrollable)
2752 scrollability = TiledBacking::HorizontallyScrollable;
2753
2754 if (verticallyScrollable)
2755 scrollability |= TiledBacking::VerticallyScrollable;
2756
2757 return scrollability;
2758}
2759
2760void FrameView::updateTiledBackingAdaptiveSizing()
2761{
2762 auto* tiledBacking = this->tiledBacking();
2763 if (!tiledBacking)
2764 return;
2765
2766 tiledBacking->setScrollability(computeScrollability());
2767}
2768
2769#if PLATFORM(IOS_FAMILY)
2770
2771void FrameView::didUpdateViewportOverrideRects()
2772{
2773 if (!frame().settings().visualViewportAPIEnabled())
2774 return;
2775
2776 if (auto* window = frame().window())
2777 window->visualViewport().update();
2778}
2779
2780void FrameView::unobscuredContentSizeChanged()
2781{
2782 updateTiledBackingAdaptiveSizing();
2783}
2784
2785#endif
2786
2787static LayerFlushThrottleState::Flags determineLayerFlushThrottleState(Page& page)
2788{
2789 // We only throttle when constantly receiving new data during the inital page load.
2790 if (!page.progress().isMainLoadProgressing())
2791 return 0;
2792 // Scrolling during page loading disables throttling.
2793 if (page.mainFrame().view()->wasScrolledByUser())
2794 return 0;
2795 // Disable for image documents so large GIF animations don't get throttled during loading.
2796 auto* document = page.mainFrame().document();
2797 if (!document || is<ImageDocument>(*document))
2798 return 0;
2799 return LayerFlushThrottleState::Enabled;
2800}
2801
2802void FrameView::disableLayerFlushThrottlingTemporarilyForInteraction()
2803{
2804 if (!frame().page())
2805 return;
2806 auto& page = *frame().page();
2807
2808 LayerFlushThrottleState::Flags flags = LayerFlushThrottleState::UserIsInteracting | determineLayerFlushThrottleState(page);
2809 if (page.chrome().client().adjustLayerFlushThrottling(flags))
2810 return;
2811
2812 if (RenderView* view = renderView())
2813 view->compositor().disableLayerFlushThrottlingTemporarilyForInteraction();
2814}
2815
2816void FrameView::loadProgressingStatusChanged()
2817{
2818 if (!m_isVisuallyNonEmpty && frame().loader().isComplete())
2819 fireLayoutRelatedMilestonesIfNeeded();
2820 updateLayerFlushThrottling();
2821 adjustTiledBackingCoverage();
2822}
2823
2824void FrameView::updateLayerFlushThrottling()
2825{
2826 Page* page = frame().page();
2827 if (!page)
2828 return;
2829
2830 ASSERT(frame().isMainFrame());
2831
2832 LayerFlushThrottleState::Flags flags = determineLayerFlushThrottleState(*page);
2833
2834 // See if the client is handling throttling.
2835 if (page->chrome().client().adjustLayerFlushThrottling(flags))
2836 return;
2837
2838 for (auto* frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) {
2839 if (RenderView* renderView = frame->contentRenderer())
2840 renderView->compositor().setLayerFlushThrottlingEnabled(flags & LayerFlushThrottleState::Enabled);
2841 }
2842}
2843
2844void FrameView::adjustTiledBackingCoverage()
2845{
2846 if (!m_speculativeTilingEnabled)
2847 enableSpeculativeTilingIfNeeded();
2848
2849 RenderView* renderView = this->renderView();
2850 if (renderView && renderView->layer() && renderView->layer()->backing())
2851 renderView->layer()->backing()->adjustTiledBackingCoverage();
2852#if PLATFORM(IOS_FAMILY)
2853 if (LegacyTileCache* tileCache = legacyTileCache())
2854 tileCache->setSpeculativeTileCreationEnabled(m_speculativeTilingEnabled);
2855#endif
2856}
2857
2858static bool shouldEnableSpeculativeTilingDuringLoading(const FrameView& view)
2859{
2860 Page* page = view.frame().page();
2861 return page && view.isVisuallyNonEmpty() && !page->progress().isMainLoadProgressing();
2862}
2863
2864void FrameView::enableSpeculativeTilingIfNeeded()
2865{
2866 ASSERT(!m_speculativeTilingEnabled);
2867 if (m_wasScrolledByUser) {
2868 m_speculativeTilingEnabled = true;
2869 return;
2870 }
2871 if (!shouldEnableSpeculativeTilingDuringLoading(*this))
2872 return;
2873
2874 if (m_speculativeTilingDelayDisabledForTesting) {
2875 speculativeTilingEnableTimerFired();
2876 return;
2877 }
2878
2879 if (m_speculativeTilingEnableTimer.isActive())
2880 return;
2881 // Delay enabling a bit as load completion may trigger further loading from scripts.
2882 static const Seconds speculativeTilingEnableDelay { 500_ms };
2883 m_speculativeTilingEnableTimer.startOneShot(speculativeTilingEnableDelay);
2884}
2885
2886void FrameView::speculativeTilingEnableTimerFired()
2887{
2888 if (m_speculativeTilingEnabled)
2889 return;
2890 m_speculativeTilingEnabled = shouldEnableSpeculativeTilingDuringLoading(*this);
2891 adjustTiledBackingCoverage();
2892}
2893
2894void FrameView::show()
2895{
2896 ScrollView::show();
2897
2898 if (frame().isMainFrame()) {
2899 // Turn off speculative tiling for a brief moment after a FrameView appears on screen.
2900 // Note that adjustTiledBackingCoverage() kicks the (500ms) timer to re-enable it.
2901 m_speculativeTilingEnabled = false;
2902 m_wasScrolledByUser = false;
2903 adjustTiledBackingCoverage();
2904 }
2905}
2906
2907void FrameView::hide()
2908{
2909 ScrollView::hide();
2910 adjustTiledBackingCoverage();
2911}
2912
2913bool FrameView::needsLayout() const
2914{
2915 return layoutContext().needsLayout();
2916}
2917
2918void FrameView::setNeedsLayoutAfterViewConfigurationChange()
2919{
2920 layoutContext().setNeedsLayoutAfterViewConfigurationChange();
2921}
2922
2923void FrameView::setNeedsCompositingConfigurationUpdate()
2924{
2925 RenderView* renderView = this->renderView();
2926 if (renderView->usesCompositing()) {
2927 if (auto* rootLayer = renderView->layer())
2928 rootLayer->setNeedsCompositingConfigurationUpdate();
2929 renderView->compositor().scheduleCompositingLayerUpdate();
2930 }
2931}
2932
2933void FrameView::setNeedsCompositingGeometryUpdate()
2934{
2935 RenderView* renderView = this->renderView();
2936 if (renderView->usesCompositing()) {
2937 if (auto* rootLayer = renderView->layer())
2938 rootLayer->setNeedsCompositingGeometryUpdate();
2939 renderView->compositor().scheduleCompositingLayerUpdate();
2940 }
2941}
2942
2943void FrameView::scheduleSelectionUpdate()
2944{
2945 if (needsLayout())
2946 return;
2947 // FIXME: We should not need to go through the layout process since selection update does not change dimension/geometry.
2948 // However we can't tell at this point if the tree is stable yet, so let's just schedule a root only layout for now.
2949 setNeedsLayoutAfterViewConfigurationChange();
2950}
2951
2952bool FrameView::isTransparent() const
2953{
2954 return m_isTransparent;
2955}
2956
2957void FrameView::setTransparent(bool isTransparent)
2958{
2959 if (m_isTransparent == isTransparent)
2960 return;
2961
2962 m_isTransparent = isTransparent;
2963
2964 // setTransparent can be called in the window between FrameView initialization
2965 // and switching in the new Document; this means that the RenderView that we
2966 // retrieve is actually attached to the previous Document, which is going away,
2967 // and must not update compositing layers.
2968 if (!isViewForDocumentInFrame())
2969 return;
2970
2971 setNeedsLayoutAfterViewConfigurationChange();
2972 setNeedsCompositingConfigurationUpdate();
2973}
2974
2975bool FrameView::hasOpaqueBackground() const
2976{
2977 return !m_isTransparent && m_baseBackgroundColor.isOpaque();
2978}
2979
2980Color FrameView::baseBackgroundColor() const
2981{
2982 return m_baseBackgroundColor;
2983}
2984
2985void FrameView::setBaseBackgroundColor(const Color& backgroundColor)
2986{
2987 Color newBaseBackgroundColor = backgroundColor.isValid() ? backgroundColor : Color::white;
2988 if (m_baseBackgroundColor == newBaseBackgroundColor)
2989 return;
2990
2991 m_baseBackgroundColor = newBaseBackgroundColor;
2992
2993 if (!isViewForDocumentInFrame())
2994 return;
2995
2996 recalculateScrollbarOverlayStyle();
2997 setNeedsLayoutAfterViewConfigurationChange();
2998 setNeedsCompositingConfigurationUpdate();
2999}
3000
3001void FrameView::updateBackgroundRecursively(const Optional<Color>& backgroundColor)
3002{
3003#if HAVE(OS_DARK_MODE_SUPPORT)
3004#if PLATFORM(MAC) || PLATFORM(IOS_FAMILY)
3005 static const auto cssValueControlBackground = CSSValueAppleSystemControlBackground;
3006#else
3007 static const auto cssValueControlBackground = CSSValueWindow;
3008#endif
3009 Color baseBackgroundColor = backgroundColor.valueOr(RenderTheme::singleton().systemColor(cssValueControlBackground, styleColorOptions()));
3010#else
3011 Color baseBackgroundColor = backgroundColor.valueOr(Color::white);
3012#endif
3013
3014 for (auto* frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) {
3015 if (FrameView* view = frame->view()) {
3016 view->setTransparent(!baseBackgroundColor.isVisible());
3017 view->setBaseBackgroundColor(baseBackgroundColor);
3018 if (view->needsLayout())
3019 view->layoutContext().scheduleLayout();
3020 }
3021 }
3022}
3023
3024bool FrameView::hasExtendedBackgroundRectForPainting() const
3025{
3026 TiledBacking* tiledBacking = this->tiledBacking();
3027 if (!tiledBacking)
3028 return false;
3029
3030 return tiledBacking->hasMargins();
3031}
3032
3033void FrameView::updateExtendBackgroundIfNecessary()
3034{
3035 ExtendedBackgroundMode mode = calculateExtendedBackgroundMode();
3036 if (mode == ExtendedBackgroundModeNone)
3037 return;
3038
3039 updateTilesForExtendedBackgroundMode(mode);
3040}
3041
3042FrameView::ExtendedBackgroundMode FrameView::calculateExtendedBackgroundMode() const
3043{
3044#if PLATFORM(IOS_FAMILY)
3045 // <rdar://problem/16201373>
3046 return ExtendedBackgroundModeNone;
3047#else
3048 if (!frame().settings().backgroundShouldExtendBeyondPage())
3049 return ExtendedBackgroundModeNone;
3050
3051 // Just because Settings::backgroundShouldExtendBeyondPage() is true does not necessarily mean
3052 // that the background rect needs to be extended for painting. Simple backgrounds can be extended
3053 // just with RenderLayerCompositor's rootExtendedBackgroundColor. More complicated backgrounds,
3054 // such as images, require extending the background rect to continue painting into the extended
3055 // region. This function finds out if it is necessary to extend the background rect for painting.
3056
3057 if (!frame().isMainFrame())
3058 return ExtendedBackgroundModeNone;
3059
3060 Document* document = frame().document();
3061 if (!document)
3062 return ExtendedBackgroundModeNone;
3063
3064 if (!renderView())
3065 return ExtendedBackgroundModeNone;
3066
3067 auto* rootBackgroundRenderer = renderView()->rendererForRootBackground();
3068 if (!rootBackgroundRenderer)
3069 return ExtendedBackgroundModeNone;
3070
3071 if (!rootBackgroundRenderer->style().hasBackgroundImage())
3072 return ExtendedBackgroundModeNone;
3073
3074 ExtendedBackgroundMode mode = ExtendedBackgroundModeNone;
3075 if (rootBackgroundRenderer->style().backgroundRepeatX() == FillRepeat::Repeat)
3076 mode |= ExtendedBackgroundModeHorizontal;
3077 if (rootBackgroundRenderer->style().backgroundRepeatY() == FillRepeat::Repeat)
3078 mode |= ExtendedBackgroundModeVertical;
3079
3080 return mode;
3081#endif
3082}
3083
3084void FrameView::updateTilesForExtendedBackgroundMode(ExtendedBackgroundMode mode)
3085{
3086 RenderView* renderView = this->renderView();
3087 if (!renderView)
3088 return;
3089
3090 RenderLayerBacking* backing = renderView->layer()->backing();
3091 if (!backing)
3092 return;
3093
3094 TiledBacking* tiledBacking = backing->tiledBacking();
3095 if (!tiledBacking)
3096 return;
3097
3098 ExtendedBackgroundMode existingMode = ExtendedBackgroundModeNone;
3099 if (tiledBacking->hasVerticalMargins())
3100 existingMode |= ExtendedBackgroundModeVertical;
3101 if (tiledBacking->hasHorizontalMargins())
3102 existingMode |= ExtendedBackgroundModeHorizontal;
3103
3104 if (existingMode == mode)
3105 return;
3106
3107 backing->setTiledBackingHasMargins(mode & ExtendedBackgroundModeHorizontal, mode & ExtendedBackgroundModeVertical);
3108}
3109
3110IntRect FrameView::extendedBackgroundRectForPainting() const
3111{
3112 TiledBacking* tiledBacking = this->tiledBacking();
3113 if (!tiledBacking)
3114 return IntRect();
3115
3116 RenderView* renderView = this->renderView();
3117 if (!renderView)
3118 return IntRect();
3119
3120 LayoutRect extendedRect = renderView->unextendedBackgroundRect();
3121 if (!tiledBacking->hasMargins())
3122 return snappedIntRect(extendedRect);
3123
3124 extendedRect.moveBy(LayoutPoint(-tiledBacking->leftMarginWidth(), -tiledBacking->topMarginHeight()));
3125 extendedRect.expand(LayoutSize(tiledBacking->leftMarginWidth() + tiledBacking->rightMarginWidth(), tiledBacking->topMarginHeight() + tiledBacking->bottomMarginHeight()));
3126 return snappedIntRect(extendedRect);
3127}
3128
3129bool FrameView::shouldUpdateWhileOffscreen() const
3130{
3131 return m_shouldUpdateWhileOffscreen;
3132}
3133
3134void FrameView::setShouldUpdateWhileOffscreen(bool shouldUpdateWhileOffscreen)
3135{
3136 m_shouldUpdateWhileOffscreen = shouldUpdateWhileOffscreen;
3137}
3138
3139bool FrameView::shouldUpdate() const
3140{
3141 if (isOffscreen() && !shouldUpdateWhileOffscreen())
3142 return false;
3143 return true;
3144}
3145
3146bool FrameView::safeToPropagateScrollToParent() const
3147{
3148 auto* document = frame().document();
3149 if (!document)
3150 return false;
3151
3152 auto* parentFrame = frame().tree().parent();
3153 if (!parentFrame)
3154 return false;
3155
3156 auto* parentDocument = parentFrame->document();
3157 if (!parentDocument)
3158 return false;
3159
3160 return document->securityOrigin().canAccess(parentDocument->securityOrigin());
3161}
3162
3163void FrameView::scrollToAnchor()
3164{
3165 RefPtr<ContainerNode> anchorNode = m_maintainScrollPositionAnchor;
3166
3167 LOG_WITH_STREAM(Scrolling, stream << "FrameView::scrollToAnchor() " << anchorNode.get());
3168
3169 if (!anchorNode)
3170 return;
3171
3172 if (!anchorNode->renderer())
3173 return;
3174 m_shouldScrollToFocusedElement = false;
3175 m_delayedScrollToFocusedElementTimer.stop();
3176
3177 LayoutRect rect;
3178 bool insideFixed = false;
3179 if (anchorNode != frame().document() && anchorNode->renderer())
3180 rect = anchorNode->renderer()->absoluteAnchorRect(&insideFixed);
3181
3182 LOG_WITH_STREAM(Scrolling, stream << " anchor node rect " << rect);
3183
3184 // Scroll nested layers and frames to reveal the anchor.
3185 // Align to the top and to the closest side (this matches other browsers).
3186 if (anchorNode->renderer()->style().isHorizontalWritingMode())
3187 anchorNode->renderer()->scrollRectToVisible(rect, insideFixed, { SelectionRevealMode::Reveal, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignTopAlways, ShouldAllowCrossOriginScrolling::No });
3188 else if (anchorNode->renderer()->style().isFlippedBlocksWritingMode())
3189 anchorNode->renderer()->scrollRectToVisible(rect, insideFixed, { SelectionRevealMode::Reveal, ScrollAlignment::alignRightAlways, ScrollAlignment::alignToEdgeIfNeeded, ShouldAllowCrossOriginScrolling::No });
3190 else
3191 anchorNode->renderer()->scrollRectToVisible(rect, insideFixed, { SelectionRevealMode::Reveal, ScrollAlignment::alignLeftAlways, ScrollAlignment::alignToEdgeIfNeeded, ShouldAllowCrossOriginScrolling::No });
3192
3193 if (AXObjectCache* cache = frame().document()->existingAXObjectCache())
3194 cache->handleScrolledToAnchor(anchorNode.get());
3195
3196 // scrollRectToVisible can call into setScrollPosition(), which resets m_maintainScrollPositionAnchor.
3197 LOG_WITH_STREAM(Scrolling, stream << " restoring anchor node to " << anchorNode.get());
3198 m_maintainScrollPositionAnchor = anchorNode;
3199 m_shouldScrollToFocusedElement = false;
3200 m_delayedScrollToFocusedElementTimer.stop();
3201}
3202
3203void FrameView::updateEmbeddedObject(RenderEmbeddedObject& embeddedObject)
3204{
3205 // No need to update if it's already crashed or known to be missing.
3206 if (embeddedObject.isPluginUnavailable())
3207 return;
3208
3209 HTMLFrameOwnerElement& element = embeddedObject.frameOwnerElement();
3210
3211 if (embeddedObject.isSnapshottedPlugIn()) {
3212 if (is<HTMLObjectElement>(element) || is<HTMLEmbedElement>(element)) {
3213 HTMLPlugInImageElement& pluginElement = downcast<HTMLPlugInImageElement>(element);
3214 pluginElement.checkSnapshotStatus();
3215 }
3216 return;
3217 }
3218
3219 auto weakRenderer = makeWeakPtr(embeddedObject);
3220
3221 // FIXME: This could turn into a real virtual dispatch if we defined
3222 // updateWidget(PluginCreationOption) on HTMLElement.
3223 if (is<HTMLPlugInImageElement>(element)) {
3224 HTMLPlugInImageElement& pluginElement = downcast<HTMLPlugInImageElement>(element);
3225 if (pluginElement.needsCheckForSizeChange()) {
3226 pluginElement.checkSnapshotStatus();
3227 return;
3228 }
3229 if (pluginElement.needsWidgetUpdate())
3230 pluginElement.updateWidget(CreatePlugins::Yes);
3231 } else
3232 ASSERT_NOT_REACHED();
3233
3234 // It's possible the renderer was destroyed below updateWidget() since loading a plugin may execute arbitrary JavaScript.
3235 if (!weakRenderer)
3236 return;
3237
3238 auto ignoreWidgetState = embeddedObject.updateWidgetPosition();
3239 UNUSED_PARAM(ignoreWidgetState);
3240}
3241
3242bool FrameView::updateEmbeddedObjects()
3243{
3244 if (layoutContext().isLayoutNested() || !m_embeddedObjectsToUpdate || m_embeddedObjectsToUpdate->isEmpty())
3245 return true;
3246
3247 WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
3248
3249 // Insert a marker for where we should stop.
3250 ASSERT(!m_embeddedObjectsToUpdate->contains(nullptr));
3251 m_embeddedObjectsToUpdate->add(nullptr);
3252
3253 while (!m_embeddedObjectsToUpdate->isEmpty()) {
3254 RenderEmbeddedObject* embeddedObject = m_embeddedObjectsToUpdate->takeFirst();
3255 if (!embeddedObject)
3256 break;
3257 updateEmbeddedObject(*embeddedObject);
3258 }
3259
3260 return m_embeddedObjectsToUpdate->isEmpty();
3261}
3262
3263void FrameView::updateEmbeddedObjectsTimerFired()
3264{
3265 RefPtr<FrameView> protectedThis(this);
3266 m_updateEmbeddedObjectsTimer.stop();
3267 for (unsigned i = 0; i < maxUpdateEmbeddedObjectsIterations; i++) {
3268 if (updateEmbeddedObjects())
3269 break;
3270 }
3271}
3272
3273void FrameView::flushAnyPendingPostLayoutTasks()
3274{
3275 layoutContext().flushAsynchronousTasks();
3276 if (m_updateEmbeddedObjectsTimer.isActive())
3277 updateEmbeddedObjectsTimerFired();
3278}
3279
3280void FrameView::queuePostLayoutCallback(Function<void()>&& callback)
3281{
3282 m_postLayoutCallbackQueue.append(WTFMove(callback));
3283}
3284
3285void FrameView::flushPostLayoutTasksQueue()
3286{
3287 if (layoutContext().isLayoutNested())
3288 return;
3289
3290 if (!m_postLayoutCallbackQueue.size())
3291 return;
3292
3293 Vector<Function<void()>> queue = WTFMove(m_postLayoutCallbackQueue);
3294 for (auto& task : queue)
3295 task();
3296}
3297
3298void FrameView::performPostLayoutTasks()
3299{
3300 // FIXME: We should not run any JavaScript code in this function.
3301 LOG(Layout, "FrameView %p performPostLayoutTasks", this);
3302 updateHasReachedSignificantRenderedTextThreshold();
3303 frame().selection().updateAppearanceAfterLayout();
3304
3305 flushPostLayoutTasksQueue();
3306
3307 if (!layoutContext().isLayoutNested() && frame().document()->documentElement())
3308 fireLayoutRelatedMilestonesIfNeeded();
3309
3310#if PLATFORM(IOS_FAMILY)
3311 // Only send layout-related delegate callbacks synchronously for the main frame to
3312 // avoid re-entering layout for the main frame while delivering a layout-related delegate
3313 // callback for a subframe.
3314 if (frame().isMainFrame()) {
3315 if (Page* page = frame().page())
3316 page->chrome().client().didLayout();
3317 }
3318#endif
3319
3320 // FIXME: We should consider adding DidLayout as a LayoutMilestone. That would let us merge this
3321 // with didLayout(LayoutMilestones).
3322 frame().loader().client().dispatchDidLayout();
3323
3324 updateWidgetPositions();
3325
3326#if ENABLE(CSS_SCROLL_SNAP)
3327 updateSnapOffsets();
3328#endif
3329 m_updateEmbeddedObjectsTimer.startOneShot(0_s);
3330
3331 if (auto* page = frame().page()) {
3332 if (auto* scrollingCoordinator = page->scrollingCoordinator())
3333 scrollingCoordinator->frameViewLayoutUpdated(*this);
3334 }
3335
3336 if (RenderView* renderView = this->renderView()) {
3337 if (renderView->usesCompositing())
3338 renderView->compositor().frameViewDidLayout();
3339 }
3340
3341 scrollToAnchor();
3342
3343 sendResizeEventIfNeeded();
3344
3345 updateLayoutViewport();
3346 viewportContentsChanged();
3347
3348 updateScrollSnapState();
3349
3350 if (AXObjectCache* cache = frame().document()->existingAXObjectCache())
3351 cache->performDeferredCacheUpdate();
3352}
3353
3354IntSize FrameView::sizeForResizeEvent() const
3355{
3356#if PLATFORM(IOS_FAMILY)
3357 if (m_useCustomSizeForResizeEvent)
3358 return m_customSizeForResizeEvent;
3359#endif
3360 if (useFixedLayout() && !fixedLayoutSize().isEmpty() && delegatesScrolling())
3361 return fixedLayoutSize();
3362 return visibleContentRectIncludingScrollbars().size();
3363}
3364
3365void FrameView::sendResizeEventIfNeeded()
3366{
3367 if (layoutContext().isInRenderTreeLayout() || needsLayout())
3368 return;
3369
3370 RenderView* renderView = this->renderView();
3371 if (!renderView || renderView->printing())
3372 return;
3373
3374 if (frame().page() && frame().page()->chrome().client().isSVGImageChromeClient())
3375 return;
3376
3377 IntSize currentSize = sizeForResizeEvent();
3378 float currentZoomFactor = renderView->style().zoom();
3379
3380 if (currentSize == m_lastViewportSize && currentZoomFactor == m_lastZoomFactor)
3381 return;
3382
3383 m_lastViewportSize = currentSize;
3384 m_lastZoomFactor = currentZoomFactor;
3385
3386 if (!layoutContext().didFirstLayout())
3387 return;
3388
3389#if PLATFORM(IOS_FAMILY)
3390 // Don't send the resize event if the document is loading. Some pages automatically reload
3391 // when the window is resized; Safari on iOS often resizes the window while setting up its
3392 // viewport. This obviously can cause problems.
3393 if (DocumentLoader* documentLoader = frame().loader().documentLoader()) {
3394 if (documentLoader->isLoadingInAPISense())
3395 return;
3396 }
3397#endif
3398
3399 bool isMainFrame = frame().isMainFrame();
3400 bool canSendResizeEventSynchronously = isMainFrame && !m_shouldAutoSize;
3401
3402 LOG(Events, "FrameView %p sendResizeEventIfNeeded sending resize event, size %dx%d (canSendResizeEventSynchronously %d)", this, currentSize.width(), currentSize.height(), canSendResizeEventSynchronously);
3403
3404 Ref<Event> resizeEvent = Event::create(eventNames().resizeEvent, Event::CanBubble::No, Event::IsCancelable::No);
3405 if (canSendResizeEventSynchronously)
3406 frame().document()->dispatchWindowEvent(resizeEvent);
3407 else {
3408 // FIXME: Queueing this event for an unpredictable time in the future seems
3409 // intrinsically racy. By the time this resize event fires, the frame might
3410 // be resized again, so we could end up with two resize events for the same size.
3411 frame().document()->enqueueWindowEvent(WTFMove(resizeEvent));
3412 }
3413
3414 if (InspectorInstrumentation::hasFrontends() && isMainFrame) {
3415 if (Page* page = frame().page()) {
3416 if (InspectorClient* inspectorClient = page->inspectorController().inspectorClient())
3417 inspectorClient->didResizeMainFrame(&frame());
3418 }
3419 }
3420}
3421
3422void FrameView::willStartLiveResize()
3423{
3424 ScrollView::willStartLiveResize();
3425 adjustTiledBackingCoverage();
3426}
3427
3428void FrameView::willEndLiveResize()
3429{
3430 ScrollView::willEndLiveResize();
3431 adjustTiledBackingCoverage();
3432}
3433
3434void FrameView::autoSizeIfEnabled()
3435{
3436 if (!m_shouldAutoSize)
3437 return;
3438
3439 if (m_inAutoSize)
3440 return;
3441
3442 auto* document = frame().document();
3443 if (!document)
3444 return;
3445
3446 auto* renderView = document->renderView();
3447 if (!renderView)
3448 return;
3449
3450 auto* firstChild = renderView->firstChild();
3451 if (!firstChild)
3452 return;
3453
3454 LOG(Layout, "FrameView %p autoSizeIfEnabled", this);
3455 SetForScope<bool> changeInAutoSize(m_inAutoSize, true);
3456 if (layoutContext().subtreeLayoutRoot())
3457 layoutContext().convertSubtreeLayoutToFullLayout();
3458
3459 ScrollbarMode horizonalScrollbarMode = ScrollbarAlwaysOff;
3460 ScrollbarMode verticalScrollbarMode = ScrollbarAlwaysOff;
3461 setVerticalScrollbarLock(false);
3462 setHorizontalScrollbarLock(false);
3463 setScrollbarModes(horizonalScrollbarMode, verticalScrollbarMode, true, true);
3464
3465 ASSERT(is<RenderElement>(*firstChild));
3466 auto& documentRenderer = downcast<RenderElement>(*firstChild);
3467 documentRenderer.mutableStyle().setMaxWidth(Length(m_autoSizeConstraint.width(), Fixed));
3468 resize(m_autoSizeConstraint.width(), m_autoSizeConstraint.height());
3469 document->updateStyleIfNeeded();
3470 document->updateLayoutIgnorePendingStylesheets();
3471
3472 auto currentContentsSize = this->contentsSize();
3473 auto finalWidth = std::max(m_autoSizeConstraint.width(), currentContentsSize.width());
3474 auto finalHeight = m_autoSizeFixedMinimumHeight ? std::max(m_autoSizeFixedMinimumHeight, currentContentsSize.height()) : currentContentsSize.height();
3475 resize(finalWidth, finalHeight);
3476 document->updateLayoutIgnorePendingStylesheets();
3477 m_autoSizeContentSize = contentsSize();
3478 if (auto* page = frame().page())
3479 page->chrome().client().intrinsicContentsSizeChanged(m_autoSizeContentSize);
3480 m_didRunAutosize = true;
3481}
3482
3483void FrameView::setAutoSizeFixedMinimumHeight(int fixedMinimumHeight)
3484{
3485 if (m_autoSizeFixedMinimumHeight == fixedMinimumHeight)
3486 return;
3487
3488 m_autoSizeFixedMinimumHeight = fixedMinimumHeight;
3489
3490 setNeedsLayoutAfterViewConfigurationChange();
3491}
3492
3493RenderElement* FrameView::viewportRenderer() const
3494{
3495 if (m_viewportRendererType == ViewportRendererType::None)
3496 return nullptr;
3497
3498 auto* document = frame().document();
3499 if (!document)
3500 return nullptr;
3501
3502 if (m_viewportRendererType == ViewportRendererType::Document) {
3503 auto* documentElement = document->documentElement();
3504 if (!documentElement)
3505 return nullptr;
3506 return documentElement->renderer();
3507 }
3508
3509 if (m_viewportRendererType == ViewportRendererType::Body) {
3510 auto* body = document->body();
3511 if (!body)
3512 return nullptr;
3513 return body->renderer();
3514 }
3515
3516 ASSERT_NOT_REACHED();
3517 return nullptr;
3518}
3519
3520void FrameView::updateOverflowStatus(bool horizontalOverflow, bool verticalOverflow)
3521{
3522 auto* viewportRenderer = this->viewportRenderer();
3523 if (!viewportRenderer)
3524 return;
3525
3526 if (m_overflowStatusDirty) {
3527 m_horizontalOverflow = horizontalOverflow;
3528 m_verticalOverflow = verticalOverflow;
3529 m_overflowStatusDirty = false;
3530 return;
3531 }
3532
3533 bool horizontalOverflowChanged = (m_horizontalOverflow != horizontalOverflow);
3534 bool verticalOverflowChanged = (m_verticalOverflow != verticalOverflow);
3535
3536 if (horizontalOverflowChanged || verticalOverflowChanged) {
3537 m_horizontalOverflow = horizontalOverflow;
3538 m_verticalOverflow = verticalOverflow;
3539
3540 Ref<OverflowEvent> overflowEvent = OverflowEvent::create(horizontalOverflowChanged, horizontalOverflow,
3541 verticalOverflowChanged, verticalOverflow);
3542 overflowEvent->setTarget(viewportRenderer->element());
3543
3544 frame().document()->enqueueOverflowEvent(WTFMove(overflowEvent));
3545 }
3546}
3547
3548const Pagination& FrameView::pagination() const
3549{
3550 if (m_pagination != Pagination())
3551 return m_pagination;
3552
3553 if (frame().isMainFrame()) {
3554 if (Page* page = frame().page())
3555 return page->pagination();
3556 }
3557
3558 return m_pagination;
3559}
3560
3561void FrameView::setPagination(const Pagination& pagination)
3562{
3563 if (m_pagination == pagination)
3564 return;
3565
3566 m_pagination = pagination;
3567
3568 frame().document()->styleScope().didChangeStyleSheetEnvironment();
3569}
3570
3571IntRect FrameView::windowClipRect() const
3572{
3573 ASSERT(frame().view() == this);
3574
3575 if (m_cachedWindowClipRect)
3576 return *m_cachedWindowClipRect;
3577
3578 if (paintsEntireContents())
3579 return contentsToWindow(IntRect(IntPoint(), totalContentsSize()));
3580
3581 // Set our clip rect to be our contents.
3582 IntRect clipRect = contentsToWindow(visibleContentRect(LegacyIOSDocumentVisibleRect));
3583
3584 if (!frame().ownerElement())
3585 return clipRect;
3586
3587 // Take our owner element and get its clip rect.
3588 HTMLFrameOwnerElement* ownerElement = frame().ownerElement();
3589 if (FrameView* parentView = ownerElement->document().view())
3590 clipRect.intersect(parentView->windowClipRectForFrameOwner(ownerElement, true));
3591 return clipRect;
3592}
3593
3594IntRect FrameView::windowClipRectForFrameOwner(const HTMLFrameOwnerElement* ownerElement, bool clipToLayerContents) const
3595{
3596 // The renderer can sometimes be null when style="display:none" interacts
3597 // with external content and plugins.
3598 if (!ownerElement->renderer())
3599 return windowClipRect();
3600
3601 // If we have no layer, just return our window clip rect.
3602 const RenderLayer* enclosingLayer = ownerElement->renderer()->enclosingLayer();
3603 if (!enclosingLayer)
3604 return windowClipRect();
3605
3606 // Apply the clip from the layer.
3607 IntRect clipRect;
3608 if (clipToLayerContents)
3609 clipRect = snappedIntRect(enclosingLayer->childrenClipRect());
3610 else
3611 clipRect = snappedIntRect(enclosingLayer->selfClipRect());
3612 clipRect = contentsToWindow(clipRect);
3613 return intersection(clipRect, windowClipRect());
3614}
3615
3616bool FrameView::isActive() const
3617{
3618 Page* page = frame().page();
3619 return page && page->focusController().isActive();
3620}
3621
3622bool FrameView::forceUpdateScrollbarsOnMainThreadForPerformanceTesting() const
3623{
3624 Page* page = frame().page();
3625 return page && page->settings().forceUpdateScrollbarsOnMainThreadForPerformanceTesting();
3626}
3627
3628void FrameView::scrollTo(const ScrollPosition& newPosition)
3629{
3630 IntPoint oldPosition = scrollPosition();
3631 ScrollView::scrollTo(newPosition);
3632 if (oldPosition != scrollPosition())
3633 scrollPositionChanged(oldPosition, scrollPosition());
3634
3635 didChangeScrollOffset();
3636}
3637
3638float FrameView::adjustScrollStepForFixedContent(float step, ScrollbarOrientation orientation, ScrollGranularity granularity)
3639{
3640 if (granularity != ScrollByPage || orientation == HorizontalScrollbar)
3641 return step;
3642
3643 TrackedRendererListHashSet* positionedObjects = nullptr;
3644 if (RenderView* root = frame().contentRenderer()) {
3645 if (!root->hasPositionedObjects())
3646 return step;
3647 positionedObjects = root->positionedObjects();
3648 }
3649
3650 FloatRect unobscuredContentRect = this->unobscuredContentRect();
3651 float topObscuredArea = 0;
3652 float bottomObscuredArea = 0;
3653 for (const auto& positionedObject : *positionedObjects) {
3654 const RenderStyle& style = positionedObject->style();
3655 if (style.position() != PositionType::Fixed || style.visibility() == Visibility::Hidden || !style.opacity())
3656 continue;
3657
3658 FloatQuad contentQuad = positionedObject->absoluteContentQuad();
3659 if (!contentQuad.isRectilinear())
3660 continue;
3661
3662 FloatRect contentBoundingBox = contentQuad.boundingBox();
3663 FloatRect fixedRectInView = intersection(unobscuredContentRect, contentBoundingBox);
3664
3665 if (fixedRectInView.width() < unobscuredContentRect.width())
3666 continue;
3667
3668 if (fixedRectInView.y() == unobscuredContentRect.y())
3669 topObscuredArea = std::max(topObscuredArea, fixedRectInView.height());
3670 else if (fixedRectInView.maxY() == unobscuredContentRect.maxY())
3671 bottomObscuredArea = std::max(bottomObscuredArea, fixedRectInView.height());
3672 }
3673
3674 return Scrollbar::pageStep(unobscuredContentRect.height(), unobscuredContentRect.height() - topObscuredArea - bottomObscuredArea);
3675}
3676
3677void FrameView::invalidateScrollbarRect(Scrollbar& scrollbar, const IntRect& rect)
3678{
3679 // Add in our offset within the FrameView.
3680 IntRect dirtyRect = rect;
3681 dirtyRect.moveBy(scrollbar.location());
3682 invalidateRect(dirtyRect);
3683}
3684
3685float FrameView::visibleContentScaleFactor() const
3686{
3687 if (!frame().isMainFrame() || !frame().settings().delegatesPageScaling())
3688 return 1;
3689
3690 Page* page = frame().page();
3691 if (!page)
3692 return 1;
3693
3694 return page->pageScaleFactor();
3695}
3696
3697void FrameView::setVisibleScrollerThumbRect(const IntRect& scrollerThumb)
3698{
3699 if (!frame().isMainFrame())
3700 return;
3701
3702 if (Page* page = frame().page())
3703 page->chrome().client().notifyScrollerThumbIsVisibleInRect(scrollerThumb);
3704}
3705
3706ScrollableArea* FrameView::enclosingScrollableArea() const
3707{
3708 // FIXME: Walk up the frame tree and look for a scrollable parent frame or RenderLayer.
3709 return nullptr;
3710}
3711
3712IntRect FrameView::scrollableAreaBoundingBox(bool*) const
3713{
3714 RenderWidget* ownerRenderer = frame().ownerRenderer();
3715 if (!ownerRenderer)
3716 return frameRect();
3717
3718 return ownerRenderer->absoluteContentQuad().enclosingBoundingBox();
3719}
3720
3721bool FrameView::isScrollable(Scrollability definitionOfScrollable)
3722{
3723 // Check for:
3724 // 1) If there an actual overflow.
3725 // 2) display:none or visibility:hidden set to self or inherited.
3726 // 3) overflow{-x,-y}: hidden;
3727 // 4) scrolling: no;
3728 if (!didFirstLayout())
3729 return false;
3730
3731 bool requiresActualOverflowToBeConsideredScrollable = !frame().isMainFrame() || definitionOfScrollable != Scrollability::ScrollableOrRubberbandable;
3732#if !ENABLE(RUBBER_BANDING)
3733 requiresActualOverflowToBeConsideredScrollable = true;
3734#endif
3735
3736 // Covers #1
3737 if (requiresActualOverflowToBeConsideredScrollable) {
3738 IntSize totalContentsSize = this->totalContentsSize();
3739 IntSize visibleContentSize = visibleContentRect(LegacyIOSDocumentVisibleRect).size();
3740 if (totalContentsSize.height() <= visibleContentSize.height() && totalContentsSize.width() <= visibleContentSize.width())
3741 return false;
3742 }
3743
3744 // Covers #2.
3745 HTMLFrameOwnerElement* owner = frame().ownerElement();
3746 if (owner && (!owner->renderer() || !owner->renderer()->visibleToHitTesting()))
3747 return false;
3748
3749 // Cover #3 and #4.
3750 ScrollbarMode horizontalMode;
3751 ScrollbarMode verticalMode;
3752 calculateScrollbarModesForLayout(horizontalMode, verticalMode, RulesFromWebContentOnly);
3753 if (horizontalMode == ScrollbarAlwaysOff && verticalMode == ScrollbarAlwaysOff)
3754 return false;
3755
3756 return true;
3757}
3758
3759bool FrameView::isScrollableOrRubberbandable()
3760{
3761 return isScrollable(Scrollability::ScrollableOrRubberbandable);
3762}
3763
3764bool FrameView::hasScrollableOrRubberbandableAncestor()
3765{
3766 if (frame().isMainFrame())
3767 return isScrollableOrRubberbandable();
3768
3769 for (FrameView* parent = this->parentFrameView(); parent; parent = parent->parentFrameView()) {
3770 Scrollability frameScrollability = parent->frame().isMainFrame() ? Scrollability::ScrollableOrRubberbandable : Scrollability::Scrollable;
3771 if (parent->isScrollable(frameScrollability))
3772 return true;
3773 }
3774
3775 return false;
3776}
3777
3778void FrameView::updateScrollableAreaSet()
3779{
3780 // That ensures that only inner frames are cached.
3781 FrameView* parentFrameView = this->parentFrameView();
3782 if (!parentFrameView)
3783 return;
3784
3785 if (!isScrollable()) {
3786 parentFrameView->removeScrollableArea(this);
3787 return;
3788 }
3789
3790 parentFrameView->addScrollableArea(this);
3791}
3792
3793bool FrameView::shouldSuspendScrollAnimations() const
3794{
3795 return frame().loader().state() != FrameStateComplete;
3796}
3797
3798void FrameView::scrollbarStyleChanged(ScrollbarStyle newStyle, bool forceUpdate)
3799{
3800 if (!frame().isMainFrame())
3801 return;
3802
3803 if (Page* page = frame().page())
3804 page->chrome().client().recommendedScrollbarStyleDidChange(newStyle);
3805
3806 ScrollView::scrollbarStyleChanged(newStyle, forceUpdate);
3807}
3808
3809void FrameView::notifyPageThatContentAreaWillPaint() const
3810{
3811 Page* page = frame().page();
3812 if (!page)
3813 return;
3814
3815 contentAreaWillPaint();
3816
3817 if (!m_scrollableAreas)
3818 return;
3819
3820 for (auto& scrollableArea : *m_scrollableAreas)
3821 scrollableArea->contentAreaWillPaint();
3822}
3823
3824bool FrameView::scrollAnimatorEnabled() const
3825{
3826#if ENABLE(SMOOTH_SCROLLING)
3827 if (Page* page = frame().page())
3828 return page->settings().scrollAnimatorEnabled();
3829#endif
3830
3831 return false;
3832}
3833
3834void FrameView::updateScrollCorner()
3835{
3836 RenderElement* renderer = nullptr;
3837 std::unique_ptr<RenderStyle> cornerStyle;
3838 IntRect cornerRect = scrollCornerRect();
3839
3840 if (!cornerRect.isEmpty()) {
3841 // Try the <body> element first as a scroll corner source.
3842 Document* doc = frame().document();
3843 Element* body = doc ? doc->bodyOrFrameset() : nullptr;
3844 if (body && body->renderer()) {
3845 renderer = body->renderer();
3846 cornerStyle = renderer->getUncachedPseudoStyle(PseudoStyleRequest(PseudoId::ScrollbarCorner), &renderer->style());
3847 }
3848
3849 if (!cornerStyle) {
3850 // If the <body> didn't have a custom style, then the root element might.
3851 Element* docElement = doc ? doc->documentElement() : nullptr;
3852 if (docElement && docElement->renderer()) {
3853 renderer = docElement->renderer();
3854 cornerStyle = renderer->getUncachedPseudoStyle(PseudoStyleRequest(PseudoId::ScrollbarCorner), &renderer->style());
3855 }
3856 }
3857
3858 if (!cornerStyle) {
3859 // If we have an owning iframe/frame element, then it can set the custom scrollbar also.
3860 if (RenderWidget* renderer = frame().ownerRenderer())
3861 cornerStyle = renderer->getUncachedPseudoStyle(PseudoStyleRequest(PseudoId::ScrollbarCorner), &renderer->style());
3862 }
3863 }
3864
3865 if (!cornerStyle)
3866 m_scrollCorner = nullptr;
3867 else {
3868 if (!m_scrollCorner) {
3869 m_scrollCorner = createRenderer<RenderScrollbarPart>(renderer->document(), WTFMove(*cornerStyle));
3870 m_scrollCorner->initializeStyle();
3871 } else
3872 m_scrollCorner->setStyle(WTFMove(*cornerStyle));
3873 invalidateScrollCorner(cornerRect);
3874 }
3875}
3876
3877void FrameView::paintScrollCorner(GraphicsContext& context, const IntRect& cornerRect)
3878{
3879 if (context.invalidatingControlTints()) {
3880 updateScrollCorner();
3881 return;
3882 }
3883
3884 if (m_scrollCorner) {
3885 if (frame().isMainFrame())
3886 context.fillRect(cornerRect, baseBackgroundColor());
3887 m_scrollCorner->paintIntoRect(context, cornerRect.location(), cornerRect);
3888 return;
3889 }
3890
3891#if PLATFORM(MAC)
3892 // Keep this in sync with ScrollAnimatorMac's effectiveAppearanceForScrollerImp:.
3893 LocalDefaultSystemAppearance localAppearance(useDarkAppearanceForScrollbars());
3894#endif
3895
3896 ScrollView::paintScrollCorner(context, cornerRect);
3897}
3898
3899void FrameView::paintScrollbar(GraphicsContext& context, Scrollbar& bar, const IntRect& rect)
3900{
3901 if (bar.isCustomScrollbar() && frame().isMainFrame()) {
3902 IntRect toFill = bar.frameRect();
3903 toFill.intersect(rect);
3904 context.fillRect(toFill, baseBackgroundColor());
3905 }
3906
3907 ScrollView::paintScrollbar(context, bar, rect);
3908}
3909
3910Color FrameView::documentBackgroundColor() const
3911{
3912 // <https://bugs.webkit.org/show_bug.cgi?id=59540> We blend the background color of
3913 // the document and the body against the base background color of the frame view.
3914 // Background images are unfortunately impractical to include.
3915
3916 // Return invalid Color objects whenever there is insufficient information.
3917 if (!frame().document())
3918 return Color();
3919
3920 auto* htmlElement = frame().document()->documentElement();
3921 auto* bodyElement = frame().document()->bodyOrFrameset();
3922
3923 // Start with invalid colors.
3924 Color htmlBackgroundColor;
3925 Color bodyBackgroundColor;
3926 if (htmlElement && htmlElement->renderer())
3927 htmlBackgroundColor = htmlElement->renderer()->style().visitedDependentColorWithColorFilter(CSSPropertyBackgroundColor);
3928 if (bodyElement && bodyElement->renderer())
3929 bodyBackgroundColor = bodyElement->renderer()->style().visitedDependentColorWithColorFilter(CSSPropertyBackgroundColor);
3930
3931 if (!bodyBackgroundColor.isValid()) {
3932 if (!htmlBackgroundColor.isValid())
3933 return Color();
3934 return baseBackgroundColor().blend(htmlBackgroundColor);
3935 }
3936
3937 if (!htmlBackgroundColor.isValid())
3938 return baseBackgroundColor().blend(bodyBackgroundColor);
3939
3940 // We take the aggregate of the base background color
3941 // the <html> background color, and the <body>
3942 // background color to find the document color. The
3943 // addition of the base background color is not
3944 // technically part of the document background, but it
3945 // otherwise poses problems when the aggregate is not
3946 // fully opaque.
3947 return baseBackgroundColor().blend(htmlBackgroundColor).blend(bodyBackgroundColor);
3948}
3949
3950bool FrameView::hasCustomScrollbars() const
3951{
3952 for (auto& widget : children()) {
3953 if (is<FrameView>(widget)) {
3954 if (downcast<FrameView>(widget.get()).hasCustomScrollbars())
3955 return true;
3956 } else if (is<Scrollbar>(widget)) {
3957 if (downcast<Scrollbar>(widget.get()).isCustomScrollbar())
3958 return true;
3959 }
3960 }
3961 return false;
3962}
3963
3964FrameView* FrameView::parentFrameView() const
3965{
3966 if (!parent())
3967 return nullptr;
3968 auto* parentFrame = frame().tree().parent();
3969 if (!parentFrame)
3970 return nullptr;
3971 return parentFrame->view();
3972}
3973
3974bool FrameView::isInChildFrameWithFrameFlattening() const
3975{
3976 if (!frameFlatteningEnabled())
3977 return false;
3978
3979 if (!parent())
3980 return false;
3981
3982 HTMLFrameOwnerElement* ownerElement = frame().ownerElement();
3983 if (!ownerElement)
3984 return false;
3985
3986 if (!ownerElement->renderWidget())
3987 return false;
3988
3989 // Frame flattening applies when the owner element is either in a frameset or
3990 // an iframe with flattening parameters.
3991 if (is<HTMLIFrameElement>(*ownerElement))
3992 return downcast<RenderIFrame>(*ownerElement->renderWidget()).flattenFrame();
3993
3994 if (is<HTMLFrameElement>(*ownerElement))
3995 return true;
3996
3997 return false;
3998}
3999
4000void FrameView::updateControlTints()
4001{
4002 // This is called when control tints are changed from aqua/graphite to clear and vice versa.
4003 // We do a "fake" paint, and when the theme gets a paint call, it can then do an invalidate.
4004 // This is only done if the theme supports control tinting. It's up to the theme and platform
4005 // to define when controls get the tint and to call this function when that changes.
4006
4007 // Optimize the common case where we bring a window to the front while it's still empty.
4008 if (frame().document()->url().isEmpty())
4009 return;
4010
4011 // As noted above, this is a "fake" paint, so we should pause counting relevant repainted objects.
4012 Page* page = frame().page();
4013 bool isCurrentlyCountingRelevantRepaintedObject = false;
4014 if (page) {
4015 isCurrentlyCountingRelevantRepaintedObject = page->isCountingRelevantRepaintedObjects();
4016 page->setIsCountingRelevantRepaintedObjects(false);
4017 }
4018
4019 RenderView* renderView = this->renderView();
4020 if ((renderView && renderView->theme().supportsControlTints()) || hasCustomScrollbars())
4021 invalidateControlTints();
4022
4023 if (page)
4024 page->setIsCountingRelevantRepaintedObjects(isCurrentlyCountingRelevantRepaintedObject);
4025}
4026
4027void FrameView::traverseForPaintInvalidation(GraphicsContext::PaintInvalidationReasons paintInvalidationReasons)
4028{
4029 if (needsLayout())
4030 layoutContext().layout();
4031
4032 GraphicsContext context(paintInvalidationReasons);
4033 if (platformWidget()) {
4034 // FIXME: consult paintsEntireContents().
4035 paintContents(context, visibleContentRect(LegacyIOSDocumentVisibleRect));
4036 } else
4037 paint(context, frameRect());
4038}
4039
4040bool FrameView::wasScrolledByUser() const
4041{
4042 return m_wasScrolledByUser;
4043}
4044
4045void FrameView::setWasScrolledByUser(bool wasScrolledByUser)
4046{
4047 LOG(Scrolling, "FrameView::setWasScrolledByUser at %d", wasScrolledByUser);
4048
4049 m_shouldScrollToFocusedElement = false;
4050 m_delayedScrollToFocusedElementTimer.stop();
4051 if (currentScrollType() == ScrollType::Programmatic)
4052 return;
4053 m_maintainScrollPositionAnchor = nullptr;
4054 if (m_wasScrolledByUser == wasScrolledByUser)
4055 return;
4056 m_wasScrolledByUser = wasScrolledByUser;
4057 if (frame().isMainFrame())
4058 updateLayerFlushThrottling();
4059 adjustTiledBackingCoverage();
4060}
4061
4062void FrameView::willPaintContents(GraphicsContext& context, const IntRect&, PaintingState& paintingState)
4063{
4064 Document* document = frame().document();
4065
4066 if (!context.paintingDisabled())
4067 InspectorInstrumentation::willPaint(*renderView());
4068
4069 paintingState.isTopLevelPainter = !sCurrentPaintTimeStamp;
4070
4071 if (paintingState.isTopLevelPainter)
4072 sCurrentPaintTimeStamp = MonotonicTime::now();
4073
4074 paintingState.paintBehavior = m_paintBehavior;
4075
4076 if (FrameView* parentView = parentFrameView()) {
4077 if (parentView->paintBehavior() & PaintBehavior::FlattenCompositingLayers)
4078 m_paintBehavior.add(PaintBehavior::FlattenCompositingLayers);
4079
4080 if (parentView->paintBehavior() & PaintBehavior::Snapshotting)
4081 m_paintBehavior.add(PaintBehavior::Snapshotting);
4082
4083 if (parentView->paintBehavior() & PaintBehavior::TileFirstPaint)
4084 m_paintBehavior.add(PaintBehavior::TileFirstPaint);
4085 }
4086
4087 if (document->printing()) {
4088 m_paintBehavior.add(PaintBehavior::FlattenCompositingLayers);
4089 m_paintBehavior.add(PaintBehavior::Snapshotting);
4090 }
4091
4092 paintingState.isFlatteningPaintOfRootFrame = (m_paintBehavior & PaintBehavior::FlattenCompositingLayers) && !frame().ownerElement();
4093 if (paintingState.isFlatteningPaintOfRootFrame)
4094 notifyWidgetsInAllFrames(WillPaintFlattened);
4095
4096 ASSERT(!m_isPainting);
4097 m_isPainting = true;
4098}
4099
4100void FrameView::didPaintContents(GraphicsContext& context, const IntRect& dirtyRect, PaintingState& paintingState)
4101{
4102 m_isPainting = false;
4103
4104 if (paintingState.isFlatteningPaintOfRootFrame)
4105 notifyWidgetsInAllFrames(DidPaintFlattened);
4106
4107 m_paintBehavior = paintingState.paintBehavior;
4108 m_lastPaintTime = MonotonicTime::now();
4109
4110 // Regions may have changed as a result of the visibility/z-index of element changing.
4111 frame().document()->updateZOrderDependentRegions();
4112
4113 if (paintingState.isTopLevelPainter)
4114 sCurrentPaintTimeStamp = MonotonicTime();
4115
4116 if (!context.paintingDisabled()) {
4117 InspectorInstrumentation::didPaint(*renderView(), dirtyRect);
4118 // FIXME: should probably not fire milestones for snapshot painting. https://bugs.webkit.org/show_bug.cgi?id=117623
4119 firePaintRelatedMilestonesIfNeeded();
4120 }
4121}
4122
4123void FrameView::paintContents(GraphicsContext& context, const IntRect& dirtyRect, SecurityOriginPaintPolicy securityOriginPaintPolicy)
4124{
4125#ifndef NDEBUG
4126 bool fillWithWarningColor;
4127 if (frame().document()->printing())
4128 fillWithWarningColor = false; // Printing, don't fill with red (can't remember why).
4129 else if (frame().ownerElement())
4130 fillWithWarningColor = false; // Subframe, don't fill with red.
4131 else if (isTransparent())
4132 fillWithWarningColor = false; // Transparent, don't fill with red.
4133 else if (m_paintBehavior & PaintBehavior::SelectionOnly)
4134 fillWithWarningColor = false; // Selections are transparent, don't fill with red.
4135 else if (m_nodeToDraw)
4136 fillWithWarningColor = false; // Element images are transparent, don't fill with red.
4137 else
4138 fillWithWarningColor = true;
4139
4140 if (fillWithWarningColor)
4141 context.fillRect(dirtyRect, Color(255, 64, 255));
4142#endif
4143
4144 RenderView* renderView = this->renderView();
4145 if (!renderView) {
4146 LOG_ERROR("called FrameView::paint with nil renderer");
4147 return;
4148 }
4149
4150 if (!layoutContext().inPaintableState())
4151 return;
4152
4153 ASSERT(!needsLayout());
4154 if (needsLayout()) {
4155 RELEASE_LOG_IF_ALLOWED("FrameView::paintContents() - not painting because render tree needs layout (is main frame %d)", frame().isMainFrame());
4156 return;
4157 }
4158
4159 PaintingState paintingState;
4160 willPaintContents(context, dirtyRect, paintingState);
4161
4162 // m_nodeToDraw is used to draw only one element (and its descendants)
4163 RenderObject* renderer = m_nodeToDraw ? m_nodeToDraw->renderer() : nullptr;
4164 RenderLayer* rootLayer = renderView->layer();
4165
4166#ifndef NDEBUG
4167 RenderElement::SetLayoutNeededForbiddenScope forbidSetNeedsLayout(&rootLayer->renderer());
4168#endif
4169
4170 // To work around http://webkit.org/b/135106, ensure that the paint root isn't an inline with culled line boxes.
4171 // FIXME: This can cause additional content to be included in the snapshot, so remove this once that bug is fixed.
4172 while (is<RenderInline>(renderer) && !downcast<RenderInline>(*renderer).firstLineBox())
4173 renderer = renderer->parent();
4174
4175 rootLayer->paint(context, dirtyRect, LayoutSize(), m_paintBehavior, renderer, { }, securityOriginPaintPolicy == SecurityOriginPaintPolicy::AnyOrigin ? RenderLayer::SecurityOriginPaintPolicy::AnyOrigin : RenderLayer::SecurityOriginPaintPolicy::AccessibleOriginOnly);
4176 if (rootLayer->containsDirtyOverlayScrollbars())
4177 rootLayer->paintOverlayScrollbars(context, dirtyRect, m_paintBehavior, renderer);
4178
4179 didPaintContents(context, dirtyRect, paintingState);
4180}
4181
4182void FrameView::setPaintBehavior(OptionSet<PaintBehavior> behavior)
4183{
4184 m_paintBehavior = behavior;
4185}
4186
4187OptionSet<PaintBehavior> FrameView::paintBehavior() const
4188{
4189 return m_paintBehavior;
4190}
4191
4192bool FrameView::isPainting() const
4193{
4194 return m_isPainting;
4195}
4196
4197// FIXME: change this to use the subtreePaint terminology.
4198void FrameView::setNodeToDraw(Node* node)
4199{
4200 m_nodeToDraw = node;
4201}
4202
4203void FrameView::paintContentsForSnapshot(GraphicsContext& context, const IntRect& imageRect, SelectionInSnapshot shouldPaintSelection, CoordinateSpaceForSnapshot coordinateSpace)
4204{
4205 updateLayoutAndStyleIfNeededRecursive();
4206
4207 // Cache paint behavior and set a new behavior appropriate for snapshots.
4208 auto oldBehavior = paintBehavior();
4209 setPaintBehavior(oldBehavior | PaintBehavior::FlattenCompositingLayers | PaintBehavior::Snapshotting);
4210
4211 // If the snapshot should exclude selection, then we'll clear the current selection
4212 // in the render tree only. This will allow us to restore the selection from the DOM
4213 // after we paint the snapshot.
4214 if (shouldPaintSelection == ExcludeSelection) {
4215 for (auto* frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) {
4216 if (auto* renderView = frame->contentRenderer())
4217 renderView->selection().clear();
4218 }
4219 }
4220
4221 if (coordinateSpace == DocumentCoordinates)
4222 paintContents(context, imageRect);
4223 else {
4224 // A snapshot in ViewCoordinates will include a scrollbar, and the snapshot will contain
4225 // whatever content the document is currently scrolled to.
4226 paint(context, imageRect);
4227 }
4228
4229 // Restore selection.
4230 if (shouldPaintSelection == ExcludeSelection) {
4231 for (auto* frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr()))
4232 frame->selection().updateAppearance();
4233 }
4234
4235 // Restore cached paint behavior.
4236 setPaintBehavior(oldBehavior);
4237}
4238
4239void FrameView::paintOverhangAreas(GraphicsContext& context, const IntRect& horizontalOverhangArea, const IntRect& verticalOverhangArea, const IntRect& dirtyRect)
4240{
4241 if (context.paintingDisabled())
4242 return;
4243
4244 if (frame().document()->printing())
4245 return;
4246
4247 ScrollView::paintOverhangAreas(context, horizontalOverhangArea, verticalOverhangArea, dirtyRect);
4248}
4249
4250void FrameView::updateLayoutAndStyleIfNeededRecursive()
4251{
4252 // Style updating, render tree creation, and layout needs to be done multiple times
4253 // for more than one reason. But one reason is that when an <object> element determines
4254 // what it needs to load a subframe, a second pass is needed. That requires update
4255 // passes equal to the number of levels of DOM nesting. That is why this number is large.
4256 // There are test cases where we have roughly 10 levels of DOM nesting, so this needs to
4257 // be greater than that. We have a limit to avoid the possibility of an infinite loop.
4258 // Typical calls will run the loop 2 times (once to do work, once to detect no further work
4259 // is needed).
4260 // FIXME: We should find an approach that does not require a loop at all.
4261 const unsigned maxUpdatePasses = 25;
4262
4263 // Style updates can trigger script, which can cause this FrameView to be destroyed.
4264 Ref<FrameView> protectedThis(*this);
4265
4266 AnimationUpdateBlock animationUpdateBlock(&frame().animation());
4267
4268 using DescendantsDeque = Deque<Ref<FrameView>, 16>;
4269 auto nextRenderedDescendant = [this] (DescendantsDeque& descendantsDeque) -> RefPtr<FrameView> {
4270 if (descendantsDeque.isEmpty())
4271 descendantsDeque.append(*this);
4272 else {
4273 // Append renderered children after processing the parent, in case the processing
4274 // affects the set of rendered children.
4275 auto previousView = descendantsDeque.takeFirst();
4276 for (auto* frame = previousView->frame().tree().firstRenderedChild(); frame; frame = frame->tree().nextRenderedSibling()) {
4277 if (auto* view = frame->view())
4278 descendantsDeque.append(*view);
4279 }
4280 if (descendantsDeque.isEmpty())
4281 return nullptr;
4282 }
4283 return descendantsDeque.first().ptr();
4284 };
4285
4286 for (unsigned i = 0; i < maxUpdatePasses; ++i) {
4287 bool didWork = false;
4288 DescendantsDeque deque;
4289 while (auto view = nextRenderedDescendant(deque)) {
4290 if (view->frame().document()->updateStyleIfNeeded())
4291 didWork = true;
4292 if (view->needsLayout()) {
4293 view->layoutContext().layout();
4294 didWork = true;
4295 }
4296 }
4297 if (!didWork)
4298 break;
4299 }
4300
4301#if !ASSERT_DISABLED
4302 auto needsStyleRecalc = [&] {
4303 DescendantsDeque deque;
4304 while (auto view = nextRenderedDescendant(deque)) {
4305 auto* document = view->frame().document();
4306 if (document && document->childNeedsStyleRecalc())
4307 return true;
4308 }
4309 return false;
4310 };
4311
4312 auto needsLayout = [&] {
4313 DescendantsDeque deque;
4314 while (auto view = nextRenderedDescendant(deque)) {
4315 if (view->needsLayout())
4316 return true;
4317 }
4318 return false;
4319 };
4320#endif
4321
4322 ASSERT(!needsStyleRecalc());
4323 ASSERT(!needsLayout());
4324}
4325
4326void FrameView::incrementVisuallyNonEmptyCharacterCount(const String& inlineText)
4327{
4328 if (m_visuallyNonEmptyCharacterCount > visualCharacterThreshold && m_hasReachedSignificantRenderedTextThreshold)
4329 return;
4330
4331 auto nonWhitespaceLength = [](auto& inlineText) {
4332 auto length = inlineText.length();
4333 for (unsigned i = 0; i < inlineText.length(); ++i) {
4334 if (isNotHTMLSpace(inlineText[i]))
4335 continue;
4336 --length;
4337 }
4338 return length;
4339 };
4340 m_visuallyNonEmptyCharacterCount += nonWhitespaceLength(inlineText);
4341 ++m_textRendererCountForVisuallyNonEmptyCharacters;
4342}
4343
4344static bool elementOverflowRectIsLargerThanThreshold(const Element& element)
4345{
4346 // Require the document to grow a bit.
4347 // Using a value of 48 allows the header on Google's search page to render immediately before search results populate later.
4348 static const int documentHeightThreshold = 48;
4349 if (auto* elementRenderBox = element.renderBox())
4350 return snappedIntRect(elementRenderBox->layoutOverflowRect()).height() >= documentHeightThreshold;
4351
4352 return false;
4353}
4354
4355void FrameView::updateHasReachedSignificantRenderedTextThreshold()
4356{
4357 if (m_hasReachedSignificantRenderedTextThreshold)
4358 return;
4359
4360 auto* page = frame().page();
4361 if (!page || !page->requestedLayoutMilestones().contains(DidRenderSignificantAmountOfText))
4362 return;
4363
4364 auto* document = frame().document();
4365 if (!document)
4366 return;
4367
4368 document->updateMainArticleElementAfterLayout();
4369 auto hasMainArticleElement = document->hasMainArticleElement();
4370 auto characterThreshold = hasMainArticleElement ? mainArticleSignificantRenderedTextCharacterThreshold : defaultSignificantRenderedTextCharacterThreshold;
4371 if (m_visuallyNonEmptyCharacterCount < characterThreshold)
4372 return;
4373
4374 auto meanLength = hasMainArticleElement ? mainArticleSignificantRenderedTextMeanLength : defaultSignificantRenderedTextMeanLength;
4375 if (!m_textRendererCountForVisuallyNonEmptyCharacters || m_visuallyNonEmptyCharacterCount / static_cast<float>(m_textRendererCountForVisuallyNonEmptyCharacters) < meanLength)
4376 return;
4377
4378 m_hasReachedSignificantRenderedTextThreshold = true;
4379}
4380
4381bool FrameView::qualifiesAsSignificantRenderedText() const
4382{
4383 ASSERT(!m_renderedSignificantAmountOfText);
4384 auto* document = frame().document();
4385 if (!document || document->styleScope().hasPendingSheetsBeforeBody())
4386 return false;
4387
4388 auto* documentElement = document->documentElement();
4389 if (!documentElement || !elementOverflowRectIsLargerThanThreshold(*documentElement))
4390 return false;
4391
4392 return m_hasReachedSignificantRenderedTextThreshold;
4393}
4394
4395bool FrameView::qualifiesAsVisuallyNonEmpty() const
4396{
4397 // No content yet.
4398 Element* documentElement = frame().document()->documentElement();
4399 if (!documentElement || !documentElement->renderer())
4400 return false;
4401
4402 // FIXME: We should also ignore renderers with non-final style.
4403 if (frame().document()->styleScope().hasPendingSheetsBeforeBody())
4404 return false;
4405
4406 auto finishedParsingMainDocument = frame().loader().stateMachine().committedFirstRealDocumentLoad() && (frame().document()->readyState() == Document::Interactive || frame().document()->readyState() == Document::Complete);
4407 // Ensure that we always fire visually non-empty milestone eventually.
4408 if (finishedParsingMainDocument && frame().loader().isComplete())
4409 return true;
4410
4411 auto isVisible = [](const Element* element) {
4412 if (!element || !element->renderer())
4413 return false;
4414 if (!element->renderer()->opacity())
4415 return false;
4416 return element->renderer()->style().visibility() == Visibility::Visible;
4417 };
4418
4419 if (!isVisible(documentElement))
4420 return false;
4421
4422 if (!isVisible(frame().document()->body()))
4423 return false;
4424
4425 if (!elementOverflowRectIsLargerThanThreshold(*documentElement))
4426 return false;
4427
4428 // The first few hundred characters rarely contain the interesting content of the page.
4429 if (m_visuallyNonEmptyCharacterCount > visualCharacterThreshold)
4430 return true;
4431
4432 // Use a threshold value to prevent very small amounts of visible content from triggering didFirstVisuallyNonEmptyLayout
4433 if (m_visuallyNonEmptyPixelCount > visualPixelThreshold)
4434 return true;
4435
4436 auto isMoreContentExpected = [&]() {
4437 ASSERT(finishedParsingMainDocument);
4438 // Pending css/font loading means we should wait a little longer. Classic non-async, non-defer scripts are all processed by now.
4439 auto* documentLoader = frame().loader().documentLoader();
4440 if (!documentLoader)
4441 return false;
4442
4443 auto& resourceLoader = documentLoader->cachedResourceLoader();
4444 if (!resourceLoader.requestCount())
4445 return false;
4446
4447 auto& resources = resourceLoader.allCachedResources();
4448 for (auto& resource : resources) {
4449 if (resource.value->isLoaded())
4450 continue;
4451 if (resource.value->type() == CachedResource::Type::CSSStyleSheet || resource.value->type() == CachedResource::Type::FontResource)
4452 return true;
4453 }
4454 return false;
4455 };
4456
4457 // Finished parsing the main document and we still don't yet have enough content. Check if we might be getting some more.
4458 if (finishedParsingMainDocument)
4459 return !isMoreContentExpected();
4460
4461 return false;
4462}
4463
4464bool FrameView::isViewForDocumentInFrame() const
4465{
4466 RenderView* renderView = this->renderView();
4467 if (!renderView)
4468 return false;
4469
4470 return &renderView->frameView() == this;
4471}
4472
4473void FrameView::enableAutoSizeMode(bool enable, const IntSize& viewSize)
4474{
4475 ASSERT(!enable || !viewSize.isEmpty());
4476 if (m_shouldAutoSize == enable && m_autoSizeConstraint == viewSize)
4477 return;
4478
4479 m_shouldAutoSize = enable;
4480 m_autoSizeConstraint = viewSize;
4481 m_autoSizeContentSize = contentsSize();
4482 m_didRunAutosize = false;
4483
4484 setNeedsLayoutAfterViewConfigurationChange();
4485 layoutContext().scheduleLayout();
4486 if (m_shouldAutoSize) {
4487 overrideViewportSizeForCSSViewportUnits({ m_autoSizeConstraint.width(), m_overrideViewportSize ? m_overrideViewportSize->height : WTF::nullopt });
4488 return;
4489 }
4490
4491 clearViewportSizeOverrideForCSSViewportUnits();
4492 // Since autosize mode forces the scrollbar mode, change them to being auto.
4493 setVerticalScrollbarLock(false);
4494 setHorizontalScrollbarLock(false);
4495 setScrollbarModes(ScrollbarAuto, ScrollbarAuto);
4496}
4497
4498void FrameView::forceLayout(bool allowSubtreeLayout)
4499{
4500 if (!allowSubtreeLayout && layoutContext().subtreeLayoutRoot())
4501 layoutContext().convertSubtreeLayoutToFullLayout();
4502 layoutContext().layout();
4503}
4504
4505void FrameView::forceLayoutForPagination(const FloatSize& pageSize, const FloatSize& originalPageSize, float maximumShrinkFactor, AdjustViewSizeOrNot shouldAdjustViewSize)
4506{
4507 if (!renderView())
4508 return;
4509
4510 Ref<FrameView> protectedThis(*this);
4511 auto& renderView = *this->renderView();
4512
4513 // Dumping externalRepresentation(frame().renderer()).ascii() is a good trick to see
4514 // the state of things before and after the layout
4515 float pageLogicalWidth = renderView.style().isHorizontalWritingMode() ? pageSize.width() : pageSize.height();
4516 float pageLogicalHeight = renderView.style().isHorizontalWritingMode() ? pageSize.height() : pageSize.width();
4517
4518 renderView.setPageLogicalSize({ floor(pageLogicalWidth), floor(pageLogicalHeight) });
4519 renderView.setNeedsLayoutAndPrefWidthsRecalc();
4520 forceLayout();
4521 if (hasOneRef())
4522 return;
4523
4524 // If we don't fit in the given page width, we'll lay out again. If we don't fit in the
4525 // page width when shrunk, we will lay out at maximum shrink and clip extra content.
4526 // FIXME: We are assuming a shrink-to-fit printing implementation. A cropping
4527 // implementation should not do this!
4528 bool horizontalWritingMode = renderView.style().isHorizontalWritingMode();
4529 const LayoutRect& documentRect = renderView.documentRect();
4530 LayoutUnit docLogicalWidth = horizontalWritingMode ? documentRect.width() : documentRect.height();
4531 if (docLogicalWidth > pageLogicalWidth) {
4532 int expectedPageWidth = std::min<float>(documentRect.width(), pageSize.width() * maximumShrinkFactor);
4533 int expectedPageHeight = std::min<float>(documentRect.height(), pageSize.height() * maximumShrinkFactor);
4534 FloatSize maxPageSize = frame().resizePageRectsKeepingRatio(FloatSize(originalPageSize.width(), originalPageSize.height()), FloatSize(expectedPageWidth, expectedPageHeight));
4535 pageLogicalWidth = horizontalWritingMode ? maxPageSize.width() : maxPageSize.height();
4536 pageLogicalHeight = horizontalWritingMode ? maxPageSize.height() : maxPageSize.width();
4537
4538 renderView.setPageLogicalSize({ floor(pageLogicalWidth), floor(pageLogicalHeight) });
4539 renderView.setNeedsLayoutAndPrefWidthsRecalc();
4540 forceLayout();
4541 if (hasOneRef())
4542 return;
4543
4544 const LayoutRect& updatedDocumentRect = renderView.documentRect();
4545 LayoutUnit docLogicalHeight = horizontalWritingMode ? updatedDocumentRect.height() : updatedDocumentRect.width();
4546 LayoutUnit docLogicalTop = horizontalWritingMode ? updatedDocumentRect.y() : updatedDocumentRect.x();
4547 LayoutUnit docLogicalRight = horizontalWritingMode ? updatedDocumentRect.maxX() : updatedDocumentRect.maxY();
4548 LayoutUnit clippedLogicalLeft;
4549 if (!renderView.style().isLeftToRightDirection())
4550 clippedLogicalLeft = docLogicalRight - pageLogicalWidth;
4551 LayoutRect overflow(clippedLogicalLeft, docLogicalTop, pageLogicalWidth, docLogicalHeight);
4552
4553 if (!horizontalWritingMode)
4554 overflow = overflow.transposedRect();
4555 renderView.clearLayoutOverflow();
4556 renderView.addLayoutOverflow(overflow); // This is how we clip in case we overflow again.
4557 }
4558
4559 if (shouldAdjustViewSize)
4560 adjustViewSize();
4561}
4562
4563void FrameView::adjustPageHeightDeprecated(float *newBottom, float oldTop, float oldBottom, float /*bottomLimit*/)
4564{
4565 RenderView* renderView = this->renderView();
4566 if (!renderView) {
4567 *newBottom = oldBottom;
4568 return;
4569
4570 }
4571 // Use a context with painting disabled.
4572 GraphicsContext context(GraphicsContext::PaintInvalidationReasons::None);
4573 renderView->setTruncatedAt(static_cast<int>(floorf(oldBottom)));
4574 IntRect dirtyRect(0, static_cast<int>(floorf(oldTop)), renderView->layoutOverflowRect().maxX(), static_cast<int>(ceilf(oldBottom - oldTop)));
4575 renderView->setPrintRect(dirtyRect);
4576 renderView->layer()->paint(context, dirtyRect);
4577 *newBottom = renderView->bestTruncatedAt();
4578 if (!*newBottom)
4579 *newBottom = oldBottom;
4580 renderView->setPrintRect(IntRect());
4581}
4582
4583IntRect FrameView::convertFromRendererToContainingView(const RenderElement* renderer, const IntRect& rendererRect) const
4584{
4585 IntRect rect = snappedIntRect(enclosingLayoutRect(renderer->localToAbsoluteQuad(FloatRect(rendererRect)).boundingBox()));
4586
4587 return contentsToView(rect);
4588}
4589
4590IntRect FrameView::convertFromContainingViewToRenderer(const RenderElement* renderer, const IntRect& viewRect) const
4591{
4592 IntRect rect = viewToContents(viewRect);
4593
4594 // FIXME: we don't have a way to map an absolute rect down to a local quad, so just
4595 // move the rect for now.
4596 rect.setLocation(roundedIntPoint(renderer->absoluteToLocal(rect.location(), UseTransforms)));
4597 return rect;
4598}
4599
4600FloatRect FrameView::convertFromContainingViewToRenderer(const RenderElement* renderer, const FloatRect& viewRect) const
4601{
4602 FloatRect rect = viewToContents(viewRect);
4603
4604 return (renderer->absoluteToLocalQuad(rect)).boundingBox();
4605}
4606
4607IntPoint FrameView::convertFromRendererToContainingView(const RenderElement* renderer, const IntPoint& rendererPoint) const
4608{
4609 IntPoint point = roundedIntPoint(renderer->localToAbsolute(rendererPoint, UseTransforms));
4610
4611 return contentsToView(point);
4612}
4613
4614IntPoint FrameView::convertFromContainingViewToRenderer(const RenderElement* renderer, const IntPoint& viewPoint) const
4615{
4616 IntPoint point = viewPoint;
4617
4618 // Convert from FrameView coords into page ("absolute") coordinates.
4619 if (!delegatesScrolling())
4620 point = viewToContents(point);
4621
4622 return roundedIntPoint(renderer->absoluteToLocal(point, UseTransforms));
4623}
4624
4625IntRect FrameView::convertToContainingView(const IntRect& localRect) const
4626{
4627 if (const ScrollView* parentScrollView = parent()) {
4628 if (is<FrameView>(*parentScrollView)) {
4629 const FrameView& parentView = downcast<FrameView>(*parentScrollView);
4630 // Get our renderer in the parent view
4631 RenderWidget* renderer = frame().ownerRenderer();
4632 if (!renderer)
4633 return localRect;
4634
4635 auto rect = localRect;
4636 rect.moveBy(roundedIntPoint(renderer->contentBoxLocation()));
4637 return parentView.convertFromRendererToContainingView(renderer, rect);
4638 }
4639
4640 return Widget::convertToContainingView(localRect);
4641 }
4642
4643 return localRect;
4644}
4645
4646IntRect FrameView::convertFromContainingView(const IntRect& parentRect) const
4647{
4648 if (const ScrollView* parentScrollView = parent()) {
4649 if (is<FrameView>(*parentScrollView)) {
4650 const FrameView& parentView = downcast<FrameView>(*parentScrollView);
4651
4652 // Get our renderer in the parent view
4653 RenderWidget* renderer = frame().ownerRenderer();
4654 if (!renderer)
4655 return parentRect;
4656
4657 auto rect = parentView.convertFromContainingViewToRenderer(renderer, parentRect);
4658 rect.moveBy(-roundedIntPoint(renderer->contentBoxLocation()));
4659 return rect;
4660 }
4661
4662 return Widget::convertFromContainingView(parentRect);
4663 }
4664
4665 return parentRect;
4666}
4667
4668FloatRect FrameView::convertFromContainingView(const FloatRect& parentRect) const
4669{
4670 if (const ScrollView* parentScrollView = parent()) {
4671 if (is<FrameView>(*parentScrollView)) {
4672 const FrameView& parentView = downcast<FrameView>(*parentScrollView);
4673
4674 // Get our renderer in the parent view
4675 RenderWidget* renderer = frame().ownerRenderer();
4676 if (!renderer)
4677 return parentRect;
4678
4679 auto rect = parentView.convertFromContainingViewToRenderer(renderer, parentRect);
4680 rect.moveBy(-renderer->contentBoxLocation());
4681 return rect;
4682 }
4683
4684 return Widget::convertFromContainingView(parentRect);
4685 }
4686
4687 return parentRect;
4688}
4689
4690IntPoint FrameView::convertToContainingView(const IntPoint& localPoint) const
4691{
4692 if (const ScrollView* parentScrollView = parent()) {
4693 if (is<FrameView>(*parentScrollView)) {
4694 const FrameView& parentView = downcast<FrameView>(*parentScrollView);
4695
4696 // Get our renderer in the parent view
4697 RenderWidget* renderer = frame().ownerRenderer();
4698 if (!renderer)
4699 return localPoint;
4700
4701 auto point = localPoint;
4702 point.moveBy(roundedIntPoint(renderer->contentBoxLocation()));
4703 return parentView.convertFromRendererToContainingView(renderer, point);
4704 }
4705
4706 return Widget::convertToContainingView(localPoint);
4707 }
4708
4709 return localPoint;
4710}
4711
4712IntPoint FrameView::convertFromContainingView(const IntPoint& parentPoint) const
4713{
4714 if (const ScrollView* parentScrollView = parent()) {
4715 if (is<FrameView>(*parentScrollView)) {
4716 const FrameView& parentView = downcast<FrameView>(*parentScrollView);
4717
4718 // Get our renderer in the parent view
4719 RenderWidget* renderer = frame().ownerRenderer();
4720 if (!renderer)
4721 return parentPoint;
4722
4723 auto point = parentView.convertFromContainingViewToRenderer(renderer, parentPoint);
4724 point.moveBy(-roundedIntPoint(renderer->contentBoxLocation()));
4725 return point;
4726 }
4727
4728 return Widget::convertFromContainingView(parentPoint);
4729 }
4730
4731 return parentPoint;
4732}
4733
4734float FrameView::documentToAbsoluteScaleFactor(Optional<float> effectiveZoom) const
4735{
4736 // If effectiveZoom is passed, it already factors in pageZoomFactor().
4737 return effectiveZoom.valueOr(frame().pageZoomFactor()) * frame().frameScaleFactor();
4738}
4739
4740float FrameView::absoluteToDocumentScaleFactor(Optional<float> effectiveZoom) const
4741{
4742 // If effectiveZoom is passed, it already factors in pageZoomFactor().
4743 return 1 / documentToAbsoluteScaleFactor(effectiveZoom);
4744}
4745
4746FloatRect FrameView::absoluteToDocumentRect(FloatRect rect, Optional<float> effectiveZoom) const
4747{
4748 rect.scale(absoluteToDocumentScaleFactor(effectiveZoom));
4749 return rect;
4750}
4751
4752FloatPoint FrameView::absoluteToDocumentPoint(FloatPoint p, Optional<float> effectiveZoom) const
4753{
4754 return p.scaled(absoluteToDocumentScaleFactor(effectiveZoom));
4755}
4756
4757FloatRect FrameView::absoluteToClientRect(FloatRect rect, Optional<float> effectiveZoom) const
4758{
4759 return documentToClientRect(absoluteToDocumentRect(rect, effectiveZoom));
4760}
4761
4762FloatSize FrameView::documentToClientOffset() const
4763{
4764 FloatSize clientOrigin = -toFloatSize(visibleContentRect().location());
4765
4766 // Layout and visual viewports are affected by page zoom, so we need to factor that out.
4767 return clientOrigin.scaled(1 / (frame().pageZoomFactor() * frame().frameScaleFactor()));
4768}
4769
4770FloatRect FrameView::documentToClientRect(FloatRect rect) const
4771{
4772 rect.move(documentToClientOffset());
4773 return rect;
4774}
4775
4776FloatPoint FrameView::documentToClientPoint(FloatPoint p) const
4777{
4778 p.move(documentToClientOffset());
4779 return p;
4780}
4781
4782FloatRect FrameView::clientToDocumentRect(FloatRect rect) const
4783{
4784 rect.move(-documentToClientOffset());
4785 return rect;
4786}
4787
4788FloatPoint FrameView::clientToDocumentPoint(FloatPoint point) const
4789{
4790 point.move(-documentToClientOffset());
4791 return point;
4792}
4793
4794FloatPoint FrameView::absoluteToLayoutViewportPoint(FloatPoint p) const
4795{
4796 ASSERT(frame().settings().visualViewportEnabled());
4797 p.scale(1 / frame().frameScaleFactor());
4798 p.moveBy(-layoutViewportRect().location());
4799 return p;
4800}
4801
4802FloatPoint FrameView::layoutViewportToAbsolutePoint(FloatPoint p) const
4803{
4804 ASSERT(frame().settings().visualViewportEnabled());
4805 p.moveBy(layoutViewportRect().location());
4806 return p.scaled(frame().frameScaleFactor());
4807}
4808
4809FloatRect FrameView::layoutViewportToAbsoluteRect(FloatRect rect) const
4810{
4811 ASSERT(frame().settings().visualViewportEnabled());
4812 rect.moveBy(layoutViewportRect().location());
4813 rect.scale(frame().frameScaleFactor());
4814 return rect;
4815}
4816
4817FloatRect FrameView::absoluteToLayoutViewportRect(FloatRect rect) const
4818{
4819 ASSERT(frame().settings().visualViewportEnabled());
4820 rect.scale(1 / frame().frameScaleFactor());
4821 rect.moveBy(-layoutViewportRect().location());
4822 return rect;
4823}
4824
4825FloatRect FrameView::clientToLayoutViewportRect(FloatRect rect) const
4826{
4827 ASSERT(frame().settings().visualViewportEnabled());
4828 rect.scale(frame().pageZoomFactor());
4829 return rect;
4830}
4831
4832FloatPoint FrameView::clientToLayoutViewportPoint(FloatPoint p) const
4833{
4834 ASSERT(frame().settings().visualViewportEnabled());
4835 return p.scaled(frame().pageZoomFactor());
4836}
4837
4838void FrameView::setTracksRepaints(bool trackRepaints)
4839{
4840 if (trackRepaints == m_isTrackingRepaints)
4841 return;
4842
4843 // Force layout to flush out any pending repaints.
4844 if (trackRepaints) {
4845 if (frame().document())
4846 frame().document()->updateLayout();
4847 }
4848
4849 for (Frame* frame = &m_frame->tree().top(); frame; frame = frame->tree().traverseNext()) {
4850 if (RenderView* renderView = frame->contentRenderer())
4851 renderView->compositor().setTracksRepaints(trackRepaints);
4852 }
4853
4854 resetTrackedRepaints();
4855 m_isTrackingRepaints = trackRepaints;
4856}
4857
4858void FrameView::resetTrackedRepaints()
4859{
4860 m_trackedRepaintRects.clear();
4861 if (RenderView* renderView = this->renderView())
4862 renderView->compositor().resetTrackedRepaintRects();
4863}
4864
4865String FrameView::trackedRepaintRectsAsText() const
4866{
4867 if (frame().document())
4868 frame().document()->updateLayout();
4869
4870 TextStream ts;
4871 if (!m_trackedRepaintRects.isEmpty()) {
4872 ts << "(repaint rects\n";
4873 for (auto& rect : m_trackedRepaintRects)
4874 ts << " (rect " << LayoutUnit(rect.x()) << " " << LayoutUnit(rect.y()) << " " << LayoutUnit(rect.width()) << " " << LayoutUnit(rect.height()) << ")\n";
4875 ts << ")\n";
4876 }
4877 return ts.release();
4878}
4879
4880bool FrameView::addScrollableArea(ScrollableArea* scrollableArea)
4881{
4882 if (!m_scrollableAreas)
4883 m_scrollableAreas = std::make_unique<ScrollableAreaSet>();
4884
4885 if (m_scrollableAreas->add(scrollableArea).isNewEntry) {
4886 scrollableAreaSetChanged();
4887 return true;
4888 }
4889
4890 return false;
4891}
4892
4893bool FrameView::removeScrollableArea(ScrollableArea* scrollableArea)
4894{
4895 if (m_scrollableAreas && m_scrollableAreas->remove(scrollableArea)) {
4896 scrollableAreaSetChanged();
4897 return true;
4898 }
4899 return false;
4900}
4901
4902bool FrameView::containsScrollableArea(ScrollableArea* scrollableArea) const
4903{
4904 return m_scrollableAreas && m_scrollableAreas->contains(scrollableArea);
4905}
4906
4907void FrameView::scrollableAreaSetChanged()
4908{
4909 if (auto* page = frame().page()) {
4910 if (auto* scrollingCoordinator = page->scrollingCoordinator())
4911 scrollingCoordinator->frameViewEventTrackingRegionsChanged(*this);
4912 }
4913}
4914
4915void FrameView::sendScrollEvent()
4916{
4917 frame().eventHandler().sendScrollEvent();
4918 frame().eventHandler().dispatchFakeMouseMoveEventSoon();
4919}
4920
4921void FrameView::addChild(Widget& widget)
4922{
4923 if (is<FrameView>(widget)) {
4924 auto& childFrameView = downcast<FrameView>(widget);
4925 if (childFrameView.isScrollable())
4926 addScrollableArea(&childFrameView);
4927 }
4928
4929 ScrollView::addChild(widget);
4930}
4931
4932void FrameView::removeChild(Widget& widget)
4933{
4934 if (is<FrameView>(widget))
4935 removeScrollableArea(&downcast<FrameView>(widget));
4936
4937 ScrollView::removeChild(widget);
4938}
4939
4940bool FrameView::wheelEvent(const PlatformWheelEvent& wheelEvent)
4941{
4942 // Note that to allow for rubber-band over-scroll behavior, even non-scrollable views
4943 // should handle wheel events.
4944#if !ENABLE(RUBBER_BANDING)
4945 if (!isScrollable())
4946 return false;
4947#endif
4948
4949 if (delegatesScrolling()) {
4950 ScrollPosition oldPosition = scrollPosition();
4951 ScrollPosition newPosition = oldPosition - IntSize(wheelEvent.deltaX(), wheelEvent.deltaY());
4952 if (oldPosition != newPosition) {
4953 ScrollView::scrollTo(newPosition);
4954 scrollPositionChanged(oldPosition, scrollPosition());
4955 didChangeScrollOffset();
4956 }
4957 return true;
4958 }
4959
4960 // We don't allow mouse wheeling to happen in a ScrollView that has had its scrollbars explicitly disabled.
4961 if (!canHaveScrollbars())
4962 return false;
4963
4964 if (platformWidget())
4965 return false;
4966
4967#if ENABLE(ASYNC_SCROLLING)
4968 if (Page* page = frame().page()) {
4969 if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) {
4970 if (scrollingCoordinator->coordinatesScrollingForFrameView(*this))
4971 return scrollingCoordinator->handleWheelEvent(*this, wheelEvent) != ScrollingEventResult::DidNotHandleEvent;
4972 }
4973 }
4974#endif
4975
4976 return ScrollableArea::handleWheelEvent(wheelEvent);
4977}
4978
4979
4980bool FrameView::isVerticalDocument() const
4981{
4982 RenderView* renderView = this->renderView();
4983 if (!renderView)
4984 return true;
4985
4986 return renderView->style().isHorizontalWritingMode();
4987}
4988
4989bool FrameView::isFlippedDocument() const
4990{
4991 RenderView* renderView = this->renderView();
4992 if (!renderView)
4993 return false;
4994
4995 return renderView->style().isFlippedBlocksWritingMode();
4996}
4997
4998void FrameView::notifyWidgetsInAllFrames(WidgetNotification notification)
4999{
5000 for (auto* frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) {
5001 if (FrameView* view = frame->view())
5002 view->notifyWidgets(notification);
5003 }
5004}
5005
5006AXObjectCache* FrameView::axObjectCache() const
5007{
5008 if (frame().document())
5009 return frame().document()->existingAXObjectCache();
5010 return nullptr;
5011}
5012
5013#if PLATFORM(IOS_FAMILY)
5014bool FrameView::useCustomFixedPositionLayoutRect() const
5015{
5016 return !frame().settings().visualViewportEnabled() && m_useCustomFixedPositionLayoutRect;
5017}
5018
5019void FrameView::setCustomFixedPositionLayoutRect(const IntRect& rect)
5020{
5021 if (m_useCustomFixedPositionLayoutRect && m_customFixedPositionLayoutRect == rect)
5022 return;
5023 m_useCustomFixedPositionLayoutRect = true;
5024 m_customFixedPositionLayoutRect = rect;
5025 updateContentsSize();
5026}
5027
5028bool FrameView::updateFixedPositionLayoutRect()
5029{
5030 if (!m_useCustomFixedPositionLayoutRect)
5031 return false;
5032
5033 IntRect newRect;
5034 Page* page = frame().page();
5035 if (!page || !page->chrome().client().fetchCustomFixedPositionLayoutRect(newRect))
5036 return false;
5037
5038 if (newRect != m_customFixedPositionLayoutRect) {
5039 m_customFixedPositionLayoutRect = newRect;
5040 setViewportConstrainedObjectsNeedLayout();
5041 return true;
5042 }
5043 return false;
5044}
5045
5046void FrameView::setCustomSizeForResizeEvent(IntSize customSize)
5047{
5048 m_useCustomSizeForResizeEvent = true;
5049 m_customSizeForResizeEvent = customSize;
5050 sendResizeEventIfNeeded();
5051}
5052
5053void FrameView::setScrollVelocity(double horizontalVelocity, double verticalVelocity, double scaleChangeRate, MonotonicTime timestamp)
5054{
5055 if (TiledBacking* tiledBacking = this->tiledBacking())
5056 tiledBacking->setVelocity(VelocityData(horizontalVelocity, verticalVelocity, scaleChangeRate, timestamp));
5057}
5058#endif // PLATFORM(IOS_FAMILY)
5059
5060void FrameView::setScrollingPerformanceLoggingEnabled(bool flag)
5061{
5062 if (TiledBacking* tiledBacking = this->tiledBacking())
5063 tiledBacking->setScrollingPerformanceLoggingEnabled(flag);
5064}
5065
5066void FrameView::didAddScrollbar(Scrollbar* scrollbar, ScrollbarOrientation orientation)
5067{
5068 ScrollableArea::didAddScrollbar(scrollbar, orientation);
5069 Page* page = frame().page();
5070 if (page && page->expectsWheelEventTriggers())
5071 scrollAnimator().setWheelEventTestTrigger(page->testTrigger());
5072 if (AXObjectCache* cache = axObjectCache())
5073 cache->handleScrollbarUpdate(this);
5074}
5075
5076void FrameView::willRemoveScrollbar(Scrollbar* scrollbar, ScrollbarOrientation orientation)
5077{
5078 ScrollableArea::willRemoveScrollbar(scrollbar, orientation);
5079 if (AXObjectCache* cache = axObjectCache()) {
5080 cache->remove(scrollbar);
5081 cache->handleScrollbarUpdate(this);
5082 }
5083}
5084
5085void FrameView::addPaintPendingMilestones(OptionSet<LayoutMilestone> milestones)
5086{
5087 m_milestonesPendingPaint.add(milestones);
5088}
5089
5090void FrameView::fireLayoutRelatedMilestonesIfNeeded()
5091{
5092 OptionSet<LayoutMilestone> requestedMilestones;
5093 OptionSet<LayoutMilestone> milestonesAchieved;
5094 Page* page = frame().page();
5095 if (page)
5096 requestedMilestones = page->requestedLayoutMilestones();
5097
5098 if (m_firstLayoutCallbackPending) {
5099 m_firstLayoutCallbackPending = false;
5100 frame().loader().didFirstLayout();
5101 if (requestedMilestones & DidFirstLayout)
5102 milestonesAchieved.add(DidFirstLayout);
5103 if (frame().isMainFrame())
5104 page->startCountingRelevantRepaintedObjects();
5105 }
5106
5107 if (!m_isVisuallyNonEmpty && qualifiesAsVisuallyNonEmpty()) {
5108 m_isVisuallyNonEmpty = true;
5109 addPaintPendingMilestones(DidFirstMeaningfulPaint);
5110 if (requestedMilestones & DidFirstVisuallyNonEmptyLayout)
5111 milestonesAchieved.add(DidFirstVisuallyNonEmptyLayout);
5112 }
5113
5114 if (!m_renderedSignificantAmountOfText && qualifiesAsSignificantRenderedText()) {
5115 m_renderedSignificantAmountOfText = true;
5116 if (requestedMilestones & DidRenderSignificantAmountOfText)
5117 milestonesAchieved.add(DidRenderSignificantAmountOfText);
5118 }
5119
5120 if (milestonesAchieved && frame().isMainFrame()) {
5121 if (milestonesAchieved.contains(DidFirstVisuallyNonEmptyLayout))
5122 RELEASE_LOG_IF_ALLOWED("fireLayoutRelatedMilestonesIfNeeded() - firing first visually non-empty layout milestone on the main frame");
5123 frame().loader().didReachLayoutMilestone(milestonesAchieved);
5124 }
5125}
5126
5127void FrameView::firePaintRelatedMilestonesIfNeeded()
5128{
5129 Page* page = frame().page();
5130 if (!page)
5131 return;
5132
5133 OptionSet<LayoutMilestone> milestonesAchieved;
5134
5135 // Make sure the pending paint milestones have actually been requested before we send them.
5136 if (m_milestonesPendingPaint & DidFirstFlushForHeaderLayer) {
5137 if (page->requestedLayoutMilestones() & DidFirstFlushForHeaderLayer)
5138 milestonesAchieved.add(DidFirstFlushForHeaderLayer);
5139 }
5140
5141 if (m_milestonesPendingPaint & DidFirstPaintAfterSuppressedIncrementalRendering) {
5142 if (page->requestedLayoutMilestones() & DidFirstPaintAfterSuppressedIncrementalRendering)
5143 milestonesAchieved.add(DidFirstPaintAfterSuppressedIncrementalRendering);
5144 }
5145
5146 if (m_milestonesPendingPaint & DidFirstMeaningfulPaint) {
5147 if (page->requestedLayoutMilestones() & DidFirstMeaningfulPaint)
5148 milestonesAchieved.add(DidFirstMeaningfulPaint);
5149 }
5150
5151 m_milestonesPendingPaint = { };
5152
5153 if (milestonesAchieved)
5154 page->mainFrame().loader().didReachLayoutMilestone(milestonesAchieved);
5155}
5156
5157void FrameView::setVisualUpdatesAllowedByClient(bool visualUpdatesAllowed)
5158{
5159 if (m_visualUpdatesAllowedByClient == visualUpdatesAllowed)
5160 return;
5161
5162 m_visualUpdatesAllowedByClient = visualUpdatesAllowed;
5163
5164 frame().document()->setVisualUpdatesAllowedByClient(visualUpdatesAllowed);
5165}
5166
5167void FrameView::setScrollPinningBehavior(ScrollPinningBehavior pinning)
5168{
5169 m_scrollPinningBehavior = pinning;
5170
5171 if (Page* page = frame().page()) {
5172 if (auto* scrollingCoordinator = page->scrollingCoordinator())
5173 scrollingCoordinator->setScrollPinningBehavior(pinning);
5174 }
5175
5176 updateScrollbars(scrollPosition());
5177}
5178
5179ScrollBehaviorForFixedElements FrameView::scrollBehaviorForFixedElements() const
5180{
5181 return frame().settings().backgroundShouldExtendBeyondPage() ? StickToViewportBounds : StickToDocumentBounds;
5182}
5183
5184RenderView* FrameView::renderView() const
5185{
5186 return frame().contentRenderer();
5187}
5188
5189int FrameView::mapFromLayoutToCSSUnits(LayoutUnit value) const
5190{
5191 return value / (frame().pageZoomFactor() * frame().frameScaleFactor());
5192}
5193
5194LayoutUnit FrameView::mapFromCSSToLayoutUnits(int value) const
5195{
5196 return value * frame().pageZoomFactor() * frame().frameScaleFactor();
5197}
5198
5199void FrameView::didAddWidgetToRenderTree(Widget& widget)
5200{
5201 ASSERT(!m_widgetsInRenderTree.contains(&widget));
5202 m_widgetsInRenderTree.add(&widget);
5203}
5204
5205void FrameView::willRemoveWidgetFromRenderTree(Widget& widget)
5206{
5207 ASSERT(m_widgetsInRenderTree.contains(&widget));
5208 m_widgetsInRenderTree.remove(&widget);
5209}
5210
5211static Vector<RefPtr<Widget>> collectAndProtectWidgets(const HashSet<Widget*>& set)
5212{
5213 return copyToVectorOf<RefPtr<Widget>>(set);
5214}
5215
5216void FrameView::updateWidgetPositions()
5217{
5218 m_updateWidgetPositionsTimer.stop();
5219 // updateWidgetPosition() can possibly cause layout to be re-entered (via plug-ins running
5220 // scripts in response to NPP_SetWindow, for example), so we need to keep the Widgets
5221 // alive during enumeration.
5222 for (auto& widget : collectAndProtectWidgets(m_widgetsInRenderTree)) {
5223 if (auto* renderer = RenderWidget::find(*widget)) {
5224 auto ignoreWidgetState = renderer->updateWidgetPosition();
5225 UNUSED_PARAM(ignoreWidgetState);
5226 }
5227 }
5228}
5229
5230void FrameView::scheduleUpdateWidgetPositions()
5231{
5232 if (!m_updateWidgetPositionsTimer.isActive())
5233 m_updateWidgetPositionsTimer.startOneShot(0_s);
5234}
5235
5236void FrameView::updateWidgetPositionsTimerFired()
5237{
5238 updateWidgetPositions();
5239}
5240
5241void FrameView::notifyWidgets(WidgetNotification notification)
5242{
5243 for (auto& widget : collectAndProtectWidgets(m_widgetsInRenderTree))
5244 widget->notifyWidget(notification);
5245}
5246
5247void FrameView::setViewExposedRect(Optional<FloatRect> viewExposedRect)
5248{
5249 if (m_viewExposedRect == viewExposedRect)
5250 return;
5251
5252 LOG_WITH_STREAM(Scrolling, stream << "FrameView " << this << " setViewExposedRect " << (viewExposedRect ? viewExposedRect.value() : FloatRect()));
5253
5254 bool hasRectChanged = !m_viewExposedRect == !viewExposedRect;
5255 m_viewExposedRect = viewExposedRect;
5256
5257 // FIXME: We should support clipping to the exposed rect for subframes as well.
5258 if (!frame().isMainFrame())
5259 return;
5260
5261 if (TiledBacking* tiledBacking = this->tiledBacking()) {
5262 if (hasRectChanged)
5263 updateTiledBackingAdaptiveSizing();
5264 adjustTiledBackingCoverage();
5265 tiledBacking->setTiledScrollingIndicatorPosition(m_viewExposedRect ? m_viewExposedRect.value().location() : FloatPoint());
5266 }
5267
5268 if (auto* view = renderView())
5269 view->compositor().scheduleLayerFlush(false /* canThrottle */);
5270
5271 if (auto* page = frame().page())
5272 page->pageOverlayController().didChangeViewExposedRect();
5273}
5274
5275void FrameView::clearViewportSizeOverrideForCSSViewportUnits()
5276{
5277 if (!m_overrideViewportSize)
5278 return;
5279
5280 m_overrideViewportSize = WTF::nullopt;
5281 if (auto* document = frame().document())
5282 document->styleScope().didChangeStyleSheetEnvironment();
5283}
5284
5285void FrameView::setViewportSizeForCSSViewportUnits(IntSize size)
5286{
5287 overrideViewportSizeForCSSViewportUnits({ size.width(), size.height() });
5288}
5289
5290void FrameView::overrideViewportSizeForCSSViewportUnits(OverrideViewportSize size)
5291{
5292 if (m_overrideViewportSize && *m_overrideViewportSize == size)
5293 return;
5294
5295 m_overrideViewportSize = size;
5296
5297 if (auto* document = frame().document())
5298 document->styleScope().didChangeStyleSheetEnvironment();
5299}
5300
5301IntSize FrameView::viewportSizeForCSSViewportUnits() const
5302{
5303 OverrideViewportSize viewportSize;
5304
5305 if (m_overrideViewportSize) {
5306 viewportSize = *m_overrideViewportSize;
5307 // auto-size overrides the width only, so we can't always bail out early here.
5308 if (viewportSize.width && viewportSize.height)
5309 return { *viewportSize.width, *viewportSize.height };
5310 }
5311
5312 if (useFixedLayout()) {
5313 auto fixedLayoutSize = this->fixedLayoutSize();
5314 viewportSize.width = viewportSize.width.valueOr(fixedLayoutSize.width());
5315 viewportSize.height = viewportSize.height.valueOr(fixedLayoutSize.height());
5316 return { *viewportSize.width, *viewportSize.height };
5317 }
5318
5319 // FIXME: the value returned should take into account the value of the overflow
5320 // property on the root element.
5321 auto visibleContentSizeIncludingScrollbars = visibleContentRectIncludingScrollbars().size();
5322 viewportSize.width = viewportSize.width.valueOr(visibleContentSizeIncludingScrollbars.width());
5323 viewportSize.height = viewportSize.height.valueOr(visibleContentSizeIncludingScrollbars.height());
5324 return { *viewportSize.width, *viewportSize.height };
5325}
5326
5327bool FrameView::shouldPlaceBlockDirectionScrollbarOnLeft() const
5328{
5329 return renderView() && renderView()->shouldPlaceBlockDirectionScrollbarOnLeft();
5330}
5331
5332} // namespace WebCore
5333