1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 2000 Simon Hausmann <hausmann@kde.org>
4 * (C) 2000 Stefan Schimanski (1Stein@gmx.de)
5 * Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010 Apple 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 "RenderEmbeddedObject.h"
26
27#include "CSSValueKeywords.h"
28#include "Chrome.h"
29#include "ChromeClient.h"
30#include "Cursor.h"
31#include "EventHandler.h"
32#include "EventNames.h"
33#include "FontCascade.h"
34#include "FontSelector.h"
35#include "Frame.h"
36#include "FrameLoaderClient.h"
37#include "GraphicsContext.h"
38#include "HTMLAppletElement.h"
39#include "HTMLEmbedElement.h"
40#include "HTMLNames.h"
41#include "HTMLObjectElement.h"
42#include "HTMLParamElement.h"
43#include "HTMLPlugInElement.h"
44#include "HitTestResult.h"
45#include "LocalizedStrings.h"
46#include "MouseEvent.h"
47#include "Page.h"
48#include "PaintInfo.h"
49#include "Path.h"
50#include "PlatformMouseEvent.h"
51#include "PluginViewBase.h"
52#include "RenderLayoutState.h"
53#include "RenderTheme.h"
54#include "RenderView.h"
55#include "Settings.h"
56#include "Text.h"
57#include "TextRun.h"
58#include <wtf/IsoMallocInlines.h>
59#include <wtf/StackStats.h>
60
61namespace WebCore {
62
63using namespace HTMLNames;
64
65WTF_MAKE_ISO_ALLOCATED_IMPL(RenderEmbeddedObject);
66
67static const float replacementTextRoundedRectHeight = 22;
68static const float replacementTextRoundedRectLeftTextMargin = 10;
69static const float replacementTextRoundedRectRightTextMargin = 10;
70static const float replacementTextRoundedRectRightTextMarginWithArrow = 5;
71static const float replacementTextRoundedRectTopTextMargin = -1;
72static const float replacementTextRoundedRectRadius = 11;
73static const float replacementArrowLeftMargin = -4;
74static const float replacementArrowPadding = 4;
75static const float replacementArrowCirclePadding = 3;
76
77static const Color& replacementTextRoundedRectPressedColor()
78{
79 static NeverDestroyed<Color> pressed(105, 105, 105, 242);
80 return pressed;
81}
82
83static const Color& replacementTextRoundedRectColor()
84{
85 static NeverDestroyed<Color> standard(125, 125, 125, 242);
86 return standard;
87}
88
89static const Color& replacementTextColor()
90{
91 static NeverDestroyed<Color> standard(240, 240, 240, 255);
92 return standard;
93}
94
95static const Color& unavailablePluginBorderColor()
96{
97 static NeverDestroyed<Color> standard(255, 255, 255, 216);
98 return standard;
99}
100
101RenderEmbeddedObject::RenderEmbeddedObject(HTMLFrameOwnerElement& element, RenderStyle&& style)
102 : RenderWidget(element, WTFMove(style))
103 , m_isPluginUnavailable(false)
104 , m_unavailablePluginIndicatorIsPressed(false)
105 , m_mouseDownWasInUnavailablePluginIndicator(false)
106{
107 // Actual size is not known yet, report the default intrinsic size.
108 view().frameView().incrementVisuallyNonEmptyPixelCount(roundedIntSize(intrinsicSize()));
109}
110
111RenderEmbeddedObject::~RenderEmbeddedObject()
112{
113 // Do not add any code here. Add it to willBeDestroyed() instead.
114}
115
116void RenderEmbeddedObject::willBeDestroyed()
117{
118 view().frameView().removeEmbeddedObjectToUpdate(*this);
119 RenderWidget::willBeDestroyed();
120}
121
122RenderPtr<RenderEmbeddedObject> RenderEmbeddedObject::createForApplet(HTMLAppletElement& applet, RenderStyle&& style)
123{
124 auto renderer = createRenderer<RenderEmbeddedObject>(applet, WTFMove(style));
125 renderer->setInline(true);
126 return renderer;
127}
128
129bool RenderEmbeddedObject::requiresLayer() const
130{
131 if (RenderWidget::requiresLayer())
132 return true;
133
134 return allowsAcceleratedCompositing();
135}
136
137bool RenderEmbeddedObject::allowsAcceleratedCompositing() const
138{
139#if PLATFORM(IOS_FAMILY)
140 // The timing of layer creation is different on the phone, since the plugin can only be manipulated from the main thread.
141 return is<PluginViewBase>(widget()) && downcast<PluginViewBase>(*widget()).willProvidePluginLayer();
142#else
143 return is<PluginViewBase>(widget()) && downcast<PluginViewBase>(*widget()).platformLayer();
144#endif
145}
146
147#if !PLATFORM(IOS_FAMILY)
148static String unavailablePluginReplacementText(RenderEmbeddedObject::PluginUnavailabilityReason pluginUnavailabilityReason)
149{
150 switch (pluginUnavailabilityReason) {
151 case RenderEmbeddedObject::PluginMissing:
152 return missingPluginText();
153 case RenderEmbeddedObject::PluginCrashed:
154 return crashedPluginText();
155 case RenderEmbeddedObject::PluginBlockedByContentSecurityPolicy:
156 return blockedPluginByContentSecurityPolicyText();
157 case RenderEmbeddedObject::InsecurePluginVersion:
158 return insecurePluginVersionText();
159 case RenderEmbeddedObject::UnsupportedPlugin:
160 return unsupportedPluginText();
161 case RenderEmbeddedObject::PluginTooSmall:
162 return pluginTooSmallText();
163 }
164
165 ASSERT_NOT_REACHED();
166 return String();
167}
168#endif
169
170static bool shouldUnavailablePluginMessageBeButton(Page& page, RenderEmbeddedObject::PluginUnavailabilityReason pluginUnavailabilityReason)
171{
172 return page.chrome().client().shouldUnavailablePluginMessageBeButton(pluginUnavailabilityReason);
173}
174
175void RenderEmbeddedObject::setPluginUnavailabilityReason(PluginUnavailabilityReason pluginUnavailabilityReason)
176{
177#if PLATFORM(IOS_FAMILY)
178 UNUSED_PARAM(pluginUnavailabilityReason);
179#else
180 setPluginUnavailabilityReasonWithDescription(pluginUnavailabilityReason, unavailablePluginReplacementText(pluginUnavailabilityReason));
181#endif
182}
183
184void RenderEmbeddedObject::setPluginUnavailabilityReasonWithDescription(PluginUnavailabilityReason pluginUnavailabilityReason, const String& description)
185{
186#if PLATFORM(IOS_FAMILY)
187 UNUSED_PARAM(pluginUnavailabilityReason);
188 UNUSED_PARAM(description);
189#else
190 ASSERT(!m_isPluginUnavailable);
191 m_isPluginUnavailable = true;
192 m_pluginUnavailabilityReason = pluginUnavailabilityReason;
193
194 if (description.isEmpty())
195 m_unavailablePluginReplacementText = unavailablePluginReplacementText(pluginUnavailabilityReason);
196 else
197 m_unavailablePluginReplacementText = description;
198#endif
199}
200
201void RenderEmbeddedObject::setUnavailablePluginIndicatorIsPressed(bool pressed)
202{
203 if (m_unavailablePluginIndicatorIsPressed == pressed)
204 return;
205 m_unavailablePluginIndicatorIsPressed = pressed;
206 repaint();
207}
208
209void RenderEmbeddedObject::paintSnapshotImage(PaintInfo& paintInfo, const LayoutPoint& paintOffset, Image& image)
210{
211 LayoutUnit cWidth = contentWidth();
212 LayoutUnit cHeight = contentHeight();
213 if (!cWidth || !cHeight)
214 return;
215
216 GraphicsContext& context = paintInfo.context();
217 LayoutSize contentSize(cWidth, cHeight);
218 LayoutPoint contentLocation = location() + paintOffset;
219 contentLocation.move(borderLeft() + paddingLeft(), borderTop() + paddingTop());
220
221 LayoutRect rect(contentLocation, contentSize);
222 IntRect alignedRect = snappedIntRect(rect);
223 if (alignedRect.width() <= 0 || alignedRect.height() <= 0)
224 return;
225
226 InterpolationQuality interpolation = chooseInterpolationQuality(context, image, &image, alignedRect.size());
227 ImageOrientationDescription orientationDescription(shouldRespectImageOrientation(), style().imageOrientation());
228 context.drawImage(image, alignedRect, ImagePaintingOptions(orientationDescription, interpolation));
229}
230
231void RenderEmbeddedObject::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
232{
233 if (!is<HTMLPlugInElement>(frameOwnerElement()))
234 return;
235
236 HTMLPlugInElement& plugInElement = downcast<HTMLPlugInElement>(frameOwnerElement());
237
238 if (plugInElement.displayState() > HTMLPlugInElement::DisplayingSnapshot) {
239 RenderWidget::paintContents(paintInfo, paintOffset);
240 if (!plugInElement.isRestartedPlugin())
241 return;
242 }
243
244 if (!is<HTMLPlugInImageElement>(plugInElement))
245 return;
246
247 if (Image* snapshot = downcast<HTMLPlugInImageElement>(plugInElement).snapshotImage())
248 paintSnapshotImage(paintInfo, paintOffset, *snapshot);
249}
250
251void RenderEmbeddedObject::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
252{
253 // The relevant repainted object heuristic is not tuned for plugin documents.
254 bool countsTowardsRelevantObjects = !document().isPluginDocument() && paintInfo.phase == PaintPhase::Foreground;
255
256 if (isPluginUnavailable()) {
257 if (countsTowardsRelevantObjects)
258 page().addRelevantUnpaintedObject(this, visualOverflowRect());
259 RenderReplaced::paint(paintInfo, paintOffset);
260 return;
261 }
262
263 if (countsTowardsRelevantObjects)
264 page().addRelevantRepaintedObject(this, visualOverflowRect());
265
266 RenderWidget::paint(paintInfo, paintOffset);
267}
268
269static void drawReplacementArrow(GraphicsContext& context, const FloatRect& insideRect)
270{
271 GraphicsContextStateSaver stateSaver(context);
272
273 FloatRect rect(insideRect);
274 rect.inflate(-replacementArrowPadding);
275
276 FloatPoint center(rect.center());
277 FloatPoint arrowTip(rect.maxX(), center.y());
278
279 context.setStrokeThickness(2);
280 context.setLineCap(RoundCap);
281 context.setLineJoin(RoundJoin);
282
283 Path path;
284 path.moveTo(FloatPoint(rect.x(), center.y()));
285 path.addLineTo(arrowTip);
286 path.addLineTo(FloatPoint(center.x(), rect.y()));
287 path.moveTo(arrowTip);
288 path.addLineTo(FloatPoint(center.x(), rect.maxY()));
289 context.strokePath(path);
290}
291
292void RenderEmbeddedObject::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
293{
294 if (!showsUnavailablePluginIndicator())
295 return;
296
297 if (paintInfo.phase == PaintPhase::Selection)
298 return;
299
300 GraphicsContext& context = paintInfo.context();
301 if (context.paintingDisabled())
302 return;
303
304 FloatRect contentRect;
305 FloatRect indicatorRect;
306 FloatRect replacementTextRect;
307 FloatRect arrowRect;
308 FontCascade font;
309 TextRun run(emptyString());
310 float textWidth;
311 getReplacementTextGeometry(paintOffset, contentRect, indicatorRect, replacementTextRect, arrowRect, font, run, textWidth);
312
313 Path background;
314 background.addRoundedRect(indicatorRect, FloatSize(replacementTextRoundedRectRadius, replacementTextRoundedRectRadius));
315
316 GraphicsContextStateSaver stateSaver(context);
317 context.clip(contentRect);
318 context.setFillColor(m_unavailablePluginIndicatorIsPressed ? replacementTextRoundedRectPressedColor() : replacementTextRoundedRectColor());
319 context.fillPath(background);
320
321 Path strokePath;
322 FloatRect strokeRect(indicatorRect);
323 strokeRect.inflate(1);
324 strokePath.addRoundedRect(strokeRect, FloatSize(replacementTextRoundedRectRadius + 1, replacementTextRoundedRectRadius + 1));
325
326 context.setStrokeColor(unavailablePluginBorderColor());
327 context.setStrokeThickness(2);
328 context.strokePath(strokePath);
329
330 const FontMetrics& fontMetrics = font.fontMetrics();
331 float labelX = roundf(replacementTextRect.location().x() + replacementTextRoundedRectLeftTextMargin);
332 float labelY = roundf(replacementTextRect.location().y() + (replacementTextRect.size().height() - fontMetrics.height()) / 2 + fontMetrics.ascent() + replacementTextRoundedRectTopTextMargin);
333 context.setFillColor(replacementTextColor());
334 context.drawBidiText(font, run, FloatPoint(labelX, labelY));
335
336 if (shouldUnavailablePluginMessageBeButton(page(), m_pluginUnavailabilityReason)) {
337 arrowRect.inflate(-replacementArrowCirclePadding);
338
339 context.beginTransparencyLayer(1.0);
340 context.setFillColor(replacementTextColor());
341 context.fillEllipse(arrowRect);
342
343 context.setCompositeOperation(CompositeClear);
344 drawReplacementArrow(context, arrowRect);
345 context.endTransparencyLayer();
346 }
347}
348
349void RenderEmbeddedObject::setUnavailablePluginIndicatorIsHidden(bool hidden)
350{
351 auto newState = hidden ? UnavailablePluginIndicatorState::Hidden : UnavailablePluginIndicatorState::Visible;
352 if (m_isUnavailablePluginIndicatorState == newState)
353 return;
354 m_isUnavailablePluginIndicatorState = newState;
355 repaint();
356}
357
358LayoutRect RenderEmbeddedObject::getReplacementTextGeometry(const LayoutPoint& accumulatedOffset) const
359{
360 FloatRect contentRect;
361 FloatRect indicatorRect;
362 FloatRect replacementTextRect;
363 FloatRect arrowRect;
364 FontCascade font;
365 TextRun run(emptyString());
366 float textWidth;
367 getReplacementTextGeometry(accumulatedOffset, contentRect, indicatorRect, replacementTextRect, arrowRect, font, run, textWidth);
368 return LayoutRect(indicatorRect);
369}
370
371void RenderEmbeddedObject::getReplacementTextGeometry(const LayoutPoint& accumulatedOffset, FloatRect& contentRect, FloatRect& indicatorRect, FloatRect& replacementTextRect, FloatRect& arrowRect, FontCascade& font, TextRun& run, float& textWidth) const
372{
373 bool includesArrow = shouldUnavailablePluginMessageBeButton(page(), m_pluginUnavailabilityReason);
374
375 contentRect = contentBoxRect();
376 contentRect.moveBy(roundedIntPoint(accumulatedOffset));
377
378 FontCascadeDescription fontDescription;
379 RenderTheme::singleton().systemFont(CSSValueWebkitSmallControl, fontDescription);
380 fontDescription.setWeight(boldWeightValue());
381 fontDescription.setRenderingMode(settings().fontRenderingMode());
382 fontDescription.setComputedSize(12);
383 font = FontCascade(WTFMove(fontDescription), 0, 0);
384 font.update(0);
385
386 run = TextRun(m_unavailablePluginReplacementText);
387 textWidth = font.width(run);
388
389 replacementTextRect.setSize(FloatSize(textWidth + replacementTextRoundedRectLeftTextMargin + (includesArrow ? replacementTextRoundedRectRightTextMarginWithArrow : replacementTextRoundedRectRightTextMargin), replacementTextRoundedRectHeight));
390 replacementTextRect.setLocation(contentRect.location() + (contentRect.size() / 2 - replacementTextRect.size() / 2));
391
392 indicatorRect = replacementTextRect;
393
394 // Expand the background rect to include the arrow, if it will be used.
395 if (includesArrow) {
396 arrowRect = indicatorRect;
397 arrowRect.setX(ceilf(arrowRect.maxX() + replacementArrowLeftMargin));
398 arrowRect.setWidth(arrowRect.height());
399 indicatorRect.unite(arrowRect);
400 }
401}
402
403LayoutRect RenderEmbeddedObject::unavailablePluginIndicatorBounds(const LayoutPoint& accumulatedOffset) const
404{
405 return getReplacementTextGeometry(accumulatedOffset);
406}
407
408void RenderEmbeddedObject::layout()
409{
410 StackStats::LayoutCheckPoint layoutCheckPoint;
411 ASSERT(needsLayout());
412
413 LayoutSize oldSize = contentBoxRect().size();
414
415 updateLogicalWidth();
416 updateLogicalHeight();
417
418 RenderWidget::layout();
419
420 clearOverflow();
421 addVisualEffectOverflow();
422
423 updateLayerTransform();
424
425 bool wasMissingWidget = false;
426 if (!widget() && canHaveWidget()) {
427 wasMissingWidget = true;
428 view().frameView().addEmbeddedObjectToUpdate(*this);
429 }
430
431 clearNeedsLayout();
432
433 LayoutSize newSize = contentBoxRect().size();
434
435 if (!wasMissingWidget && newSize.width() >= oldSize.width() && newSize.height() >= oldSize.height()) {
436 HTMLFrameOwnerElement& element = frameOwnerElement();
437 if (is<HTMLPlugInImageElement>(element)) {
438 HTMLPlugInImageElement& plugInImageElement = downcast<HTMLPlugInImageElement>(element);
439 if (plugInImageElement.displayState() > HTMLPlugInElement::DisplayingSnapshot && plugInImageElement.snapshotDecision() == HTMLPlugInImageElement::MaySnapshotWhenResized) {
440 plugInImageElement.setNeedsCheckForSizeChange();
441 view().frameView().addEmbeddedObjectToUpdate(*this);
442 }
443 }
444 }
445
446 if (!canHaveChildren())
447 return;
448
449 // This code copied from RenderMedia::layout().
450 RenderObject* child = firstChild();
451
452 if (!child)
453 return;
454
455 auto& childBox = downcast<RenderBox>(*child);
456
457 if (newSize == oldSize && !childBox.needsLayout())
458 return;
459
460 // When calling layout() on a child node, a parent must either push a LayoutStateMaintainter, or
461 // instantiate LayoutStateDisabler. Since using a LayoutStateMaintainer is slightly more efficient,
462 // and this method will be called many times per second during playback, use a LayoutStateMaintainer:
463 LayoutStateMaintainer statePusher(*this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode());
464
465 childBox.setLocation(LayoutPoint(borderLeft(), borderTop()) + LayoutSize(paddingLeft(), paddingTop()));
466 childBox.mutableStyle().setHeight(Length(newSize.height(), Fixed));
467 childBox.mutableStyle().setWidth(Length(newSize.width(), Fixed));
468 childBox.setNeedsLayout(MarkOnlyThis);
469 childBox.layout();
470 clearChildNeedsLayout();
471}
472
473bool RenderEmbeddedObject::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
474{
475 if (!RenderWidget::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction))
476 return false;
477
478 if (!is<PluginViewBase>(widget()))
479 return true;
480
481 PluginViewBase& view = downcast<PluginViewBase>(*widget());
482 IntPoint roundedPoint = locationInContainer.roundedPoint();
483
484 if (Scrollbar* horizontalScrollbar = view.horizontalScrollbar()) {
485 if (horizontalScrollbar->shouldParticipateInHitTesting() && horizontalScrollbar->frameRect().contains(roundedPoint)) {
486 result.setScrollbar(horizontalScrollbar);
487 return true;
488 }
489 }
490
491 if (Scrollbar* verticalScrollbar = view.verticalScrollbar()) {
492 if (verticalScrollbar->shouldParticipateInHitTesting() && verticalScrollbar->frameRect().contains(roundedPoint)) {
493 result.setScrollbar(verticalScrollbar);
494 return true;
495 }
496 }
497
498 return true;
499}
500
501bool RenderEmbeddedObject::scroll(ScrollDirection direction, ScrollGranularity granularity, float, Element**, RenderBox*, const IntPoint&)
502{
503 if (!is<PluginViewBase>(widget()))
504 return false;
505
506 return downcast<PluginViewBase>(*widget()).scroll(direction, granularity);
507}
508
509bool RenderEmbeddedObject::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity, float multiplier, Element** stopElement)
510{
511 // Plugins don't expose a writing direction, so assuming horizontal LTR.
512 return scroll(logicalToPhysical(direction, true, false), granularity, multiplier, stopElement);
513}
514
515
516bool RenderEmbeddedObject::isInUnavailablePluginIndicator(const FloatPoint& point) const
517{
518 return getReplacementTextGeometry(LayoutPoint()).contains(LayoutPoint(point));
519}
520
521bool RenderEmbeddedObject::isInUnavailablePluginIndicator(const MouseEvent& event) const
522{
523 return isInUnavailablePluginIndicator(absoluteToLocal(event.absoluteLocation(), UseTransforms));
524}
525
526void RenderEmbeddedObject::handleUnavailablePluginIndicatorEvent(Event* event)
527{
528 if (!shouldUnavailablePluginMessageBeButton(page(), m_pluginUnavailabilityReason))
529 return;
530
531 if (!is<MouseEvent>(*event))
532 return;
533
534 MouseEvent& mouseEvent = downcast<MouseEvent>(*event);
535 HTMLPlugInElement& element = downcast<HTMLPlugInElement>(frameOwnerElement());
536 if (mouseEvent.type() == eventNames().mousedownEvent && mouseEvent.button() == LeftButton) {
537 m_mouseDownWasInUnavailablePluginIndicator = isInUnavailablePluginIndicator(mouseEvent);
538 if (m_mouseDownWasInUnavailablePluginIndicator) {
539 frame().eventHandler().setCapturingMouseEventsElement(&element);
540 element.setIsCapturingMouseEvents(true);
541 setUnavailablePluginIndicatorIsPressed(true);
542 }
543 mouseEvent.setDefaultHandled();
544 }
545 if (mouseEvent.type() == eventNames().mouseupEvent && mouseEvent.button() == LeftButton) {
546 if (m_unavailablePluginIndicatorIsPressed) {
547 frame().eventHandler().setCapturingMouseEventsElement(nullptr);
548 element.setIsCapturingMouseEvents(false);
549 setUnavailablePluginIndicatorIsPressed(false);
550 }
551 if (m_mouseDownWasInUnavailablePluginIndicator && isInUnavailablePluginIndicator(mouseEvent)) {
552 page().chrome().client().unavailablePluginButtonClicked(element, m_pluginUnavailabilityReason);
553 }
554 m_mouseDownWasInUnavailablePluginIndicator = false;
555 event->setDefaultHandled();
556 }
557 if (mouseEvent.type() == eventNames().mousemoveEvent) {
558 setUnavailablePluginIndicatorIsPressed(m_mouseDownWasInUnavailablePluginIndicator && isInUnavailablePluginIndicator(mouseEvent));
559 mouseEvent.setDefaultHandled();
560 }
561}
562
563CursorDirective RenderEmbeddedObject::getCursor(const LayoutPoint& point, Cursor& cursor) const
564{
565 if (showsUnavailablePluginIndicator() && shouldUnavailablePluginMessageBeButton(page(), m_pluginUnavailabilityReason) && isInUnavailablePluginIndicator(point)) {
566 cursor = handCursor();
567 return SetCursor;
568 }
569 if (widget() && widget()->isPluginViewBase()) {
570 // A plug-in is responsible for setting the cursor when the pointer is over it.
571 return DoNotSetCursor;
572 }
573 return RenderWidget::getCursor(point, cursor);
574}
575
576bool RenderEmbeddedObject::canHaveChildren() const
577{
578 if (isSnapshottedPlugIn())
579 return true;
580
581 return false;
582}
583
584}
585