a2168a583a40254bb2da87a8b70b0953bad467fa
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / html / HTMLVideoElement.cpp
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 "bindings/core/v8/ExceptionState.h"
30 #include "core/CSSPropertyNames.h"
31 #include "core/HTMLNames.h"
32 #include "core/dom/Attribute.h"
33 #include "core/dom/Document.h"
34 #include "core/dom/ExceptionCode.h"
35 #include "core/dom/shadow/ShadowRoot.h"
36 #include "core/frame/Settings.h"
37 #include "core/html/HTMLImageLoader.h"
38 #include "core/html/canvas/CanvasRenderingContext.h"
39 #include "core/html/parser/HTMLParserIdioms.h"
40 #include "core/rendering/RenderImage.h"
41 #include "core/rendering/RenderVideo.h"
42 #include "platform/UserGestureIndicator.h"
43 #include "platform/graphics/GraphicsContext.h"
44 #include "platform/graphics/ImageBuffer.h"
45 #include "platform/graphics/gpu/Extensions3DUtil.h"
46 #include "public/platform/WebCanvas.h"
47 #include "public/platform/WebGraphicsContext3D.h"
48
49 namespace blink {
50
51 using namespace HTMLNames;
52
53 inline HTMLVideoElement::HTMLVideoElement(Document& document)
54     : HTMLMediaElement(videoTag, document)
55 {
56     if (document.settings())
57         m_defaultPosterURL = AtomicString(document.settings()->defaultVideoPosterURL());
58 }
59
60 PassRefPtrWillBeRawPtr<HTMLVideoElement> HTMLVideoElement::create(Document& document)
61 {
62     RefPtrWillBeRawPtr<HTMLVideoElement> video = adoptRefWillBeNoop(new HTMLVideoElement(document));
63     video->ensureUserAgentShadowRoot();
64     video->suspendIfNeeded();
65     return video.release();
66 }
67
68 void HTMLVideoElement::trace(Visitor* visitor)
69 {
70     visitor->trace(m_imageLoader);
71     HTMLMediaElement::trace(visitor);
72 }
73
74 bool HTMLVideoElement::rendererIsNeeded(const RenderStyle& style)
75 {
76     return HTMLElement::rendererIsNeeded(style);
77 }
78
79 RenderObject* HTMLVideoElement::createRenderer(RenderStyle*)
80 {
81     return new RenderVideo(this);
82 }
83
84 void HTMLVideoElement::attach(const AttachContext& context)
85 {
86     HTMLMediaElement::attach(context);
87
88     updateDisplayState();
89     if (shouldDisplayPosterImage()) {
90         if (!m_imageLoader)
91             m_imageLoader = HTMLImageLoader::create(this);
92         m_imageLoader->updateFromElement();
93         if (renderer())
94             toRenderImage(renderer())->imageResource()->setImageResource(m_imageLoader->image());
95     }
96 }
97
98 void HTMLVideoElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
99 {
100     if (name == widthAttr)
101         addHTMLLengthToStyle(style, CSSPropertyWidth, value);
102     else if (name == heightAttr)
103         addHTMLLengthToStyle(style, CSSPropertyHeight, value);
104     else
105         HTMLMediaElement::collectStyleForPresentationAttribute(name, value, style);
106 }
107
108 bool HTMLVideoElement::isPresentationAttribute(const QualifiedName& name) const
109 {
110     if (name == widthAttr || name == heightAttr)
111         return true;
112     return HTMLMediaElement::isPresentationAttribute(name);
113 }
114
115 void HTMLVideoElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
116 {
117     if (name == posterAttr) {
118         // Force a poster recalc by setting m_displayMode to Unknown directly before calling updateDisplayState.
119         HTMLMediaElement::setDisplayMode(Unknown);
120         updateDisplayState();
121         if (shouldDisplayPosterImage()) {
122             if (!m_imageLoader)
123                 m_imageLoader = HTMLImageLoader::create(this);
124             m_imageLoader->updateFromElement(ImageLoader::UpdateIgnorePreviousError);
125         } else {
126             if (renderer())
127                 toRenderImage(renderer())->imageResource()->setImageResource(0);
128         }
129         // Notify the player when the poster image URL changes.
130         if (webMediaPlayer())
131             webMediaPlayer()->setPoster(posterImageURL());
132     } else {
133         HTMLMediaElement::parseAttribute(name, value);
134     }
135 }
136
137 bool HTMLVideoElement::supportsFullscreen() const
138 {
139     if (!document().page())
140         return false;
141
142     if (!webMediaPlayer())
143         return false;
144
145     return true;
146 }
147
148 unsigned HTMLVideoElement::videoWidth() const
149 {
150     if (!webMediaPlayer())
151         return 0;
152     return webMediaPlayer()->naturalSize().width;
153 }
154
155 unsigned HTMLVideoElement::videoHeight() const
156 {
157     if (!webMediaPlayer())
158         return 0;
159     return webMediaPlayer()->naturalSize().height;
160 }
161
162 bool HTMLVideoElement::isURLAttribute(const Attribute& attribute) const
163 {
164     return attribute.name() == posterAttr || HTMLMediaElement::isURLAttribute(attribute);
165 }
166
167 const AtomicString HTMLVideoElement::imageSourceURL() const
168 {
169     const AtomicString& url = getAttribute(posterAttr);
170     if (!stripLeadingAndTrailingHTMLSpaces(url).isEmpty())
171         return url;
172     return m_defaultPosterURL;
173 }
174
175 void HTMLVideoElement::setDisplayMode(DisplayMode mode)
176 {
177     DisplayMode oldMode = displayMode();
178     KURL poster = posterImageURL();
179
180     if (!poster.isEmpty()) {
181         // We have a poster path, but only show it until the user triggers display by playing or seeking and the
182         // media engine has something to display.
183         // Don't show the poster if there is a seek operation or
184         // the video has restarted because of loop attribute
185         if (mode == Video && oldMode == Poster && !hasAvailableVideoFrame())
186             mode = PosterWaitingForVideo;
187     }
188
189     HTMLMediaElement::setDisplayMode(mode);
190
191     if (renderer() && displayMode() != oldMode)
192         renderer()->updateFromElement();
193 }
194
195 void HTMLVideoElement::updateDisplayState()
196 {
197     if (posterImageURL().isEmpty())
198         setDisplayMode(Video);
199     else if (displayMode() < Poster)
200         setDisplayMode(Poster);
201 }
202
203 void HTMLVideoElement::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& destRect) const
204 {
205     if (!webMediaPlayer())
206         return;
207
208     WebCanvas* canvas = context->canvas();
209     SkXfermode::Mode mode = WebCoreCompositeToSkiaComposite(context->compositeOperation(), context->blendModeOperation());
210     webMediaPlayer()->paint(canvas, destRect, context->getNormalizedAlpha(), mode);
211 }
212
213 bool HTMLVideoElement::copyVideoTextureToPlatformTexture(WebGraphicsContext3D* context, Platform3DObject texture, GLint level, GLenum internalFormat, GLenum type, bool premultiplyAlpha, bool flipY)
214 {
215     if (!webMediaPlayer())
216         return false;
217
218     if (!Extensions3DUtil::canUseCopyTextureCHROMIUM(internalFormat, type, level))
219         return false;
220
221     return webMediaPlayer()->copyVideoTextureToPlatformTexture(context, texture, level, internalFormat, type, premultiplyAlpha, flipY);
222 }
223
224 bool HTMLVideoElement::hasAvailableVideoFrame() const
225 {
226     if (!webMediaPlayer())
227         return false;
228
229     return webMediaPlayer()->hasVideo() && webMediaPlayer()->readyState() >= blink::WebMediaPlayer::ReadyStateHaveCurrentData;
230 }
231
232 void HTMLVideoElement::webkitEnterFullscreen(ExceptionState& exceptionState)
233 {
234     if (isFullscreen())
235         return;
236
237     if (!supportsFullscreen()) {
238         exceptionState.throwDOMException(InvalidStateError, "This element does not support fullscreen mode.");
239         return;
240     }
241
242     enterFullscreen();
243 }
244
245 void HTMLVideoElement::webkitExitFullscreen()
246 {
247     if (isFullscreen())
248         exitFullscreen();
249 }
250
251 bool HTMLVideoElement::webkitSupportsFullscreen()
252 {
253     return supportsFullscreen();
254 }
255
256 bool HTMLVideoElement::webkitDisplayingFullscreen()
257 {
258     return isFullscreen();
259 }
260
261 void HTMLVideoElement::didMoveToNewDocument(Document& oldDocument)
262 {
263     if (m_imageLoader)
264         m_imageLoader->elementDidMoveToNewDocument();
265     HTMLMediaElement::didMoveToNewDocument(oldDocument);
266 }
267
268 unsigned HTMLVideoElement::webkitDecodedFrameCount() const
269 {
270     if (!webMediaPlayer())
271         return 0;
272
273     return webMediaPlayer()->decodedFrameCount();
274 }
275
276 unsigned HTMLVideoElement::webkitDroppedFrameCount() const
277 {
278     if (!webMediaPlayer())
279         return 0;
280
281     return webMediaPlayer()->droppedFrameCount();
282 }
283
284 KURL HTMLVideoElement::posterImageURL() const
285 {
286     String url = stripLeadingAndTrailingHTMLSpaces(imageSourceURL());
287     if (url.isEmpty())
288         return KURL();
289     return document().completeURL(url);
290 }
291
292 KURL HTMLVideoElement::mediaPlayerPosterURL()
293 {
294     return posterImageURL();
295 }
296
297 PassRefPtr<Image> HTMLVideoElement::getSourceImageForCanvas(SourceImageMode mode, SourceImageStatus* status) const
298 {
299     if (!hasAvailableVideoFrame()) {
300         *status = InvalidSourceImageStatus;
301         return nullptr;
302     }
303
304     IntSize intrinsicSize(videoWidth(), videoHeight());
305     OwnPtr<ImageBuffer> imageBuffer = ImageBuffer::create(intrinsicSize);
306     if (!imageBuffer) {
307         *status = InvalidSourceImageStatus;
308         return nullptr;
309     }
310
311     paintCurrentFrameInContext(imageBuffer->context(), IntRect(IntPoint(0, 0), intrinsicSize));
312
313     *status = NormalSourceImageStatus;
314     return imageBuffer->copyImage(mode == CopySourceImageIfVolatile ? CopyBackingStore : DontCopyBackingStore, Unscaled);
315 }
316
317 bool HTMLVideoElement::wouldTaintOrigin(SecurityOrigin* destinationSecurityOrigin) const
318 {
319     return !hasSingleSecurityOrigin() || (!(webMediaPlayer() && webMediaPlayer()->didPassCORSAccessCheck()) && destinationSecurityOrigin->taintsCanvas(currentSrc()));
320 }
321
322 FloatSize HTMLVideoElement::sourceSize() const
323 {
324     return FloatSize(videoWidth(), videoHeight());
325 }
326
327 }