/* * Copyright (C) 1998, 1999 Torben Weis * 1999 Lars Knoll * 1999 Antti Koivisto * 2000 Dirk Mueller * Copyright (C) 2004-2008, 2013-2015 Apple Inc. All rights reserved. * (C) 2006 Graham Dennis (graham.dennis@gmail.com) * (C) 2006 Alexey Proskuryakov (ap@nypop.com) * Copyright (C) 2009 Google Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "config.h" #include "FrameView.h" #include "AXObjectCache.h" #include "AnimationController.h" #include "BackForwardController.h" #include "CachedImage.h" #include "CachedResourceLoader.h" #include "Chrome.h" #include "ChromeClient.h" #include "DOMWindow.h" #include "DebugPageOverlays.h" #include "DocumentMarkerController.h" #include "EventHandler.h" #include "FloatRect.h" #include "FocusController.h" #include "FontLoader.h" #include "FrameLoader.h" #include "FrameLoaderClient.h" #include "FrameSelection.h" #include "FrameTree.h" #include "GraphicsContext.h" #include "HTMLBodyElement.h" #include "HTMLDocument.h" #include "HTMLFrameElement.h" #include "HTMLFrameSetElement.h" #include "HTMLNames.h" #include "HTMLPlugInImageElement.h" #include "ImageDocument.h" #include "InspectorClient.h" #include "InspectorController.h" #include "InspectorInstrumentation.h" #include "Logging.h" #include "MainFrame.h" #include "MemoryCache.h" #include "MemoryPressureHandler.h" #include "OverflowEvent.h" #include "PageCache.h" #include "PageOverlayController.h" #include "ProgressTracker.h" #include "RenderEmbeddedObject.h" #include "RenderFullScreen.h" #include "RenderIFrame.h" #include "RenderInline.h" #include "RenderLayer.h" #include "RenderLayerBacking.h" #include "RenderLayerCompositor.h" #include "RenderSVGRoot.h" #include "RenderScrollbar.h" #include "RenderScrollbarPart.h" #include "RenderStyle.h" #include "RenderText.h" #include "RenderTheme.h" #include "RenderView.h" #include "RenderWidget.h" #include "SVGDocument.h" #include "SVGSVGElement.h" #include "ScriptedAnimationController.h" #include "ScrollAnimator.h" #include "ScrollingCoordinator.h" #include "Settings.h" #include "StyleResolver.h" #include "TextResourceDecoder.h" #include "TextStream.h" #include "TiledBacking.h" #include "WheelEventTestTrigger.h" #include #include #include #include #if USE(COORDINATED_GRAPHICS) #include "TiledBackingStore.h" #endif #if ENABLE(TEXT_AUTOSIZING) #include "TextAutosizer.h" #endif #if ENABLE(CSS_SCROLL_SNAP) #include "AxisScrollSnapOffsets.h" #endif #if PLATFORM(IOS) #include "DocumentLoader.h" #include "LegacyTileCache.h" #endif namespace WebCore { using namespace HTMLNames; double FrameView::sCurrentPaintTimeStamp = 0.0; // The maximum number of updateEmbeddedObjects iterations that should be done before returning. static const unsigned maxUpdateEmbeddedObjectsIterations = 2; static RenderLayer::UpdateLayerPositionsFlags updateLayerPositionFlags(RenderLayer* layer, bool isRelayoutingSubtree, bool didFullRepaint) { RenderLayer::UpdateLayerPositionsFlags flags = RenderLayer::defaultFlags; if (didFullRepaint) { flags &= ~RenderLayer::CheckForRepaint; flags |= RenderLayer::NeedsFullRepaintInBacking; } if (isRelayoutingSubtree && layer->enclosingPaginationLayer(RenderLayer::IncludeCompositedPaginatedLayers)) flags |= RenderLayer::UpdatePagination; return flags; } Pagination::Mode paginationModeForRenderStyle(const RenderStyle& style) { EOverflow overflow = style.overflowY(); if (overflow != OPAGEDX && overflow != OPAGEDY) return Pagination::Unpaginated; bool isHorizontalWritingMode = style.isHorizontalWritingMode(); TextDirection textDirection = style.direction(); WritingMode writingMode = style.writingMode(); // paged-x always corresponds to LeftToRightPaginated or RightToLeftPaginated. If the WritingMode // is horizontal, then we use TextDirection to choose between those options. If the WritingMode // is vertical, then the direction of the verticality dictates the choice. if (overflow == OPAGEDX) { if ((isHorizontalWritingMode && textDirection == LTR) || writingMode == LeftToRightWritingMode) return Pagination::LeftToRightPaginated; return Pagination::RightToLeftPaginated; } // paged-y always corresponds to TopToBottomPaginated or BottomToTopPaginated. If the WritingMode // is horizontal, then the direction of the horizontality dictates the choice. If the WritingMode // is vertical, then we use TextDirection to choose between those options. if (writingMode == TopToBottomWritingMode || (!isHorizontalWritingMode && textDirection == RTL)) return Pagination::TopToBottomPaginated; return Pagination::BottomToTopPaginated; } class SubtreeLayoutStateMaintainer { public: SubtreeLayoutStateMaintainer(RenderElement* subtreeLayoutRoot) : m_layoutRoot(subtreeLayoutRoot) { if (m_layoutRoot) { RenderView& view = m_layoutRoot->view(); view.pushLayoutState(*m_layoutRoot); m_disableLayoutState = view.shouldDisableLayoutStateForSubtree(m_layoutRoot); if (m_disableLayoutState) view.disableLayoutState(); } } ~SubtreeLayoutStateMaintainer() { if (m_layoutRoot) { RenderView& view = m_layoutRoot->view(); view.popLayoutState(*m_layoutRoot); if (m_disableLayoutState) view.enableLayoutState(); } } private: RenderElement* m_layoutRoot { nullptr }; bool m_disableLayoutState { false }; }; FrameView::FrameView(Frame& frame) : m_frame(frame) , m_canHaveScrollbars(true) , m_layoutTimer(*this, &FrameView::layoutTimerFired) , m_layoutPhase(OutsideLayout) , m_inSynchronousPostLayout(false) , m_postLayoutTasksTimer(*this, &FrameView::performPostLayoutTasks) , m_updateEmbeddedObjectsTimer(*this, &FrameView::updateEmbeddedObjectsTimerFired) , m_isTransparent(false) , m_baseBackgroundColor(Color::white) , m_mediaType("screen") , m_overflowStatusDirty(true) , m_wasScrolledByUser(false) , m_inProgrammaticScroll(false) , m_safeToPropagateScrollToParent(true) , m_delayedScrollEventTimer(*this, &FrameView::sendScrollEvent) , m_isTrackingRepaints(false) , m_shouldUpdateWhileOffscreen(true) , m_exposedRect(FloatRect::infiniteRect()) , m_deferSetNeedsLayoutCount(0) , m_setNeedsLayoutWasDeferred(false) , m_speculativeTilingEnabled(false) , m_speculativeTilingEnableTimer(*this, &FrameView::speculativeTilingEnableTimerFired) #if PLATFORM(IOS) , m_useCustomFixedPositionLayoutRect(false) , m_useCustomSizeForResizeEvent(false) #endif , m_hasOverrideViewportSize(false) , m_shouldAutoSize(false) , m_inAutoSize(false) , m_didRunAutosize(false) , m_autoSizeFixedMinimumHeight(0) , m_headerHeight(0) , m_footerHeight(0) , m_milestonesPendingPaint(0) , m_visualUpdatesAllowedByClient(true) , m_hasFlippedBlockRenderers(false) , m_scrollPinningBehavior(DoNotPin) { init(); #if ENABLE(RUBBER_BANDING) ScrollElasticity verticalElasticity = ScrollElasticityNone; ScrollElasticity horizontalElasticity = ScrollElasticityNone; if (m_frame->isMainFrame()) { verticalElasticity = m_frame->page() ? m_frame->page()->verticalScrollElasticity() : ScrollElasticityAllowed; horizontalElasticity = m_frame->page() ? m_frame->page()->horizontalScrollElasticity() : ScrollElasticityAllowed; } else if (m_frame->settings().rubberBandingForSubScrollableRegionsEnabled()) { verticalElasticity = ScrollElasticityAutomatic; horizontalElasticity = ScrollElasticityAutomatic; } ScrollableArea::setVerticalScrollElasticity(verticalElasticity); ScrollableArea::setHorizontalScrollElasticity(horizontalElasticity); #endif } Ref FrameView::create(Frame& frame) { Ref view = adoptRef(*new FrameView(frame)); view->show(); return view; } Ref FrameView::create(Frame& frame, const IntSize& initialSize) { Ref view = adoptRef(*new FrameView(frame)); view->Widget::setFrameRect(IntRect(view->location(), initialSize)); view->show(); return view; } FrameView::~FrameView() { if (m_postLayoutTasksTimer.isActive()) m_postLayoutTasksTimer.stop(); removeFromAXObjectCache(); resetScrollbars(); // Custom scrollbars should already be destroyed at this point ASSERT(!horizontalScrollbar() || !horizontalScrollbar()->isCustomScrollbar()); ASSERT(!verticalScrollbar() || !verticalScrollbar()->isCustomScrollbar()); setHasHorizontalScrollbar(false); // Remove native scrollbars now before we lose the connection to the HostWindow. setHasVerticalScrollbar(false); ASSERT(!m_scrollCorner); ASSERT(frame().view() != this || !frame().contentRenderer()); } void FrameView::reset() { m_cannotBlitToWindow = false; m_isOverlapped = false; m_contentIsOpaque = false; m_layoutTimer.stop(); m_layoutRoot = nullptr; m_delayedLayout = false; m_needsFullRepaint = true; m_layoutSchedulingEnabled = true; m_layoutPhase = OutsideLayout; m_inSynchronousPostLayout = false; m_layoutCount = 0; m_nestedLayoutCount = 0; m_postLayoutTasksTimer.stop(); m_updateEmbeddedObjectsTimer.stop(); m_firstLayout = true; m_firstLayoutCallbackPending = false; m_wasScrolledByUser = false; m_safeToPropagateScrollToParent = true; m_delayedScrollEventTimer.stop(); m_lastViewportSize = IntSize(); m_lastZoomFactor = 1.0f; m_isTrackingRepaints = false; m_trackedRepaintRects.clear(); m_lastPaintTime = 0; m_paintBehavior = PaintBehaviorNormal; m_isPainting = false; m_visuallyNonEmptyCharacterCount = 0; m_visuallyNonEmptyPixelCount = 0; m_isVisuallyNonEmpty = false; m_firstVisuallyNonEmptyLayoutCallbackPending = true; m_viewportIsStable = true; m_maintainScrollPositionAnchor = nullptr; } void FrameView::removeFromAXObjectCache() { if (AXObjectCache* cache = axObjectCache()) { if (HTMLFrameOwnerElement* owner = frame().ownerElement()) cache->childrenChanged(owner->renderer()); cache->remove(this); } } void FrameView::resetScrollbars() { // Reset the document's scrollbars back to our defaults before we yield the floor. m_firstLayout = true; setScrollbarsSuppressed(true); if (m_canHaveScrollbars) setScrollbarModes(ScrollbarAuto, ScrollbarAuto); else setScrollbarModes(ScrollbarAlwaysOff, ScrollbarAlwaysOff); setScrollbarsSuppressed(false); } void FrameView::resetScrollbarsAndClearContentsSize() { resetScrollbars(); setScrollbarsSuppressed(true); setContentsSize(IntSize()); setScrollbarsSuppressed(false); } void FrameView::init() { reset(); m_margins = LayoutSize(-1, -1); // undefined m_size = LayoutSize(); // Propagate the marginwidth/height and scrolling modes to the view. Element* ownerElement = frame().ownerElement(); if (is(ownerElement)) { HTMLFrameElementBase& frameElement = downcast(*ownerElement); if (frameElement.scrollingMode() == ScrollbarAlwaysOff) setCanHaveScrollbars(false); LayoutUnit marginWidth = frameElement.marginWidth(); LayoutUnit marginHeight = frameElement.marginHeight(); if (marginWidth != -1) setMarginWidth(marginWidth); if (marginHeight != -1) setMarginHeight(marginHeight); } Page* page = frame().page(); if (page && page->chrome().client().shouldPaintEntireContents()) setPaintsEntireContents(true); } void FrameView::prepareForDetach() { detachCustomScrollbars(); // When the view is no longer associated with a frame, it needs to be removed from the ax object cache // right now, otherwise it won't be able to reach the topDocument()'s axObject cache later. removeFromAXObjectCache(); if (frame().page()) { if (ScrollingCoordinator* scrollingCoordinator = frame().page()->scrollingCoordinator()) scrollingCoordinator->willDestroyScrollableArea(*this); } } void FrameView::detachCustomScrollbars() { Scrollbar* horizontalBar = horizontalScrollbar(); if (horizontalBar && horizontalBar->isCustomScrollbar()) setHasHorizontalScrollbar(false); Scrollbar* verticalBar = verticalScrollbar(); if (verticalBar && verticalBar->isCustomScrollbar()) setHasVerticalScrollbar(false); m_scrollCorner = nullptr; } void FrameView::recalculateScrollbarOverlayStyle() { ScrollbarOverlayStyle oldOverlayStyle = scrollbarOverlayStyle(); WTF::Optional clientOverlayStyle = frame().page() ? frame().page()->chrome().client().preferredScrollbarOverlayStyle() : ScrollbarOverlayStyleDefault; if (clientOverlayStyle) { if (clientOverlayStyle.value() != oldOverlayStyle) setScrollbarOverlayStyle(clientOverlayStyle.value()); return; } ScrollbarOverlayStyle computedOverlayStyle = ScrollbarOverlayStyleDefault; Color backgroundColor = documentBackgroundColor(); if (backgroundColor.isValid()) { // Reduce the background color from RGB to a lightness value // and determine which scrollbar style to use based on a lightness // heuristic. double hue, saturation, lightness; backgroundColor.getHSL(hue, saturation, lightness); if (lightness <= .5 && backgroundColor.alpha() > 0) computedOverlayStyle = ScrollbarOverlayStyleLight; } if (oldOverlayStyle != computedOverlayStyle) setScrollbarOverlayStyle(computedOverlayStyle); } void FrameView::clear() { setCanBlitOnScroll(true); reset(); setScrollbarsSuppressed(true); #if PLATFORM(IOS) // To avoid flashes of white, disable tile updates immediately when view is cleared at the beginning of a page load. // Tiling will be re-enabled from UIKit via [WAKWindow setTilingMode:] when we have content to draw. if (LegacyTileCache* tileCache = legacyTileCache()) tileCache->setTilingMode(LegacyTileCache::Disabled); #endif } #if PLATFORM(IOS) void FrameView::didReplaceMultipartContent() { // Re-enable tile updates that were disabled in clear(). if (LegacyTileCache* tileCache = legacyTileCache()) tileCache->setTilingMode(LegacyTileCache::Normal); } #endif bool FrameView::didFirstLayout() const { return !m_firstLayout; } void FrameView::invalidateRect(const IntRect& rect) { if (!parent()) { if (HostWindow* window = hostWindow()) window->invalidateContentsAndRootView(rect); return; } RenderWidget* renderer = frame().ownerRenderer(); if (!renderer) return; IntRect repaintRect = rect; repaintRect.move(renderer->borderLeft() + renderer->paddingLeft(), renderer->borderTop() + renderer->paddingTop()); renderer->repaintRectangle(repaintRect); } void FrameView::setFrameRect(const IntRect& newRect) { Ref protect(*this); IntRect oldRect = frameRect(); if (newRect == oldRect) return; #if ENABLE(TEXT_AUTOSIZING) // Autosized font sizes depend on the width of the viewing area. if (newRect.width() != oldRect.width()) { if (frame().isMainFrame() && page->settings().textAutosizingEnabled()) { for (Frame* frame = &page->mainFrame(); frame; frame = frame->tree().traverseNext()) frame().document()->textAutosizer()->recalculateMultipliers(); } } #endif ScrollView::setFrameRect(newRect); updateScrollableAreaSet(); if (RenderView* renderView = this->renderView()) { if (renderView->usesCompositing()) renderView->compositor().frameViewDidChangeSize(); } if (frame().isMainFrame()) frame().mainFrame().pageOverlayController().didChangeViewSize(); viewportContentsChanged(); } #if ENABLE(REQUEST_ANIMATION_FRAME) bool FrameView::scheduleAnimation() { if (HostWindow* window = hostWindow()) { window->scheduleAnimation(); return true; } return false; } #endif void FrameView::setMarginWidth(LayoutUnit w) { // make it update the rendering area when set m_margins.setWidth(w); } void FrameView::setMarginHeight(LayoutUnit h) { // make it update the rendering area when set m_margins.setHeight(h); } bool FrameView::frameFlatteningEnabled() const { return frame().settings().frameFlatteningEnabled(); } bool FrameView::isFrameFlatteningValidForThisFrame() const { if (!frameFlatteningEnabled()) return false; HTMLFrameOwnerElement* owner = frame().ownerElement(); if (!owner) return false; // Frame flattening is valid only for and