1 /*
2 * Copyright (C) 2007, 2008, 2009, 2010 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 COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27 #include "core/html/HTMLVideoElement.h"
28
29 #include "CSSPropertyNames.h"
30 #include "HTMLNames.h"
31 #include "bindings/v8/ExceptionState.h"
32 #include "core/dom/Attribute.h"
33 #include "core/dom/Document.h"
34 #include "core/dom/ExceptionCode.h"
35 #include "core/html/HTMLImageLoader.h"
36 #include "core/html/parser/HTMLParserIdioms.h"
37 #include "core/frame/Settings.h"
38 #include "core/rendering/RenderImage.h"
39 #include "core/rendering/RenderVideo.h"
40 #include "platform/UserGestureIndicator.h"
41
42 namespace WebCore {
43
44 using namespace HTMLNames;
45
HTMLVideoElement(Document & document,bool createdByParser)46 inline HTMLVideoElement::HTMLVideoElement(Document& document, bool createdByParser)
47 : HTMLMediaElement(videoTag, document, createdByParser)
48 {
49 ScriptWrappable::init(this);
50 if (document.settings())
51 m_defaultPosterURL = document.settings()->defaultVideoPosterURL();
52 }
53
create(Document & document,bool createdByParser)54 PassRefPtr<HTMLVideoElement> HTMLVideoElement::create(Document& document, bool createdByParser)
55 {
56 RefPtr<HTMLVideoElement> videoElement(adoptRef(new HTMLVideoElement(document, createdByParser)));
57 videoElement->suspendIfNeeded();
58 return videoElement.release();
59 }
60
rendererIsNeeded(const RenderStyle & style)61 bool HTMLVideoElement::rendererIsNeeded(const RenderStyle& style)
62 {
63 return HTMLElement::rendererIsNeeded(style);
64 }
65
createRenderer(RenderStyle *)66 RenderObject* HTMLVideoElement::createRenderer(RenderStyle*)
67 {
68 return new RenderVideo(this);
69 }
70
attach(const AttachContext & context)71 void HTMLVideoElement::attach(const AttachContext& context)
72 {
73 HTMLMediaElement::attach(context);
74
75 updateDisplayState();
76 if (shouldDisplayPosterImage()) {
77 if (!m_imageLoader)
78 m_imageLoader = adoptPtr(new HTMLImageLoader(this));
79 m_imageLoader->updateFromElement();
80 if (renderer())
81 toRenderImage(renderer())->imageResource()->setImageResource(m_imageLoader->image());
82 }
83 }
84
collectStyleForPresentationAttribute(const QualifiedName & name,const AtomicString & value,MutableStylePropertySet * style)85 void HTMLVideoElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
86 {
87 if (name == widthAttr)
88 addHTMLLengthToStyle(style, CSSPropertyWidth, value);
89 else if (name == heightAttr)
90 addHTMLLengthToStyle(style, CSSPropertyHeight, value);
91 else
92 HTMLMediaElement::collectStyleForPresentationAttribute(name, value, style);
93 }
94
isPresentationAttribute(const QualifiedName & name) const95 bool HTMLVideoElement::isPresentationAttribute(const QualifiedName& name) const
96 {
97 if (name == widthAttr || name == heightAttr)
98 return true;
99 return HTMLMediaElement::isPresentationAttribute(name);
100 }
101
parseAttribute(const QualifiedName & name,const AtomicString & value)102 void HTMLVideoElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
103 {
104 if (name == posterAttr) {
105 // Force a poster recalc by setting m_displayMode to Unknown directly before calling updateDisplayState.
106 HTMLMediaElement::setDisplayMode(Unknown);
107 updateDisplayState();
108 if (shouldDisplayPosterImage()) {
109 if (!m_imageLoader)
110 m_imageLoader = adoptPtr(new HTMLImageLoader(this));
111 m_imageLoader->updateFromElementIgnoringPreviousError();
112 } else {
113 if (renderer())
114 toRenderImage(renderer())->imageResource()->setImageResource(0);
115 }
116 } else
117 HTMLMediaElement::parseAttribute(name, value);
118 }
119
supportsFullscreen() const120 bool HTMLVideoElement::supportsFullscreen() const
121 {
122 if (!document().page())
123 return false;
124
125 if (!player() || !player()->supportsFullscreen())
126 return false;
127
128 return true;
129 }
130
videoWidth() const131 unsigned HTMLVideoElement::videoWidth() const
132 {
133 if (!player())
134 return 0;
135 return player()->naturalSize().width();
136 }
137
videoHeight() const138 unsigned HTMLVideoElement::videoHeight() const
139 {
140 if (!player())
141 return 0;
142 return player()->naturalSize().height();
143 }
144
isURLAttribute(const Attribute & attribute) const145 bool HTMLVideoElement::isURLAttribute(const Attribute& attribute) const
146 {
147 return attribute.name() == posterAttr || HTMLMediaElement::isURLAttribute(attribute);
148 }
149
imageSourceURL() const150 const AtomicString HTMLVideoElement::imageSourceURL() const
151 {
152 const AtomicString& url = getAttribute(posterAttr);
153 if (!stripLeadingAndTrailingHTMLSpaces(url).isEmpty())
154 return url;
155 return m_defaultPosterURL;
156 }
157
setDisplayMode(DisplayMode mode)158 void HTMLVideoElement::setDisplayMode(DisplayMode mode)
159 {
160 DisplayMode oldMode = displayMode();
161 KURL poster = posterImageURL();
162
163 if (!poster.isEmpty()) {
164 // We have a poster path, but only show it until the user triggers display by playing or seeking and the
165 // media engine has something to display.
166 if (mode == Video && !hasAvailableVideoFrame())
167 mode = PosterWaitingForVideo;
168 }
169
170 HTMLMediaElement::setDisplayMode(mode);
171
172 if (renderer() && displayMode() != oldMode)
173 renderer()->updateFromElement();
174 }
175
updateDisplayState()176 void HTMLVideoElement::updateDisplayState()
177 {
178 if (posterImageURL().isEmpty())
179 setDisplayMode(Video);
180 else if (displayMode() < Poster)
181 setDisplayMode(Poster);
182 }
183
paintCurrentFrameInContext(GraphicsContext * context,const IntRect & destRect)184 void HTMLVideoElement::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& destRect)
185 {
186 MediaPlayer* player = HTMLMediaElement::player();
187 if (!player)
188 return;
189 player->paint(context, destRect);
190 }
191
copyVideoTextureToPlatformTexture(GraphicsContext3D * context,Platform3DObject texture,GC3Dint level,GC3Denum type,GC3Denum internalFormat,bool premultiplyAlpha,bool flipY)192 bool HTMLVideoElement::copyVideoTextureToPlatformTexture(GraphicsContext3D* context, Platform3DObject texture, GC3Dint level, GC3Denum type, GC3Denum internalFormat, bool premultiplyAlpha, bool flipY)
193 {
194 if (!player())
195 return false;
196 return player()->copyVideoTextureToPlatformTexture(context, texture, level, type, internalFormat, premultiplyAlpha, flipY);
197 }
198
hasAvailableVideoFrame() const199 bool HTMLVideoElement::hasAvailableVideoFrame() const
200 {
201 if (!player())
202 return false;
203
204 return player()->hasVideo() && player()->readyState() >= MediaPlayer::HaveCurrentData;
205 }
206
webkitEnterFullscreen(ExceptionState & exceptionState)207 void HTMLVideoElement::webkitEnterFullscreen(ExceptionState& exceptionState)
208 {
209 if (isFullscreen())
210 return;
211
212 // Generate an exception if this isn't called in response to a user gesture, or if the
213 // element does not support fullscreen.
214 if (userGestureRequiredForFullscreen() && !UserGestureIndicator::processingUserGesture()) {
215 exceptionState.throwDOMException(InvalidStateError, "This element may only enter fullscreen mode in response to a user gesture ('click', for example).");
216 return;
217 }
218 if (!supportsFullscreen()) {
219 exceptionState.throwDOMException(InvalidStateError, "This element does not support fullscreen mode.");
220 return;
221 }
222
223 enterFullscreen();
224 }
225
webkitExitFullscreen()226 void HTMLVideoElement::webkitExitFullscreen()
227 {
228 if (isFullscreen())
229 exitFullscreen();
230 }
231
webkitSupportsFullscreen()232 bool HTMLVideoElement::webkitSupportsFullscreen()
233 {
234 return supportsFullscreen();
235 }
236
webkitDisplayingFullscreen()237 bool HTMLVideoElement::webkitDisplayingFullscreen()
238 {
239 return isFullscreen();
240 }
241
didMoveToNewDocument(Document & oldDocument)242 void HTMLVideoElement::didMoveToNewDocument(Document& oldDocument)
243 {
244 if (m_imageLoader)
245 m_imageLoader->elementDidMoveToNewDocument();
246 HTMLMediaElement::didMoveToNewDocument(oldDocument);
247 }
248
webkitDecodedFrameCount() const249 unsigned HTMLVideoElement::webkitDecodedFrameCount() const
250 {
251 if (!player())
252 return 0;
253
254 return player()->decodedFrameCount();
255 }
256
webkitDroppedFrameCount() const257 unsigned HTMLVideoElement::webkitDroppedFrameCount() const
258 {
259 if (!player())
260 return 0;
261
262 return player()->droppedFrameCount();
263 }
264
posterImageURL() const265 KURL HTMLVideoElement::posterImageURL() const
266 {
267 String url = stripLeadingAndTrailingHTMLSpaces(imageSourceURL());
268 if (url.isEmpty())
269 return KURL();
270 return document().completeURL(url);
271 }
272
273 }
274