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 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Library General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public License 17 * along with this library; see the file COPYING.LIB. If not, write to 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 * 21 */ 22 23 #include "config.h" 24 #include "RenderWidget.h" 25 26 #include "AXObjectCache.h" 27 #include "AnimationController.h" 28 #include "GraphicsContext.h" 29 #include "HitTestResult.h" 30 #include "RenderCounter.h" 31 #include "RenderLayer.h" 32 #include "RenderView.h" 33 #include "RenderWidgetProtector.h" 34 35 #if USE(ACCELERATED_COMPOSITING) 36 #include "RenderLayerBacking.h" 37 #endif 38 39 using namespace std; 40 41 namespace WebCore { 42 43 static HashMap<const Widget*, RenderWidget*>& widgetRendererMap() 44 { 45 static HashMap<const Widget*, RenderWidget*>* staticWidgetRendererMap = new HashMap<const Widget*, RenderWidget*>; 46 return *staticWidgetRendererMap; 47 } 48 49 static size_t widgetHierarchyUpdateSuspendCount; 50 51 typedef HashMap<RefPtr<Widget>, FrameView*> WidgetToParentMap; 52 53 static WidgetToParentMap& widgetNewParentMap() 54 { 55 DEFINE_STATIC_LOCAL(WidgetToParentMap, map, ()); 56 return map; 57 } 58 59 void RenderWidget::suspendWidgetHierarchyUpdates() 60 { 61 widgetHierarchyUpdateSuspendCount++; 62 } 63 64 void RenderWidget::resumeWidgetHierarchyUpdates() 65 { 66 ASSERT(widgetHierarchyUpdateSuspendCount); 67 if (widgetHierarchyUpdateSuspendCount == 1) { 68 WidgetToParentMap map = widgetNewParentMap(); 69 widgetNewParentMap().clear(); 70 WidgetToParentMap::iterator end = map.end(); 71 for (WidgetToParentMap::iterator it = map.begin(); it != end; ++it) { 72 Widget* child = it->first.get(); 73 ScrollView* currentParent = child->parent(); 74 FrameView* newParent = it->second; 75 if (newParent != currentParent) { 76 if (currentParent) 77 currentParent->removeChild(child); 78 if (newParent) 79 newParent->addChild(child); 80 } 81 } 82 } 83 widgetHierarchyUpdateSuspendCount--; 84 } 85 86 static void moveWidgetToParentSoon(Widget* child, FrameView* parent) 87 { 88 if (!widgetHierarchyUpdateSuspendCount) { 89 if (parent) 90 parent->addChild(child); 91 else 92 child->removeFromParent(); 93 return; 94 } 95 widgetNewParentMap().set(child, parent); 96 } 97 98 RenderWidget::RenderWidget(Node* node) 99 : RenderReplaced(node) 100 , m_widget(0) 101 , m_frameView(node->document()->view()) 102 // Reference counting is used to prevent the widget from being 103 // destroyed while inside the Widget code, which might not be 104 // able to handle that. 105 , m_refCount(1) 106 { 107 view()->addWidget(this); 108 } 109 110 void RenderWidget::destroy() 111 { 112 // We can't call the base class's destroy because we don't 113 // want to unconditionally delete ourselves (we're ref-counted). 114 // So the code below includes copied and pasted contents of 115 // both RenderBox::destroy() and RenderObject::destroy(). 116 // Fix originally made for <rdar://problem/4228818>. 117 118 animation()->cancelAnimations(this); 119 120 if (RenderView* v = view()) 121 v->removeWidget(this); 122 123 124 if (AXObjectCache::accessibilityEnabled()) { 125 document()->axObjectCache()->childrenChanged(this->parent()); 126 document()->axObjectCache()->remove(this); 127 } 128 129 if (!documentBeingDestroyed() && parent()) 130 parent()->dirtyLinesFromChangedChild(this); 131 132 remove(); 133 134 if (m_hasCounterNodeMap) 135 RenderCounter::destroyCounterNodes(this); 136 137 setWidget(0); 138 139 // removes from override size map 140 if (hasOverrideSize()) 141 setOverrideSize(-1); 142 143 if (style() && (style()->logicalHeight().isPercent() || style()->logicalMinHeight().isPercent() || style()->logicalMaxHeight().isPercent())) 144 RenderBlock::removePercentHeightDescendant(this); 145 146 if (hasLayer()) { 147 layer()->clearClipRects(); 148 setHasLayer(false); 149 destroyLayer(); 150 } 151 152 // Grab the arena from node()->document()->renderArena() before clearing the node pointer. 153 // Clear the node before deref-ing, as this may be deleted when deref is called. 154 RenderArena* arena = renderArena(); 155 setNode(0); 156 deref(arena); 157 } 158 159 RenderWidget::~RenderWidget() 160 { 161 ASSERT(m_refCount <= 0); 162 clearWidget(); 163 } 164 165 bool RenderWidget::setWidgetGeometry(const IntRect& frame, const IntSize& boundsSize) 166 { 167 if (!node()) 168 return false; 169 170 IntRect clipRect = enclosingLayer()->childrenClipRect(); 171 bool clipChanged = m_clipRect != clipRect; 172 bool boundsChanged = m_widget->frameRect() != frame; 173 174 if (!boundsChanged && !clipChanged) 175 return false; 176 177 m_clipRect = clipRect; 178 179 RenderWidgetProtector protector(this); 180 RefPtr<Node> protectedNode(node()); 181 m_widget->setFrameRect(frame); 182 if (m_widget) // setFrameRect can run arbitrary script, which might clear m_widget. 183 m_widget->setBoundsSize(boundsSize); 184 185 #if USE(ACCELERATED_COMPOSITING) 186 if (hasLayer() && layer()->isComposited()) 187 layer()->backing()->updateAfterWidgetResize(); 188 #endif 189 190 return boundsChanged; 191 } 192 193 void RenderWidget::setWidget(PassRefPtr<Widget> widget) 194 { 195 if (widget == m_widget) 196 return; 197 198 if (m_widget) { 199 moveWidgetToParentSoon(m_widget.get(), 0); 200 widgetRendererMap().remove(m_widget.get()); 201 clearWidget(); 202 } 203 m_widget = widget; 204 if (m_widget) { 205 widgetRendererMap().add(m_widget.get(), this); 206 // If we've already received a layout, apply the calculated space to the 207 // widget immediately, but we have to have really been fully constructed (with a non-null 208 // style pointer). 209 if (style()) { 210 if (!needsLayout()) 211 setWidgetGeometry(IntRect(localToAbsoluteQuad(FloatQuad(contentBoxRect())).boundingBox()), contentBoxRect().size()); 212 if (style()->visibility() != VISIBLE) 213 m_widget->hide(); 214 else { 215 m_widget->show(); 216 repaint(); 217 } 218 } 219 moveWidgetToParentSoon(m_widget.get(), m_frameView); 220 } 221 } 222 223 void RenderWidget::layout() 224 { 225 ASSERT(needsLayout()); 226 227 setNeedsLayout(false); 228 } 229 230 void RenderWidget::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 231 { 232 RenderReplaced::styleDidChange(diff, oldStyle); 233 if (m_widget) { 234 if (style()->visibility() != VISIBLE) 235 m_widget->hide(); 236 else 237 m_widget->show(); 238 } 239 } 240 241 void RenderWidget::showSubstituteImage(PassRefPtr<Image> prpImage) 242 { 243 m_substituteImage = prpImage; 244 repaint(); 245 } 246 247 void RenderWidget::notifyWidget(WidgetNotification notification) 248 { 249 if (m_widget) 250 m_widget->notifyWidget(notification); 251 } 252 253 void RenderWidget::paint(PaintInfo& paintInfo, int tx, int ty) 254 { 255 if (!shouldPaint(paintInfo, tx, ty)) 256 return; 257 258 tx += x(); 259 ty += y(); 260 261 if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)) 262 paintBoxDecorations(paintInfo, tx, ty); 263 264 if (paintInfo.phase == PaintPhaseMask) { 265 paintMask(paintInfo, tx, ty); 266 return; 267 } 268 269 if (!m_frameView || paintInfo.phase != PaintPhaseForeground || style()->visibility() != VISIBLE) 270 return; 271 272 #if PLATFORM(MAC) 273 if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) 274 paintCustomHighlight(tx - x(), ty - y(), style()->highlight(), true); 275 #endif 276 277 if (style()->hasBorderRadius()) { 278 IntRect borderRect = IntRect(tx, ty, width(), height()); 279 280 if (borderRect.isEmpty()) 281 return; 282 283 // Push a clip if we have a border radius, since we want to round the foreground content that gets painted. 284 paintInfo.context->save(); 285 paintInfo.context->addRoundedRectClip(style()->getRoundedBorderFor(borderRect)); 286 } 287 288 if (m_widget) { 289 // Tell the widget to paint now. This is the only time the widget is allowed 290 // to paint itself. That way it will composite properly with z-indexed layers. 291 if (m_substituteImage) 292 paintInfo.context->drawImage(m_substituteImage.get(), style()->colorSpace(), m_widget->frameRect()); 293 else { 294 IntPoint widgetLocation = m_widget->frameRect().location(); 295 IntPoint paintLocation(tx + borderLeft() + paddingLeft(), ty + borderTop() + paddingTop()); 296 IntRect paintRect = paintInfo.rect; 297 298 IntSize paintOffset = paintLocation - widgetLocation; 299 // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer, 300 // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing. 301 if (!paintOffset.isZero()) { 302 paintInfo.context->translate(paintOffset); 303 paintRect.move(-paintOffset); 304 } 305 m_widget->paint(paintInfo.context, paintRect); 306 307 if (!paintOffset.isZero()) 308 paintInfo.context->translate(-paintOffset); 309 } 310 311 if (m_widget->isFrameView()) { 312 FrameView* frameView = static_cast<FrameView*>(m_widget.get()); 313 bool runOverlapTests = !frameView->useSlowRepaintsIfNotOverlapped() || frameView->hasCompositedContentIncludingDescendants(); 314 if (paintInfo.overlapTestRequests && runOverlapTests) { 315 ASSERT(!paintInfo.overlapTestRequests->contains(this)); 316 paintInfo.overlapTestRequests->set(this, m_widget->frameRect()); 317 } 318 } 319 } 320 321 if (style()->hasBorderRadius()) 322 paintInfo.context->restore(); 323 324 // Paint a partially transparent wash over selected widgets. 325 if (isSelected() && !document()->printing()) { 326 // FIXME: selectionRect() is in absolute, not painting coordinates. 327 paintInfo.context->fillRect(selectionRect(), selectionBackgroundColor(), style()->colorSpace()); 328 } 329 } 330 331 void RenderWidget::setOverlapTestResult(bool isOverlapped) 332 { 333 ASSERT(m_widget); 334 ASSERT(m_widget->isFrameView()); 335 static_cast<FrameView*>(m_widget.get())->setIsOverlapped(isOverlapped); 336 } 337 338 void RenderWidget::deref(RenderArena *arena) 339 { 340 if (--m_refCount <= 0) 341 arenaDelete(arena, this); 342 } 343 344 void RenderWidget::updateWidgetPosition() 345 { 346 if (!m_widget || !node()) // Check the node in case destroy() has been called. 347 return; 348 349 IntRect contentBox = contentBoxRect(); 350 IntRect absoluteContentBox = IntRect(localToAbsoluteQuad(FloatQuad(contentBox)).boundingBox()); 351 bool boundsChanged = setWidgetGeometry(absoluteContentBox, contentBox.size()); 352 353 // if the frame bounds got changed, or if view needs layout (possibly indicating 354 // content size is wrong) we have to do a layout to set the right widget size 355 if (m_widget && m_widget->isFrameView()) { 356 FrameView* frameView = static_cast<FrameView*>(m_widget.get()); 357 // Check the frame's page to make sure that the frame isn't in the process of being destroyed. 358 if ((boundsChanged || frameView->needsLayout()) && frameView->frame()->page()) 359 frameView->layout(); 360 } 361 } 362 363 void RenderWidget::widgetPositionsUpdated() 364 { 365 if (!m_widget) 366 return; 367 m_widget->widgetPositionsUpdated(); 368 } 369 370 IntRect RenderWidget::windowClipRect() const 371 { 372 if (!m_frameView) 373 return IntRect(); 374 375 return intersection(m_frameView->contentsToWindow(m_clipRect), m_frameView->windowClipRect()); 376 } 377 378 void RenderWidget::setSelectionState(SelectionState state) 379 { 380 if (selectionState() != state) { 381 RenderReplaced::setSelectionState(state); 382 if (m_widget) 383 m_widget->setIsSelected(isSelected()); 384 } 385 } 386 387 void RenderWidget::clearWidget() 388 { 389 m_widget = 0; 390 } 391 392 RenderWidget* RenderWidget::find(const Widget* widget) 393 { 394 return widgetRendererMap().get(widget); 395 } 396 397 bool RenderWidget::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction action) 398 { 399 bool hadResult = result.innerNode(); 400 bool inside = RenderReplaced::nodeAtPoint(request, result, x, y, tx, ty, action); 401 402 // Check to see if we are really over the widget itself (and not just in the border/padding area). 403 if ((inside || result.isRectBasedTest()) && !hadResult && result.innerNode() == node()) 404 result.setIsOverWidget(contentBoxRect().contains(result.localPoint())); 405 return inside; 406 } 407 408 } // namespace WebCore 409