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