Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  * Copyright (C) 2000 Dirk Mueller (mueller (at) kde.org)
      4  * Copyright (C) 2004, 2006, 2009, 2010 Apple Inc. All rights reserved.
      5  * Copyright (C) 2013 Google Inc. All rights reserved.
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Library General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Library General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Library General Public License
     18  * along with this library; see the file COPYING.LIB.  If not, write to
     19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     20  * Boston, MA 02110-1301, USA.
     21  *
     22  */
     23 
     24 #include "config.h"
     25 #include "core/rendering/RenderWidget.h"
     26 
     27 #include "core/accessibility/AXObjectCache.h"
     28 #include "core/frame/LocalFrame.h"
     29 #include "core/html/HTMLFrameOwnerElement.h"
     30 #include "core/html/HTMLPlugInElement.h"
     31 #include "core/paint/BoxPainter.h"
     32 #include "core/rendering/GraphicsContextAnnotator.h"
     33 #include "core/rendering/HitTestResult.h"
     34 #include "core/rendering/RenderLayer.h"
     35 #include "core/rendering/RenderView.h"
     36 #include "core/rendering/compositing/CompositedLayerMapping.h"
     37 #include "core/rendering/compositing/RenderLayerCompositor.h"
     38 #include "wtf/HashMap.h"
     39 
     40 namespace blink {
     41 
     42 RenderWidget::RenderWidget(Element* element)
     43     : RenderReplaced(element)
     44 #if !ENABLE(OILPAN)
     45     // Reference counting is used to prevent the widget from being
     46     // destroyed while inside the Widget code, which might not be
     47     // able to handle that.
     48     , m_refCount(1)
     49 #endif
     50 {
     51     ASSERT(element);
     52     frameView()->addWidget(this);
     53 }
     54 
     55 void RenderWidget::willBeDestroyed()
     56 {
     57     frameView()->removeWidget(this);
     58 
     59     if (AXObjectCache* cache = document().existingAXObjectCache()) {
     60         cache->childrenChanged(this->parent());
     61         cache->remove(this);
     62     }
     63 
     64     Element* element = toElement(node());
     65     if (element && element->isFrameOwnerElement())
     66         toHTMLFrameOwnerElement(element)->setWidget(nullptr);
     67 
     68     RenderReplaced::willBeDestroyed();
     69 }
     70 
     71 void RenderWidget::destroy()
     72 {
     73 #if ENABLE(ASSERT) && ENABLE(OILPAN)
     74     ASSERT(!m_didCallDestroy);
     75     m_didCallDestroy = true;
     76 #endif
     77     willBeDestroyed();
     78     clearNode();
     79 #if ENABLE(OILPAN)
     80     // In Oilpan, postDestroy doesn't delete |this|. So calling it here is safe
     81     // though |this| will be referred in FrameView.
     82     postDestroy();
     83 #else
     84     deref();
     85 #endif
     86 }
     87 
     88 RenderWidget::~RenderWidget()
     89 {
     90 #if !ENABLE(OILPAN)
     91     ASSERT(m_refCount <= 0);
     92 #endif
     93 }
     94 
     95 Widget* RenderWidget::widget() const
     96 {
     97     // Plugin widgets are stored in their DOM node. This includes HTMLAppletElement.
     98     Element* element = toElement(node());
     99 
    100     if (element && element->isFrameOwnerElement())
    101         return toHTMLFrameOwnerElement(element)->ownedWidget();
    102 
    103     return 0;
    104 }
    105 
    106 // Widgets are always placed on integer boundaries, so rounding the size is actually
    107 // the desired behavior. This function is here because it's otherwise seldom what we
    108 // want to do with a LayoutRect.
    109 static inline IntRect roundedIntRect(const LayoutRect& rect)
    110 {
    111     return IntRect(roundedIntPoint(rect.location()), roundedIntSize(rect.size()));
    112 }
    113 
    114 bool RenderWidget::setWidgetGeometry(const LayoutRect& frame)
    115 {
    116     if (!node())
    117         return false;
    118 
    119     Widget* widget = this->widget();
    120     ASSERT(widget);
    121 
    122     IntRect newFrame = roundedIntRect(frame);
    123 
    124     if (widget->frameRect() == newFrame)
    125         return false;
    126 
    127     RefPtrWillBeRawPtr<RenderWidget> protector(this);
    128     RefPtrWillBeRawPtr<Node> protectedNode(node());
    129     widget->setFrameRect(newFrame);
    130     return widget->frameRect().size() != newFrame.size();
    131 }
    132 
    133 bool RenderWidget::updateWidgetGeometry()
    134 {
    135     Widget* widget = this->widget();
    136     ASSERT(widget);
    137 
    138     LayoutRect contentBox = contentBoxRect();
    139     LayoutRect absoluteContentBox(localToAbsoluteQuad(FloatQuad(contentBox)).boundingBox());
    140     if (widget->isFrameView()) {
    141         contentBox.setLocation(absoluteContentBox.location());
    142         return setWidgetGeometry(contentBox);
    143     }
    144 
    145     return setWidgetGeometry(absoluteContentBox);
    146 }
    147 
    148 void RenderWidget::layout()
    149 {
    150     ASSERT(needsLayout());
    151 
    152     clearNeedsLayout();
    153 }
    154 
    155 void RenderWidget::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
    156 {
    157     RenderReplaced::styleDidChange(diff, oldStyle);
    158     Widget* widget = this->widget();
    159 
    160     if (widget) {
    161         if (style()->visibility() != VISIBLE) {
    162             widget->hide();
    163         } else {
    164             widget->show();
    165         }
    166     }
    167 }
    168 
    169 void RenderWidget::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
    170 {
    171     LayoutPoint adjustedPaintOffset = paintOffset + location();
    172 
    173     Widget* widget = this->widget();
    174     RELEASE_ASSERT(widget);
    175 
    176     // Tell the widget to paint now. This is the only time the widget is allowed
    177     // to paint itself. That way it will composite properly with z-indexed layers.
    178     IntPoint widgetLocation = widget->frameRect().location();
    179     IntPoint paintLocation(roundToInt(adjustedPaintOffset.x() + borderLeft() + paddingLeft()),
    180         roundToInt(adjustedPaintOffset.y() + borderTop() + paddingTop()));
    181     IntRect paintRect = paintInfo.rect;
    182 
    183     IntSize widgetPaintOffset = paintLocation - widgetLocation;
    184     // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer,
    185     // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing.
    186     if (!widgetPaintOffset.isZero()) {
    187         paintInfo.context->translate(widgetPaintOffset.width(), widgetPaintOffset.height());
    188         paintRect.move(-widgetPaintOffset);
    189     }
    190     widget->paint(paintInfo.context, paintRect);
    191 
    192     if (!widgetPaintOffset.isZero())
    193         paintInfo.context->translate(-widgetPaintOffset.width(), -widgetPaintOffset.height());
    194 }
    195 
    196 void RenderWidget::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
    197 {
    198     ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
    199 
    200     if (!shouldPaint(paintInfo, paintOffset))
    201         return;
    202 
    203     LayoutPoint adjustedPaintOffset = paintOffset + location();
    204 
    205     if (hasBoxDecorationBackground() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection))
    206         paintBoxDecorationBackground(paintInfo, adjustedPaintOffset);
    207 
    208     if (paintInfo.phase == PaintPhaseMask) {
    209         paintMask(paintInfo, adjustedPaintOffset);
    210         return;
    211     }
    212 
    213     if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->hasOutline())
    214         paintOutline(paintInfo, LayoutRect(adjustedPaintOffset, size()));
    215 
    216     if (paintInfo.phase != PaintPhaseForeground)
    217         return;
    218 
    219     if (style()->hasBorderRadius()) {
    220         LayoutRect borderRect = LayoutRect(adjustedPaintOffset, size());
    221 
    222         if (borderRect.isEmpty())
    223             return;
    224 
    225         // Push a clip if we have a border radius, since we want to round the foreground content that gets painted.
    226         paintInfo.context->save();
    227         RoundedRect roundedInnerRect = style()->getRoundedInnerBorderFor(borderRect,
    228             paddingTop() + borderTop(), paddingBottom() + borderBottom(), paddingLeft() + borderLeft(), paddingRight() + borderRight(), true, true);
    229         BoxPainter::clipRoundedInnerRect(paintInfo.context, borderRect, roundedInnerRect);
    230     }
    231 
    232     Widget* widget = this->widget();
    233     if (widget)
    234         paintContents(paintInfo, paintOffset);
    235 
    236     if (style()->hasBorderRadius())
    237         paintInfo.context->restore();
    238 
    239     // Paint a partially transparent wash over selected widgets.
    240     if (isSelected() && !document().printing()) {
    241         LayoutRect rect = localSelectionRect();
    242         rect.moveBy(adjustedPaintOffset);
    243         paintInfo.context->fillRect(pixelSnappedIntRect(rect), selectionBackgroundColor());
    244     }
    245 
    246     if (canResize())
    247         layer()->scrollableArea()->paintResizer(paintInfo.context, roundedIntPoint(adjustedPaintOffset), paintInfo.rect);
    248 }
    249 
    250 #if !ENABLE(OILPAN)
    251 void RenderWidget::deref()
    252 {
    253     if (--m_refCount <= 0)
    254         postDestroy();
    255 }
    256 #endif
    257 
    258 void RenderWidget::updateOnWidgetChange()
    259 {
    260     Widget* widget = this->widget();
    261     if (!widget)
    262         return;
    263 
    264     if (!style())
    265         return;
    266 
    267     if (!needsLayout())
    268         updateWidgetGeometry();
    269 
    270     if (style()->visibility() != VISIBLE) {
    271         widget->hide();
    272     } else {
    273         widget->show();
    274         // FIXME: Why do we issue a full paint invalidation in this case, but not the other?
    275         setShouldDoFullPaintInvalidation(true);
    276     }
    277 }
    278 
    279 void RenderWidget::updateWidgetPosition()
    280 {
    281     Widget* widget = this->widget();
    282     if (!widget || !node()) // Check the node in case destroy() has been called.
    283         return;
    284 
    285     bool boundsChanged = updateWidgetGeometry();
    286 
    287     // if the frame bounds got changed, or if view needs layout (possibly indicating
    288     // content size is wrong) we have to do a layout to set the right widget size
    289     if (widget && widget->isFrameView()) {
    290         FrameView* frameView = toFrameView(widget);
    291         // Check the frame's page to make sure that the frame isn't in the process of being destroyed.
    292         if ((boundsChanged || frameView->needsLayout()) && frameView->frame().page())
    293             frameView->layout();
    294     }
    295 }
    296 
    297 void RenderWidget::widgetPositionsUpdated()
    298 {
    299     Widget* widget = this->widget();
    300     if (!widget)
    301         return;
    302     widget->widgetPositionsUpdated();
    303 }
    304 
    305 bool RenderWidget::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
    306 {
    307     bool hadResult = result.innerNode();
    308     bool inside = RenderReplaced::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, action);
    309 
    310     // Check to see if we are really over the widget itself (and not just in the border/padding area).
    311     if ((inside || result.isRectBasedTest()) && !hadResult && result.innerNode() == node())
    312         result.setIsOverWidget(contentBoxRect().contains(result.localPoint()));
    313     return inside;
    314 }
    315 
    316 CursorDirective RenderWidget::getCursor(const LayoutPoint& point, Cursor& cursor) const
    317 {
    318     if (widget() && widget()->isPluginView()) {
    319         // A plug-in is responsible for setting the cursor when the pointer is over it.
    320         return DoNotSetCursor;
    321     }
    322     return RenderReplaced::getCursor(point, cursor);
    323 }
    324 
    325 } // namespace blink
    326