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#if ENABLE(VIDEO) 28#include "HTMLVideoElement.h" 29 30#include "Attribute.h" 31#include "CSSPropertyNames.h" 32#include "Chrome.h" 33#include "ChromeClient.h" 34#include "Document.h" 35#include "ExceptionCode.h" 36#include "Frame.h" 37#include "HTMLImageLoader.h" 38#include "HTMLNames.h" 39#include "HTMLParserIdioms.h" 40#include "Page.h" 41#include "RenderImage.h" 42#include "RenderVideo.h" 43#include "ScriptController.h" 44#include "Settings.h" 45 46namespace WebCore { 47 48using namespace HTMLNames; 49 50inline HTMLVideoElement::HTMLVideoElement(const QualifiedName& tagName, Document* document, bool createdByParser) 51 : HTMLMediaElement(tagName, document, createdByParser) 52{ 53 ASSERT(hasTagName(videoTag)); 54 if (document->settings()) 55 m_defaultPosterURL = document->settings()->defaultVideoPosterURL(); 56} 57 58PassRefPtr<HTMLVideoElement> HTMLVideoElement::create(const QualifiedName& tagName, Document* document, bool createdByParser) 59{ 60 RefPtr<HTMLVideoElement> videoElement(adoptRef(new HTMLVideoElement(tagName, document, createdByParser))); 61 videoElement->suspendIfNeeded(); 62 return videoElement.release(); 63} 64 65bool HTMLVideoElement::rendererIsNeeded(const NodeRenderingContext& context) 66{ 67 return HTMLElement::rendererIsNeeded(context); 68} 69 70#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) 71RenderObject* HTMLVideoElement::createRenderer(RenderArena* arena, RenderStyle*) 72{ 73 return new (arena) RenderVideo(this); 74} 75#endif 76 77void HTMLVideoElement::attach(const AttachContext& context) 78{ 79 HTMLMediaElement::attach(context); 80 81#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) 82 updateDisplayState(); 83 if (shouldDisplayPosterImage()) { 84 if (!m_imageLoader) 85 m_imageLoader = adoptPtr(new HTMLImageLoader(this)); 86 m_imageLoader->updateFromElement(); 87 if (renderer()) 88 toRenderImage(renderer())->imageResource()->setCachedImage(m_imageLoader->image()); 89 } 90#endif 91} 92 93void HTMLVideoElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style) 94{ 95 if (name == widthAttr) 96 addHTMLLengthToStyle(style, CSSPropertyWidth, value); 97 else if (name == heightAttr) 98 addHTMLLengthToStyle(style, CSSPropertyHeight, value); 99 else 100 HTMLMediaElement::collectStyleForPresentationAttribute(name, value, style); 101} 102 103bool HTMLVideoElement::isPresentationAttribute(const QualifiedName& name) const 104{ 105 if (name == widthAttr || name == heightAttr) 106 return true; 107 return HTMLMediaElement::isPresentationAttribute(name); 108} 109 110void HTMLVideoElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 111{ 112 if (name == posterAttr) { 113 // Force a poster recalc by setting m_displayMode to Unknown directly before calling updateDisplayState. 114 HTMLMediaElement::setDisplayMode(Unknown); 115 updateDisplayState(); 116#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) 117 if (shouldDisplayPosterImage()) { 118 if (!m_imageLoader) 119 m_imageLoader = adoptPtr(new HTMLImageLoader(this)); 120 m_imageLoader->updateFromElementIgnoringPreviousError(); 121 } else { 122 if (renderer()) 123 toRenderImage(renderer())->imageResource()->setCachedImage(0); 124 } 125#endif 126 } else 127 HTMLMediaElement::parseAttribute(name, value); 128} 129 130bool HTMLVideoElement::supportsFullscreen() const 131{ 132 Page* page = document() ? document()->page() : 0; 133 if (!page) 134 return false; 135 136 if (!player() || !player()->supportsFullscreen()) 137 return false; 138 139#if ENABLE(FULLSCREEN_API) 140 // If the full screen API is enabled and is supported for the current element 141 // do not require that the player has a video track to enter full screen. 142 if (page->chrome().client()->supportsFullScreenForElement(this, false)) 143 return true; 144#endif 145 146 if (!player()->hasVideo()) 147 return false; 148 149 return page->chrome().client()->supportsFullscreenForNode(this); 150} 151 152unsigned HTMLVideoElement::videoWidth() const 153{ 154 if (!player()) 155 return 0; 156 return player()->naturalSize().width(); 157} 158 159unsigned HTMLVideoElement::videoHeight() const 160{ 161 if (!player()) 162 return 0; 163 return player()->naturalSize().height(); 164} 165 166unsigned HTMLVideoElement::width() const 167{ 168 bool ok; 169 unsigned w = getAttribute(widthAttr).string().toUInt(&ok); 170 return ok ? w : 0; 171} 172 173unsigned HTMLVideoElement::height() const 174{ 175 bool ok; 176 unsigned h = getAttribute(heightAttr).string().toUInt(&ok); 177 return ok ? h : 0; 178} 179 180bool HTMLVideoElement::isURLAttribute(const Attribute& attribute) const 181{ 182 return attribute.name() == posterAttr || HTMLMediaElement::isURLAttribute(attribute); 183} 184 185const AtomicString& HTMLVideoElement::imageSourceURL() const 186{ 187 const AtomicString& url = getAttribute(posterAttr); 188 if (!stripLeadingAndTrailingHTMLSpaces(url).isEmpty()) 189 return url; 190 return m_defaultPosterURL; 191} 192 193void HTMLVideoElement::setDisplayMode(DisplayMode mode) 194{ 195 DisplayMode oldMode = displayMode(); 196 KURL poster = posterImageURL(); 197 198 if (!poster.isEmpty()) { 199 // We have a poster path, but only show it until the user triggers display by playing or seeking and the 200 // media engine has something to display. 201 if (mode == Video) { 202 if (oldMode != Video && player()) 203 player()->prepareForRendering(); 204 if (!hasAvailableVideoFrame()) 205 mode = PosterWaitingForVideo; 206 } 207 } else if (oldMode != Video && player()) 208 player()->prepareForRendering(); 209 210 HTMLMediaElement::setDisplayMode(mode); 211 212 if (player() && player()->canLoadPoster()) { 213 bool canLoad = true; 214 if (!poster.isEmpty()) { 215 Frame* frame = document()->frame(); 216 FrameLoader* loader = frame ? frame->loader() : 0; 217 canLoad = loader && loader->willLoadMediaElementURL(poster); 218 } 219 if (canLoad) 220 player()->setPoster(poster); 221 } 222 223#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) 224 if (renderer() && displayMode() != oldMode) 225 renderer()->updateFromElement(); 226#endif 227} 228 229void HTMLVideoElement::updateDisplayState() 230{ 231 if (posterImageURL().isEmpty()) 232 setDisplayMode(Video); 233 else if (displayMode() < Poster) 234 setDisplayMode(Poster); 235} 236 237void HTMLVideoElement::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& destRect) 238{ 239 MediaPlayer* player = HTMLMediaElement::player(); 240 if (!player) 241 return; 242 243 player->setVisible(true); // Make player visible or it won't draw. 244 player->paintCurrentFrameInContext(context, destRect); 245} 246 247bool HTMLVideoElement::copyVideoTextureToPlatformTexture(GraphicsContext3D* context, Platform3DObject texture, GC3Dint level, GC3Denum type, GC3Denum internalFormat, bool premultiplyAlpha, bool flipY) 248{ 249 if (!player()) 250 return false; 251 return player()->copyVideoTextureToPlatformTexture(context, texture, level, type, internalFormat, premultiplyAlpha, flipY); 252} 253 254bool HTMLVideoElement::hasAvailableVideoFrame() const 255{ 256 if (!player()) 257 return false; 258 259 return player()->hasVideo() && player()->hasAvailableVideoFrame(); 260} 261 262void HTMLVideoElement::webkitEnterFullscreen(ExceptionCode& ec) 263{ 264 if (isFullscreen()) 265 return; 266 267 // Generate an exception if this isn't called in response to a user gesture, or if the 268 // element does not support fullscreen. 269 if ((userGestureRequiredForFullscreen() && !ScriptController::processingUserGesture()) || !supportsFullscreen()) { 270 ec = INVALID_STATE_ERR; 271 return; 272 } 273 274 enterFullscreen(); 275} 276 277void HTMLVideoElement::webkitExitFullscreen() 278{ 279 if (isFullscreen()) 280 exitFullscreen(); 281} 282 283bool HTMLVideoElement::webkitSupportsFullscreen() 284{ 285 return supportsFullscreen(); 286} 287 288bool HTMLVideoElement::webkitDisplayingFullscreen() 289{ 290 return isFullscreen(); 291} 292 293void HTMLVideoElement::didMoveToNewDocument(Document* oldDocument) 294{ 295 if (m_imageLoader) 296 m_imageLoader->elementDidMoveToNewDocument(); 297 HTMLMediaElement::didMoveToNewDocument(oldDocument); 298} 299 300#if ENABLE(MEDIA_STATISTICS) 301unsigned HTMLVideoElement::webkitDecodedFrameCount() const 302{ 303 if (!player()) 304 return 0; 305 306 return player()->decodedFrameCount(); 307} 308 309unsigned HTMLVideoElement::webkitDroppedFrameCount() const 310{ 311 if (!player()) 312 return 0; 313 314 return player()->droppedFrameCount(); 315} 316#endif 317 318KURL HTMLVideoElement::posterImageURL() const 319{ 320 String url = stripLeadingAndTrailingHTMLSpaces(imageSourceURL()); 321 if (url.isEmpty()) 322 return KURL(); 323 return document()->completeURL(url); 324} 325 326} 327 328#endif 329