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/page/Frame.h"
     29 #include "core/platform/graphics/GraphicsContext.h"
     30 #include "core/rendering/HitTestResult.h"
     31 #include "core/rendering/RenderLayer.h"
     32 #include "core/rendering/RenderLayerBacking.h"
     33 #include "core/rendering/RenderView.h"
     34 
     35 using namespace std;
     36 
     37 namespace WebCore {
     38 
     39 static HashMap<const Widget*, RenderWidget*>& widgetRendererMap()
     40 {
     41     static HashMap<const Widget*, RenderWidget*>* staticWidgetRendererMap = new HashMap<const Widget*, RenderWidget*>;
     42     return *staticWidgetRendererMap;
     43 }
     44 
     45 unsigned WidgetHierarchyUpdatesSuspensionScope::s_widgetHierarchyUpdateSuspendCount = 0;
     46 
     47 WidgetHierarchyUpdatesSuspensionScope::WidgetToParentMap& WidgetHierarchyUpdatesSuspensionScope::widgetNewParentMap()
     48 {
     49     DEFINE_STATIC_LOCAL(WidgetToParentMap, map, ());
     50     return map;
     51 }
     52 
     53 void WidgetHierarchyUpdatesSuspensionScope::moveWidgets()
     54 {
     55     WidgetToParentMap map;
     56     widgetNewParentMap().swap(map);
     57     WidgetToParentMap::iterator end = map.end();
     58     for (WidgetToParentMap::iterator it = map.begin(); it != end; ++it) {
     59         Widget* child = it->key.get();
     60         ScrollView* currentParent = child->parent();
     61         FrameView* newParent = it->value;
     62         if (newParent != currentParent) {
     63             if (currentParent)
     64                 currentParent->removeChild(child);
     65             if (newParent)
     66                 newParent->addChild(child);
     67         }
     68     }
     69 }
     70 
     71 static void moveWidgetToParentSoon(Widget* child, FrameView* parent)
     72 {
     73     if (!WidgetHierarchyUpdatesSuspensionScope::isSuspended()) {
     74         if (parent)
     75             parent->addChild(child);
     76         else
     77             child->removeFromParent();
     78         return;
     79     }
     80     WidgetHierarchyUpdatesSuspensionScope::scheduleWidgetToMove(child, parent);
     81 }
     82 
     83 RenderWidget::RenderWidget(Element* element)
     84     : RenderReplaced(element)
     85     , m_widget(0)
     86     , m_frameView(element->document()->view())
     87     // Reference counting is used to prevent the widget from being
     88     // destroyed while inside the Widget code, which might not be
     89     // able to handle that.
     90     , m_refCount(1)
     91 {
     92     view()->addWidget(this);
     93 }
     94 
     95 void RenderWidget::willBeDestroyed()
     96 {
     97     if (RenderView* v = view())
     98         v->removeWidget(this);
     99 
    100     if (AXObjectCache* cache = document()->existingAXObjectCache()) {
    101         cache->childrenChanged(this->parent());
    102         cache->remove(this);
    103     }
    104 
    105     setWidget(0);
    106 
    107     RenderReplaced::willBeDestroyed();
    108 }
    109 
    110 void RenderWidget::destroy()
    111 {
    112     willBeDestroyed();
    113     clearNode();
    114     deref();
    115 }
    116 
    117 RenderWidget::~RenderWidget()
    118 {
    119     ASSERT(m_refCount <= 0);
    120     clearWidget();
    121 }
    122 
    123 // Widgets are always placed on integer boundaries, so rounding the size is actually
    124 // the desired behavior. This function is here because it's otherwise seldom what we
    125 // want to do with a LayoutRect.
    126 static inline IntRect roundedIntRect(const LayoutRect& rect)
    127 {
    128     return IntRect(roundedIntPoint(rect.location()), roundedIntSize(rect.size()));
    129 }
    130 
    131 bool RenderWidget::setWidgetGeometry(const LayoutRect& frame)
    132 {
    133     if (!node())
    134         return false;
    135 
    136     IntRect clipRect = roundedIntRect(enclosingLayer()->childrenClipRect());
    137     IntRect newFrame = roundedIntRect(frame);
    138     bool clipChanged = m_clipRect != clipRect;
    139     bool boundsChanged = m_widget->frameRect() != newFrame;
    140 
    141     if (!boundsChanged && !clipChanged)
    142         return false;
    143 
    144     m_clipRect = clipRect;
    145 
    146     RefPtr<RenderWidget> protector(this);
    147     RefPtr<Node> protectedNode(node());
    148     m_widget->setFrameRect(newFrame);
    149 
    150     if (clipChanged && !boundsChanged)
    151         m_widget->clipRectChanged();
    152 
    153     if (hasLayer() && layer()->isComposited())
    154         layer()->backing()->updateAfterWidgetResize();
    155 
    156     return boundsChanged;
    157 }
    158 
    159 bool RenderWidget::updateWidgetGeometry()
    160 {
    161     LayoutRect contentBox = contentBoxRect();
    162     LayoutRect absoluteContentBox(localToAbsoluteQuad(FloatQuad(contentBox)).boundingBox());
    163     if (m_widget->isFrameView()) {
    164         contentBox.setLocation(absoluteContentBox.location());
    165         return setWidgetGeometry(contentBox);
    166     }
    167 
    168     return setWidgetGeometry(absoluteContentBox);
    169 }
    170 
    171 void RenderWidget::setWidget(PassRefPtr<Widget> widget)
    172 {
    173     if (widget == m_widget)
    174         return;
    175 
    176     if (m_widget) {
    177         moveWidgetToParentSoon(m_widget.get(), 0);
    178         widgetRendererMap().remove(m_widget.get());
    179         clearWidget();
    180     }
    181     m_widget = widget;
    182     if (m_widget) {
    183         widgetRendererMap().add(m_widget.get(), this);
    184         // If we've already received a layout, apply the calculated space to the
    185         // widget immediately, but we have to have really been fully constructed (with a non-null
    186         // style pointer).
    187         if (style()) {
    188             if (!needsLayout())
    189                 updateWidgetGeometry();
    190 
    191             if (style()->visibility() != VISIBLE)
    192                 m_widget->hide();
    193             else {
    194                 m_widget->show();
    195                 repaint();
    196             }
    197         }
    198         moveWidgetToParentSoon(m_widget.get(), m_frameView);
    199     }
    200 
    201     if (AXObjectCache* cache = document()->existingAXObjectCache())
    202         cache->childrenChanged(this);
    203 }
    204 
    205 void RenderWidget::layout()
    206 {
    207     StackStats::LayoutCheckPoint layoutCheckPoint;
    208     ASSERT(needsLayout());
    209 
    210     clearNeedsLayout();
    211 }
    212 
    213 void RenderWidget::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
    214 {
    215     RenderReplaced::styleDidChange(diff, oldStyle);
    216     if (m_widget) {
    217         if (style()->visibility() != VISIBLE)
    218             m_widget->hide();
    219         else
    220             m_widget->show();
    221     }
    222 }
    223 
    224 void RenderWidget::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
    225 {
    226     LayoutPoint adjustedPaintOffset = paintOffset + location();
    227 
    228     // Tell the widget to paint now. This is the only time the widget is allowed
    229     // to paint itself. That way it will composite properly with z-indexed layers.
    230     IntPoint widgetLocation = m_widget->frameRect().location();
    231     IntPoint paintLocation(roundToInt(adjustedPaintOffset.x() + borderLeft() + paddingLeft()),
    232         roundToInt(adjustedPaintOffset.y() + borderTop() + paddingTop()));
    233     IntRect paintRect = paintInfo.rect;
    234 
    235     IntSize widgetPaintOffset = paintLocation - widgetLocation;
    236     // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer,
    237     // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing.
    238     if (!widgetPaintOffset.isZero()) {
    239         paintInfo.context->translate(widgetPaintOffset);
    240         paintRect.move(-widgetPaintOffset);
    241     }
    242     m_widget->paint(paintInfo.context, paintRect);
    243 
    244     if (!widgetPaintOffset.isZero())
    245         paintInfo.context->translate(-widgetPaintOffset);
    246 
    247     if (m_widget->isFrameView()) {
    248         FrameView* frameView = toFrameView(m_widget.get());
    249         bool runOverlapTests = !frameView->useSlowRepaintsIfNotOverlapped() || frameView->hasCompositedContent();
    250         if (paintInfo.overlapTestRequests && runOverlapTests) {
    251             ASSERT(!paintInfo.overlapTestRequests->contains(this));
    252             paintInfo.overlapTestRequests->set(this, m_widget->frameRect());
    253         }
    254     }
    255 }
    256 
    257 void RenderWidget::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
    258 {
    259     ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
    260 
    261     if (!shouldPaint(paintInfo, paintOffset))
    262         return;
    263 
    264     LayoutPoint adjustedPaintOffset = paintOffset + location();
    265 
    266     if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection))
    267         paintBoxDecorations(paintInfo, adjustedPaintOffset);
    268 
    269     if (paintInfo.phase == PaintPhaseMask) {
    270         paintMask(paintInfo, adjustedPaintOffset);
    271         return;
    272     }
    273 
    274     if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && hasOutline())
    275         paintOutline(paintInfo, LayoutRect(adjustedPaintOffset, size()));
    276 
    277     if (!m_frameView || paintInfo.phase != PaintPhaseForeground)
    278         return;
    279 
    280     if (style()->hasBorderRadius()) {
    281         LayoutRect borderRect = LayoutRect(adjustedPaintOffset, size());
    282 
    283         if (borderRect.isEmpty())
    284             return;
    285 
    286         // Push a clip if we have a border radius, since we want to round the foreground content that gets painted.
    287         paintInfo.context->save();
    288         RoundedRect roundedInnerRect = style()->getRoundedInnerBorderFor(borderRect,
    289             paddingTop() + borderTop(), paddingBottom() + borderBottom(), paddingLeft() + borderLeft(), paddingRight() + borderRight(), true, true);
    290         clipRoundedInnerRect(paintInfo.context, borderRect, roundedInnerRect);
    291     }
    292 
    293     if (m_widget)
    294         paintContents(paintInfo, paintOffset);
    295 
    296     if (style()->hasBorderRadius())
    297         paintInfo.context->restore();
    298 
    299     // Paint a partially transparent wash over selected widgets.
    300     if (isSelected() && !document()->printing()) {
    301         // FIXME: selectionRect() is in absolute, not painting coordinates.
    302         paintInfo.context->fillRect(pixelSnappedIntRect(selectionRect()), selectionBackgroundColor());
    303     }
    304 
    305     if (hasLayer() && layer()->canResize())
    306         layer()->paintResizer(paintInfo.context, roundedIntPoint(adjustedPaintOffset), paintInfo.rect);
    307 }
    308 
    309 void RenderWidget::setIsOverlapped(bool isOverlapped)
    310 {
    311     ASSERT(m_widget);
    312     ASSERT(m_widget->isFrameView());
    313     toFrameView(m_widget.get())->setIsOverlapped(isOverlapped);
    314 }
    315 
    316 void RenderWidget::deref()
    317 {
    318     if (--m_refCount <= 0)
    319         postDestroy();
    320 }
    321 
    322 void RenderWidget::updateWidgetPosition()
    323 {
    324     if (!m_widget || !node()) // Check the node in case destroy() has been called.
    325         return;
    326 
    327     bool boundsChanged = updateWidgetGeometry();
    328 
    329     // if the frame bounds got changed, or if view needs layout (possibly indicating
    330     // content size is wrong) we have to do a layout to set the right widget size
    331     if (m_widget && m_widget->isFrameView()) {
    332         FrameView* frameView = toFrameView(m_widget.get());
    333         // Check the frame's page to make sure that the frame isn't in the process of being destroyed.
    334         if ((boundsChanged || frameView->needsLayout()) && frameView->frame()->page())
    335             frameView->layout();
    336     }
    337 }
    338 
    339 void RenderWidget::widgetPositionsUpdated()
    340 {
    341     if (!m_widget)
    342         return;
    343     m_widget->widgetPositionsUpdated();
    344 }
    345 
    346 IntRect RenderWidget::windowClipRect() const
    347 {
    348     if (!m_frameView)
    349         return IntRect();
    350 
    351     return intersection(m_frameView->contentsToWindow(m_clipRect), m_frameView->windowClipRect());
    352 }
    353 
    354 void RenderWidget::clearWidget()
    355 {
    356     m_widget = 0;
    357 }
    358 
    359 RenderWidget* RenderWidget::find(const Widget* widget)
    360 {
    361     return widgetRendererMap().get(widget);
    362 }
    363 
    364 bool RenderWidget::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
    365 {
    366     bool hadResult = result.innerNode();
    367     bool inside = RenderReplaced::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, action);
    368 
    369     // Check to see if we are really over the widget itself (and not just in the border/padding area).
    370     if ((inside || result.isRectBasedTest()) && !hadResult && result.innerNode() == node())
    371         result.setIsOverWidget(contentBoxRect().contains(result.localPoint()));
    372     return inside;
    373 }
    374 
    375 CursorDirective RenderWidget::getCursor(const LayoutPoint& point, Cursor& cursor) const
    376 {
    377     if (widget() && widget()->isPluginView()) {
    378         // A plug-in is responsible for setting the cursor when the pointer is over it.
    379         return DoNotSetCursor;
    380     }
    381     return RenderReplaced::getCursor(point, cursor);
    382 }
    383 
    384 } // namespace WebCore
    385