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