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 48 namespace blink { 49 50 using namespace HTMLNames; 51 52 inline HTMLVideoElement::HTMLVideoElement(Document& document) 53 : HTMLMediaElement(videoTag, document) 54 { 55 if (document.settings()) 56 m_defaultPosterURL = AtomicString(document.settings()->defaultVideoPosterURL()); 57 } 58 59 PassRefPtrWillBeRawPtr<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 67 void HTMLVideoElement::trace(Visitor* visitor) 68 { 69 visitor->trace(m_imageLoader); 70 HTMLMediaElement::trace(visitor); 71 } 72 73 bool HTMLVideoElement::rendererIsNeeded(const RenderStyle& style) 74 { 75 return HTMLElement::rendererIsNeeded(style); 76 } 77 78 RenderObject* HTMLVideoElement::createRenderer(RenderStyle*) 79 { 80 return new RenderVideo(this); 81 } 82 83 void 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 97 void 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 107 bool HTMLVideoElement::isPresentationAttribute(const QualifiedName& name) const 108 { 109 if (name == widthAttr || name == heightAttr) 110 return true; 111 return HTMLMediaElement::isPresentationAttribute(name); 112 } 113 114 void 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 136 bool HTMLVideoElement::supportsFullscreen() const 137 { 138 if (!document().page()) 139 return false; 140 141 if (!webMediaPlayer()) 142 return false; 143 144 return true; 145 } 146 147 unsigned HTMLVideoElement::videoWidth() const 148 { 149 if (!webMediaPlayer()) 150 return 0; 151 return webMediaPlayer()->naturalSize().width; 152 } 153 154 unsigned HTMLVideoElement::videoHeight() const 155 { 156 if (!webMediaPlayer()) 157 return 0; 158 return webMediaPlayer()->naturalSize().height; 159 } 160 161 bool HTMLVideoElement::isURLAttribute(const Attribute& attribute) const 162 { 163 return attribute.name() == posterAttr || HTMLMediaElement::isURLAttribute(attribute); 164 } 165 166 const 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 174 void 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 194 void HTMLVideoElement::updateDisplayState() 195 { 196 if (posterImageURL().isEmpty()) 197 setDisplayMode(Video); 198 else if (displayMode() < Poster) 199 setDisplayMode(Poster); 200 } 201 202 void 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 212 bool 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 223 bool HTMLVideoElement::hasAvailableVideoFrame() const 224 { 225 if (!webMediaPlayer()) 226 return false; 227 228 return webMediaPlayer()->hasVideo() && webMediaPlayer()->readyState() >= blink::WebMediaPlayer::ReadyStateHaveCurrentData; 229 } 230 231 void 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 244 void HTMLVideoElement::webkitExitFullscreen() 245 { 246 if (isFullscreen()) 247 exitFullscreen(); 248 } 249 250 bool HTMLVideoElement::webkitSupportsFullscreen() 251 { 252 return supportsFullscreen(); 253 } 254 255 bool HTMLVideoElement::webkitDisplayingFullscreen() 256 { 257 return isFullscreen(); 258 } 259 260 void HTMLVideoElement::didMoveToNewDocument(Document& oldDocument) 261 { 262 if (m_imageLoader) 263 m_imageLoader->elementDidMoveToNewDocument(); 264 HTMLMediaElement::didMoveToNewDocument(oldDocument); 265 } 266 267 unsigned HTMLVideoElement::webkitDecodedFrameCount() const 268 { 269 if (!webMediaPlayer()) 270 return 0; 271 272 return webMediaPlayer()->decodedFrameCount(); 273 } 274 275 unsigned HTMLVideoElement::webkitDroppedFrameCount() const 276 { 277 if (!webMediaPlayer()) 278 return 0; 279 280 return webMediaPlayer()->droppedFrameCount(); 281 } 282 283 KURL HTMLVideoElement::posterImageURL() const 284 { 285 String url = stripLeadingAndTrailingHTMLSpaces(imageSourceURL()); 286 if (url.isEmpty()) 287 return KURL(); 288 return document().completeURL(url); 289 } 290 291 KURL HTMLVideoElement::mediaPlayerPosterURL() 292 { 293 return posterImageURL(); 294 } 295 296 PassRefPtr<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 316 bool HTMLVideoElement::wouldTaintOrigin(SecurityOrigin* destinationSecurityOrigin) const 317 { 318 return !hasSingleSecurityOrigin() || (!(webMediaPlayer() && webMediaPlayer()->didPassCORSAccessCheck()) && destinationSecurityOrigin->taintsCanvas(currentSrc())); 319 } 320 321 FloatSize HTMLVideoElement::sourceSize() const 322 { 323 return FloatSize(videoWidth(), videoHeight()); 324 } 325 326 } 327