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 "CSSPropertyNames.h" 30 #include "HTMLNames.h" 31 #include "bindings/v8/ExceptionState.h" 32 #include "bindings/v8/ScriptController.h" 33 #include "core/dom/Attribute.h" 34 #include "core/dom/Document.h" 35 #include "core/dom/ExceptionCode.h" 36 #include "core/html/HTMLImageLoader.h" 37 #include "core/html/parser/HTMLParserIdioms.h" 38 #include "core/page/Settings.h" 39 #include "core/rendering/RenderImage.h" 40 #include "core/rendering/RenderVideo.h" 41 42 namespace WebCore { 43 44 using namespace HTMLNames; 45 46 inline HTMLVideoElement::HTMLVideoElement(const QualifiedName& tagName, Document* document, bool createdByParser) 47 : HTMLMediaElement(tagName, document, createdByParser) 48 { 49 ASSERT(hasTagName(videoTag)); 50 ScriptWrappable::init(this); 51 if (document->settings()) 52 m_defaultPosterURL = document->settings()->defaultVideoPosterURL(); 53 } 54 55 PassRefPtr<HTMLVideoElement> HTMLVideoElement::create(const QualifiedName& tagName, Document* document, bool createdByParser) 56 { 57 RefPtr<HTMLVideoElement> videoElement(adoptRef(new HTMLVideoElement(tagName, document, createdByParser))); 58 videoElement->suspendIfNeeded(); 59 return videoElement.release(); 60 } 61 62 bool HTMLVideoElement::rendererIsNeeded(const NodeRenderingContext& context) 63 { 64 return HTMLElement::rendererIsNeeded(context); 65 } 66 67 RenderObject* HTMLVideoElement::createRenderer(RenderStyle*) 68 { 69 return new RenderVideo(this); 70 } 71 72 void HTMLVideoElement::attach(const AttachContext& context) 73 { 74 HTMLMediaElement::attach(context); 75 76 updateDisplayState(); 77 if (shouldDisplayPosterImage()) { 78 if (!m_imageLoader) 79 m_imageLoader = adoptPtr(new HTMLImageLoader(this)); 80 m_imageLoader->updateFromElement(); 81 if (renderer()) 82 toRenderImage(renderer())->imageResource()->setImageResource(m_imageLoader->image()); 83 } 84 } 85 86 void HTMLVideoElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style) 87 { 88 if (name == widthAttr) 89 addHTMLLengthToStyle(style, CSSPropertyWidth, value); 90 else if (name == heightAttr) 91 addHTMLLengthToStyle(style, CSSPropertyHeight, value); 92 else 93 HTMLMediaElement::collectStyleForPresentationAttribute(name, value, style); 94 } 95 96 bool HTMLVideoElement::isPresentationAttribute(const QualifiedName& name) const 97 { 98 if (name == widthAttr || name == heightAttr) 99 return true; 100 return HTMLMediaElement::isPresentationAttribute(name); 101 } 102 103 void HTMLVideoElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 104 { 105 if (name == posterAttr) { 106 // Force a poster recalc by setting m_displayMode to Unknown directly before calling updateDisplayState. 107 HTMLMediaElement::setDisplayMode(Unknown); 108 updateDisplayState(); 109 if (shouldDisplayPosterImage()) { 110 if (!m_imageLoader) 111 m_imageLoader = adoptPtr(new HTMLImageLoader(this)); 112 m_imageLoader->updateFromElementIgnoringPreviousError(); 113 } else { 114 if (renderer()) 115 toRenderImage(renderer())->imageResource()->setImageResource(0); 116 } 117 } else 118 HTMLMediaElement::parseAttribute(name, value); 119 } 120 121 bool HTMLVideoElement::supportsFullscreen() const 122 { 123 Page* page = document() ? document()->page() : 0; 124 if (!page) 125 return false; 126 127 if (!player() || !player()->supportsFullscreen()) 128 return false; 129 130 return true; 131 } 132 133 unsigned HTMLVideoElement::videoWidth() const 134 { 135 if (!player()) 136 return 0; 137 return player()->naturalSize().width(); 138 } 139 140 unsigned HTMLVideoElement::videoHeight() const 141 { 142 if (!player()) 143 return 0; 144 return player()->naturalSize().height(); 145 } 146 147 unsigned HTMLVideoElement::width() const 148 { 149 bool ok; 150 unsigned w = getAttribute(widthAttr).string().toUInt(&ok); 151 return ok ? w : 0; 152 } 153 154 unsigned HTMLVideoElement::height() const 155 { 156 bool ok; 157 unsigned h = getAttribute(heightAttr).string().toUInt(&ok); 158 return ok ? h : 0; 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 if (mode == Video && !hasAvailableVideoFrame()) 183 mode = PosterWaitingForVideo; 184 } 185 186 HTMLMediaElement::setDisplayMode(mode); 187 188 if (renderer() && displayMode() != oldMode) 189 renderer()->updateFromElement(); 190 } 191 192 void HTMLVideoElement::updateDisplayState() 193 { 194 if (posterImageURL().isEmpty()) 195 setDisplayMode(Video); 196 else if (displayMode() < Poster) 197 setDisplayMode(Poster); 198 } 199 200 void HTMLVideoElement::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& destRect) 201 { 202 MediaPlayer* player = HTMLMediaElement::player(); 203 if (!player) 204 return; 205 player->paintCurrentFrameInContext(context, destRect); 206 } 207 208 bool HTMLVideoElement::copyVideoTextureToPlatformTexture(GraphicsContext3D* context, Platform3DObject texture, GC3Dint level, GC3Denum type, GC3Denum internalFormat, bool premultiplyAlpha, bool flipY) 209 { 210 if (!player()) 211 return false; 212 return player()->copyVideoTextureToPlatformTexture(context, texture, level, type, internalFormat, premultiplyAlpha, flipY); 213 } 214 215 bool HTMLVideoElement::hasAvailableVideoFrame() const 216 { 217 if (!player()) 218 return false; 219 220 return player()->hasVideo() && player()->readyState() >= MediaPlayer::HaveCurrentData; 221 } 222 223 void HTMLVideoElement::webkitEnterFullscreen(ExceptionState& es) 224 { 225 if (isFullscreen()) 226 return; 227 228 // Generate an exception if this isn't called in response to a user gesture, or if the 229 // element does not support fullscreen. 230 if ((userGestureRequiredForFullscreen() && !ScriptController::processingUserGesture()) || !supportsFullscreen()) { 231 es.throwDOMException(InvalidStateError); 232 return; 233 } 234 235 enterFullscreen(); 236 } 237 238 void HTMLVideoElement::webkitExitFullscreen() 239 { 240 if (isFullscreen()) 241 exitFullscreen(); 242 } 243 244 bool HTMLVideoElement::webkitSupportsFullscreen() 245 { 246 return supportsFullscreen(); 247 } 248 249 bool HTMLVideoElement::webkitDisplayingFullscreen() 250 { 251 return isFullscreen(); 252 } 253 254 void HTMLVideoElement::didMoveToNewDocument(Document* oldDocument) 255 { 256 if (m_imageLoader) 257 m_imageLoader->elementDidMoveToNewDocument(); 258 HTMLMediaElement::didMoveToNewDocument(oldDocument); 259 } 260 261 unsigned HTMLVideoElement::webkitDecodedFrameCount() const 262 { 263 if (!player()) 264 return 0; 265 266 return player()->decodedFrameCount(); 267 } 268 269 unsigned HTMLVideoElement::webkitDroppedFrameCount() const 270 { 271 if (!player()) 272 return 0; 273 274 return player()->droppedFrameCount(); 275 } 276 277 KURL HTMLVideoElement::posterImageURL() const 278 { 279 String url = stripLeadingAndTrailingHTMLSpaces(imageSourceURL()); 280 if (url.isEmpty()) 281 return KURL(); 282 return document()->completeURL(url); 283 } 284 285 } 286