1/*
2 * Copyright (C) 2012-2017 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "RenderSnapshottedPlugIn.h"
28
29#include "CachedImage.h"
30#include "Cursor.h"
31#include "EventNames.h"
32#include "Filter.h"
33#include "Frame.h"
34#include "FrameLoaderClient.h"
35#include "FrameView.h"
36#include "Gradient.h"
37#include "HTMLPlugInImageElement.h"
38#include "ImageBuffer.h"
39#include "MouseEvent.h"
40#include "Page.h"
41#include "PaintInfo.h"
42#include "Path.h"
43#include "PlatformMouseEvent.h"
44#include "RenderImageResource.h"
45#include "RenderIterator.h"
46#include "RenderView.h"
47#include <wtf/IsoMallocInlines.h>
48#include <wtf/StackStats.h>
49
50namespace WebCore {
51
52WTF_MAKE_ISO_ALLOCATED_IMPL(RenderSnapshottedPlugIn);
53
54RenderSnapshottedPlugIn::RenderSnapshottedPlugIn(HTMLPlugInImageElement& element, RenderStyle&& style)
55 : RenderEmbeddedObject(element, WTFMove(style))
56 , m_snapshotResource(std::make_unique<RenderImageResource>())
57{
58 m_snapshotResource->initialize(*this);
59}
60
61RenderSnapshottedPlugIn::~RenderSnapshottedPlugIn()
62{
63 // Do not add any code here. Add it to willBeDestroyed() instead.
64}
65
66void RenderSnapshottedPlugIn::willBeDestroyed()
67{
68 ASSERT(m_snapshotResource);
69 m_snapshotResource->shutdown();
70
71 RenderEmbeddedObject::willBeDestroyed();
72}
73
74HTMLPlugInImageElement& RenderSnapshottedPlugIn::plugInImageElement() const
75{
76 return downcast<HTMLPlugInImageElement>(RenderEmbeddedObject::frameOwnerElement());
77}
78
79void RenderSnapshottedPlugIn::layout()
80{
81 StackStats::LayoutCheckPoint layoutCheckPoint;
82 LayoutSize oldSize = contentBoxRect().size();
83
84 RenderEmbeddedObject::layout();
85
86 LayoutSize newSize = contentBoxRect().size();
87 if (newSize == oldSize)
88 return;
89
90 view().frameView().addEmbeddedObjectToUpdate(*this);
91}
92
93void RenderSnapshottedPlugIn::updateSnapshot(Image* image)
94{
95 // Zero-size plugins will have no image.
96 if (!image)
97 return;
98
99 m_snapshotResource->setCachedImage(new CachedImage(image, page().sessionID(), &page().cookieJar()));
100 repaint();
101}
102
103void RenderSnapshottedPlugIn::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
104{
105 if (paintInfo.phase == PaintPhase::Foreground && plugInImageElement().displayState() < HTMLPlugInElement::Restarting) {
106 paintSnapshot(paintInfo, paintOffset);
107 }
108
109 PaintPhase newPhase = (paintInfo.phase == PaintPhase::ChildOutlines) ? PaintPhase::Outline : paintInfo.phase;
110 newPhase = (newPhase == PaintPhase::ChildBlockBackgrounds) ? PaintPhase::ChildBlockBackground : newPhase;
111
112 PaintInfo paintInfoForChild(paintInfo);
113 paintInfoForChild.phase = newPhase;
114 paintInfoForChild.updateSubtreePaintRootForChildren(this);
115
116 for (auto& child : childrenOfType<RenderBox>(*this)) {
117 LayoutPoint childPoint = flipForWritingModeForChild(&child, paintOffset);
118 if (!child.hasSelfPaintingLayer() && !child.isFloating())
119 child.paint(paintInfoForChild, childPoint);
120 }
121
122 RenderEmbeddedObject::paint(paintInfo, paintOffset);
123}
124
125void RenderSnapshottedPlugIn::paintSnapshot(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
126{
127 Image* image = m_snapshotResource->image().get();
128 if (!image || image->isNull())
129 return;
130
131 LayoutUnit cWidth = contentWidth();
132 LayoutUnit cHeight = contentHeight();
133 if (!cWidth || !cHeight)
134 return;
135
136 GraphicsContext& context = paintInfo.context();
137
138 LayoutSize contentSize(cWidth, cHeight);
139 LayoutPoint contentLocation = location() + paintOffset;
140 contentLocation.move(borderLeft() + paddingLeft(), borderTop() + paddingTop());
141
142 LayoutRect rect(contentLocation, contentSize);
143 IntRect alignedRect = snappedIntRect(rect);
144 if (alignedRect.width() <= 0 || alignedRect.height() <= 0)
145 return;
146
147 InterpolationQuality interpolation = chooseInterpolationQuality(context, *image, image, alignedRect.size());
148 ImageOrientationDescription orientationDescription(shouldRespectImageOrientation(), style().imageOrientation());
149 context.drawImage(*image, alignedRect, ImagePaintingOptions(orientationDescription, interpolation));
150}
151
152CursorDirective RenderSnapshottedPlugIn::getCursor(const LayoutPoint& point, Cursor& overrideCursor) const
153{
154 if (plugInImageElement().displayState() < HTMLPlugInElement::Restarting) {
155 overrideCursor = handCursor();
156 return SetCursor;
157 }
158 return RenderEmbeddedObject::getCursor(point, overrideCursor);
159}
160
161void RenderSnapshottedPlugIn::handleEvent(Event& event)
162{
163 if (!is<MouseEvent>(event))
164 return;
165
166 auto& mouseEvent = downcast<MouseEvent>(event);
167
168 // If we're a snapshotted plugin, we want to make sure we activate on
169 // clicks even if the page is preventing our default behaviour. Otherwise
170 // we can never restart. One we do restart, then the page will happily
171 // block the new plugin in the normal renderer. All this means we have to
172 // be on the lookout for a mouseup event that comes after a mousedown
173 // event. The code below is not completely foolproof, but the worst that
174 // could happen is that a snapshotted plugin restarts.
175
176 if (mouseEvent.type() == eventNames().mouseoutEvent)
177 m_isPotentialMouseActivation = false;
178
179 if (mouseEvent.button() != LeftButton)
180 return;
181
182 if (mouseEvent.type() == eventNames().clickEvent || (m_isPotentialMouseActivation && mouseEvent.type() == eventNames().mouseupEvent)) {
183 m_isPotentialMouseActivation = false;
184 bool clickWasOnOverlay = plugInImageElement().partOfSnapshotOverlay(mouseEvent.target());
185 plugInImageElement().userDidClickSnapshot(mouseEvent, !clickWasOnOverlay);
186 mouseEvent.setDefaultHandled();
187 } else if (mouseEvent.type() == eventNames().mousedownEvent) {
188 m_isPotentialMouseActivation = true;
189 mouseEvent.setDefaultHandled();
190 }
191}
192
193} // namespace WebCore
194