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