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