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