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 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 "RenderView.h" 32 #include "RenderWidgetProtector.h" 33 34 using namespace std; 35 36 namespace WebCore { 37 38 static HashMap<const Widget*, RenderWidget*>& widgetRendererMap() 39 { 40 static HashMap<const Widget*, RenderWidget*>* staticWidgetRendererMap = new HashMap<const Widget*, RenderWidget*>; 41 return *staticWidgetRendererMap; 42 } 43 44 static size_t widgetHierarchyUpdateSuspendCount; 45 46 typedef HashMap<RefPtr<Widget>, FrameView*> WidgetToParentMap; 47 48 static WidgetToParentMap& widgetNewParentMap() 49 { 50 DEFINE_STATIC_LOCAL(WidgetToParentMap, map, ()); 51 return map; 52 } 53 54 void RenderWidget::suspendWidgetHierarchyUpdates() 55 { 56 widgetHierarchyUpdateSuspendCount++; 57 } 58 59 void RenderWidget::resumeWidgetHierarchyUpdates() 60 { 61 ASSERT(widgetHierarchyUpdateSuspendCount); 62 if (widgetHierarchyUpdateSuspendCount == 1) { 63 WidgetToParentMap map = widgetNewParentMap(); 64 widgetNewParentMap().clear(); 65 WidgetToParentMap::iterator end = map.end(); 66 for (WidgetToParentMap::iterator it = map.begin(); it != end; ++it) { 67 Widget* child = it->first.get(); 68 ScrollView* currentParent = child->parent(); 69 FrameView* newParent = it->second; 70 if (newParent != currentParent) { 71 if (currentParent) 72 currentParent->removeChild(child); 73 if (newParent) 74 newParent->addChild(child); 75 } 76 } 77 } 78 widgetHierarchyUpdateSuspendCount--; 79 } 80 81 static void moveWidgetToParentSoon(Widget* child, FrameView* parent) 82 { 83 if (!widgetHierarchyUpdateSuspendCount) { 84 if (parent) 85 parent->addChild(child); 86 else 87 child->removeFromParent(); 88 return; 89 } 90 widgetNewParentMap().set(child, parent); 91 } 92 93 RenderWidget::RenderWidget(Node* node) 94 : RenderReplaced(node) 95 , m_widget(0) 96 , m_frameView(node->document()->view()) 97 // Reference counting is used to prevent the widget from being 98 // destroyed while inside the Widget code, which might not be 99 // able to handle that. 100 , m_refCount(1) 101 { 102 view()->addWidget(this); 103 } 104 105 void RenderWidget::destroy() 106 { 107 // We can't call the base class's destroy because we don't 108 // want to unconditionally delete ourselves (we're ref-counted). 109 // So the code below includes copied and pasted contents of 110 // both RenderBox::destroy() and RenderObject::destroy(). 111 // Fix originally made for <rdar://problem/4228818>. 112 113 animation()->cancelAnimations(this); 114 115 if (RenderView* v = view()) 116 v->removeWidget(this); 117 118 if (m_hasCounterNodeMap) 119 RenderCounter::destroyCounterNodes(this); 120 121 if (AXObjectCache::accessibilityEnabled()) { 122 document()->axObjectCache()->childrenChanged(this->parent()); 123 document()->axObjectCache()->remove(this); 124 } 125 remove(); 126 127 setWidget(0); 128 129 // removes from override size map 130 if (hasOverrideSize()) 131 setOverrideSize(-1); 132 133 if (style() && (style()->height().isPercent() || style()->minHeight().isPercent() || style()->maxHeight().isPercent())) 134 RenderBlock::removePercentHeightDescendant(this); 135 136 if (hasLayer()) { 137 layer()->clearClipRects(); 138 setHasLayer(false); 139 destroyLayer(); 140 } 141 142 // Grab the arena from node()->document()->renderArena() before clearing the node pointer. 143 // Clear the node before deref-ing, as this may be deleted when deref is called. 144 RenderArena* arena = renderArena(); 145 setNode(0); 146 deref(arena); 147 } 148 149 RenderWidget::~RenderWidget() 150 { 151 ASSERT(m_refCount <= 0); 152 clearWidget(); 153 } 154 155 bool RenderWidget::setWidgetGeometry(const IntRect& frame) 156 { 157 ASSERT(!widgetHierarchyUpdateSuspendCount); 158 if (!node() || m_widget->frameRect() == frame) 159 return false; 160 161 RenderWidgetProtector protector(this); 162 RefPtr<Node> protectedNode(node()); 163 m_widget->setFrameRect(frame); 164 return true; 165 } 166 167 void RenderWidget::setWidget(PassRefPtr<Widget> widget) 168 { 169 if (widget == m_widget) 170 return; 171 172 if (m_widget) { 173 moveWidgetToParentSoon(m_widget.get(), 0); 174 widgetRendererMap().remove(m_widget.get()); 175 clearWidget(); 176 } 177 m_widget = widget; 178 if (m_widget) { 179 widgetRendererMap().add(m_widget.get(), this); 180 // If we've already received a layout, apply the calculated space to the 181 // widget immediately, but we have to have really been fully constructed (with a non-null 182 // style pointer). 183 if (style()) { 184 if (!needsLayout()) 185 setWidgetGeometry(absoluteContentBox()); 186 if (style()->visibility() != VISIBLE) 187 m_widget->hide(); 188 else 189 m_widget->show(); 190 } 191 moveWidgetToParentSoon(m_widget.get(), m_frameView); 192 } 193 } 194 195 void RenderWidget::layout() 196 { 197 ASSERT(needsLayout()); 198 199 setNeedsLayout(false); 200 } 201 202 void RenderWidget::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 203 { 204 RenderReplaced::styleDidChange(diff, oldStyle); 205 if (m_widget) { 206 if (style()->visibility() != VISIBLE) 207 m_widget->hide(); 208 else 209 m_widget->show(); 210 } 211 } 212 213 void RenderWidget::showSubstituteImage(PassRefPtr<Image> prpImage) 214 { 215 m_substituteImage = prpImage; 216 repaint(); 217 } 218 219 void RenderWidget::paint(PaintInfo& paintInfo, int tx, int ty) 220 { 221 if (!shouldPaint(paintInfo, tx, ty)) 222 return; 223 224 tx += x(); 225 ty += y(); 226 227 if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)) 228 paintBoxDecorations(paintInfo, tx, ty); 229 230 if (paintInfo.phase == PaintPhaseMask) { 231 paintMask(paintInfo, tx, ty); 232 return; 233 } 234 235 if (!m_frameView || paintInfo.phase != PaintPhaseForeground || style()->visibility() != VISIBLE) 236 return; 237 238 #if PLATFORM(MAC) 239 if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) 240 paintCustomHighlight(tx - x(), ty - y(), style()->highlight(), true); 241 #endif 242 243 if (style()->hasBorderRadius()) { 244 IntRect borderRect = IntRect(tx, ty, width(), height()); 245 246 if (borderRect.isEmpty()) 247 return; 248 249 // Push a clip if we have a border radius, since we want to round the foreground content that gets painted. 250 paintInfo.context->save(); 251 252 IntSize topLeft, topRight, bottomLeft, bottomRight; 253 style()->getBorderRadiiForRect(borderRect, topLeft, topRight, bottomLeft, bottomRight); 254 255 paintInfo.context->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight); 256 } 257 258 if (m_widget) { 259 // Tell the widget to paint now. This is the only time the widget is allowed 260 // to paint itself. That way it will composite properly with z-indexed layers. 261 if (m_substituteImage) 262 paintInfo.context->drawImage(m_substituteImage.get(), style()->colorSpace(), m_widget->frameRect()); 263 else { 264 IntPoint widgetLocation = m_widget->frameRect().location(); 265 IntPoint paintLocation(tx + borderLeft() + paddingLeft(), ty + borderTop() + paddingTop()); 266 IntRect paintRect = paintInfo.rect; 267 268 IntSize paintOffset = paintLocation - widgetLocation; 269 // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer, 270 // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing. 271 if (!paintOffset.isZero()) { 272 paintInfo.context->translate(paintOffset); 273 paintRect.move(-paintOffset); 274 } 275 m_widget->paint(paintInfo.context, paintRect); 276 277 if (!paintOffset.isZero()) 278 paintInfo.context->translate(-paintOffset); 279 } 280 if (m_widget->isFrameView() && paintInfo.overlapTestRequests && !static_cast<FrameView*>(m_widget.get())->useSlowRepaintsIfNotOverlapped()) { 281 ASSERT(!paintInfo.overlapTestRequests->contains(this)); 282 paintInfo.overlapTestRequests->set(this, m_widget->frameRect()); 283 } 284 } 285 286 if (style()->hasBorderRadius()) 287 paintInfo.context->restore(); 288 289 // Paint a partially transparent wash over selected widgets. 290 if (isSelected() && !document()->printing()) { 291 // FIXME: selectionRect() is in absolute, not painting coordinates. 292 paintInfo.context->fillRect(selectionRect(), selectionBackgroundColor(), style()->colorSpace()); 293 } 294 } 295 296 void RenderWidget::setOverlapTestResult(bool isOverlapped) 297 { 298 ASSERT(m_widget); 299 ASSERT(m_widget->isFrameView()); 300 static_cast<FrameView*>(m_widget.get())->setIsOverlapped(isOverlapped); 301 } 302 303 void RenderWidget::deref(RenderArena *arena) 304 { 305 if (--m_refCount <= 0) 306 arenaDelete(arena, this); 307 } 308 309 void RenderWidget::updateWidgetPosition() 310 { 311 if (!m_widget) 312 return; 313 314 // FIXME: This doesn't work correctly with transforms. 315 FloatPoint absPos = localToAbsolute(); 316 absPos.move(borderLeft() + paddingLeft(), borderTop() + paddingTop()); 317 318 int w = width() - borderLeft() - borderRight() - paddingLeft() - paddingRight(); 319 int h = height() - borderTop() - borderBottom() - paddingTop() - paddingBottom(); 320 321 bool boundsChanged = setWidgetGeometry(IntRect(absPos.x(), absPos.y(), w, h)); 322 323 #ifndef FLATTEN_IFRAME 324 // if the frame bounds got changed, or if view needs layout (possibly indicating 325 // content size is wrong) we have to do a layout to set the right widget size 326 if (m_widget->isFrameView()) { 327 FrameView* frameView = static_cast<FrameView*>(m_widget.get()); 328 if (boundsChanged || frameView->needsLayout()) 329 frameView->layout(); 330 } 331 #endif 332 } 333 334 void RenderWidget::setSelectionState(SelectionState state) 335 { 336 if (selectionState() != state) { 337 RenderReplaced::setSelectionState(state); 338 if (m_widget) 339 m_widget->setIsSelected(isSelected()); 340 } 341 } 342 343 void RenderWidget::clearWidget() 344 { 345 m_widget = 0; 346 } 347 348 RenderWidget* RenderWidget::find(const Widget* widget) 349 { 350 return widgetRendererMap().get(widget); 351 } 352 353 bool RenderWidget::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction action) 354 { 355 bool hadResult = result.innerNode(); 356 bool inside = RenderReplaced::nodeAtPoint(request, result, x, y, tx, ty, action); 357 358 // Check to see if we are really over the widget itself (and not just in the border/padding area). 359 if (inside && !hadResult && result.innerNode() == node()) 360 result.setIsOverWidget(contentBoxRect().contains(result.localPoint())); 361 return inside; 362 } 363 364 } // namespace WebCore 365