1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/generic/nsVideoFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,633 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* rendering object for the HTML <video> element */ 1.11 + 1.12 +#include "nsVideoFrame.h" 1.13 + 1.14 +#include "nsCOMPtr.h" 1.15 +#include "nsGkAtoms.h" 1.16 + 1.17 +#include "mozilla/dom/HTMLVideoElement.h" 1.18 +#include "nsIDOMHTMLVideoElement.h" 1.19 +#include "nsIDOMHTMLImageElement.h" 1.20 +#include "nsDisplayList.h" 1.21 +#include "nsGenericHTMLElement.h" 1.22 +#include "nsPresContext.h" 1.23 +#include "nsContentCreatorFunctions.h" 1.24 +#include "nsBoxLayoutState.h" 1.25 +#include "nsBoxFrame.h" 1.26 +#include "nsImageFrame.h" 1.27 +#include "nsIImageLoadingContent.h" 1.28 +#include "nsContentUtils.h" 1.29 +#include "ImageContainer.h" 1.30 +#include "ImageLayers.h" 1.31 +#include "nsContentList.h" 1.32 +#include <algorithm> 1.33 + 1.34 +using namespace mozilla; 1.35 +using namespace mozilla::layers; 1.36 +using namespace mozilla::dom; 1.37 +using namespace mozilla::gfx; 1.38 + 1.39 +nsIFrame* 1.40 +NS_NewHTMLVideoFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) 1.41 +{ 1.42 + return new (aPresShell) nsVideoFrame(aContext); 1.43 +} 1.44 + 1.45 +NS_IMPL_FRAMEARENA_HELPERS(nsVideoFrame) 1.46 + 1.47 +nsVideoFrame::nsVideoFrame(nsStyleContext* aContext) : 1.48 + nsContainerFrame(aContext) 1.49 +{ 1.50 +} 1.51 + 1.52 +nsVideoFrame::~nsVideoFrame() 1.53 +{ 1.54 +} 1.55 + 1.56 +NS_QUERYFRAME_HEAD(nsVideoFrame) 1.57 + NS_QUERYFRAME_ENTRY(nsVideoFrame) 1.58 + NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator) 1.59 +NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) 1.60 + 1.61 +nsresult 1.62 +nsVideoFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) 1.63 +{ 1.64 + nsNodeInfoManager *nodeInfoManager = GetContent()->GetCurrentDoc()->NodeInfoManager(); 1.65 + nsCOMPtr<nsINodeInfo> nodeInfo; 1.66 + Element *element; 1.67 + 1.68 + if (HasVideoElement()) { 1.69 + // Create an anonymous image element as a child to hold the poster 1.70 + // image. We may not have a poster image now, but one could be added 1.71 + // before we load, or on a subsequent load. 1.72 + nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::img, 1.73 + nullptr, 1.74 + kNameSpaceID_XHTML, 1.75 + nsIDOMNode::ELEMENT_NODE); 1.76 + NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY); 1.77 + element = NS_NewHTMLImageElement(nodeInfo.forget()); 1.78 + mPosterImage = element; 1.79 + NS_ENSURE_TRUE(mPosterImage, NS_ERROR_OUT_OF_MEMORY); 1.80 + 1.81 + // Set the nsImageLoadingContent::ImageState() to 0. This means that the 1.82 + // image will always report its state as 0, so it will never be reframed 1.83 + // to show frames for loading or the broken image icon. This is important, 1.84 + // as the image is native anonymous, and so can't be reframed (currently). 1.85 + nsCOMPtr<nsIImageLoadingContent> imgContent = do_QueryInterface(mPosterImage); 1.86 + NS_ENSURE_TRUE(imgContent, NS_ERROR_FAILURE); 1.87 + 1.88 + imgContent->ForceImageState(true, 0); 1.89 + // And now have it update its internal state 1.90 + element->UpdateState(false); 1.91 + 1.92 + UpdatePosterSource(false); 1.93 + 1.94 + if (!aElements.AppendElement(mPosterImage)) 1.95 + return NS_ERROR_OUT_OF_MEMORY; 1.96 + 1.97 + // Set up the caption overlay div for showing any TextTrack data 1.98 + nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::div, 1.99 + nullptr, 1.100 + kNameSpaceID_XHTML, 1.101 + nsIDOMNode::ELEMENT_NODE); 1.102 + NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY); 1.103 + mCaptionDiv = NS_NewHTMLDivElement(nodeInfo.forget()); 1.104 + NS_ENSURE_TRUE(mCaptionDiv, NS_ERROR_OUT_OF_MEMORY); 1.105 + nsGenericHTMLElement* div = static_cast<nsGenericHTMLElement*>(mCaptionDiv.get()); 1.106 + div->SetClassName(NS_LITERAL_STRING("caption-box")); 1.107 + 1.108 + if (!aElements.AppendElement(mCaptionDiv)) 1.109 + return NS_ERROR_OUT_OF_MEMORY; 1.110 + } 1.111 + 1.112 + // Set up "videocontrols" XUL element which will be XBL-bound to the 1.113 + // actual controls. 1.114 + nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::videocontrols, 1.115 + nullptr, 1.116 + kNameSpaceID_XUL, 1.117 + nsIDOMNode::ELEMENT_NODE); 1.118 + NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY); 1.119 + 1.120 + NS_TrustedNewXULElement(getter_AddRefs(mVideoControls), nodeInfo.forget()); 1.121 + if (!aElements.AppendElement(mVideoControls)) 1.122 + return NS_ERROR_OUT_OF_MEMORY; 1.123 + 1.124 + return NS_OK; 1.125 +} 1.126 + 1.127 +void 1.128 +nsVideoFrame::AppendAnonymousContentTo(nsBaseContentList& aElements, 1.129 + uint32_t aFliter) 1.130 +{ 1.131 + aElements.MaybeAppendElement(mPosterImage); 1.132 + aElements.MaybeAppendElement(mVideoControls); 1.133 + aElements.MaybeAppendElement(mCaptionDiv); 1.134 +} 1.135 + 1.136 +void 1.137 +nsVideoFrame::DestroyFrom(nsIFrame* aDestructRoot) 1.138 +{ 1.139 + nsContentUtils::DestroyAnonymousContent(&mCaptionDiv); 1.140 + nsContentUtils::DestroyAnonymousContent(&mVideoControls); 1.141 + nsContentUtils::DestroyAnonymousContent(&mPosterImage); 1.142 + nsContainerFrame::DestroyFrom(aDestructRoot); 1.143 +} 1.144 + 1.145 +bool 1.146 +nsVideoFrame::IsLeaf() const 1.147 +{ 1.148 + return true; 1.149 +} 1.150 + 1.151 +// Return the largest rectangle that fits in aRect and has the 1.152 +// same aspect ratio as aRatio, centered at the center of aRect 1.153 +static gfxRect 1.154 +CorrectForAspectRatio(const gfxRect& aRect, const nsIntSize& aRatio) 1.155 +{ 1.156 + NS_ASSERTION(aRatio.width > 0 && aRatio.height > 0 && !aRect.IsEmpty(), 1.157 + "Nothing to draw"); 1.158 + // Choose scale factor that scales aRatio to just fit into aRect 1.159 + gfxFloat scale = 1.160 + std::min(aRect.Width()/aRatio.width, aRect.Height()/aRatio.height); 1.161 + gfxSize scaledRatio(scale*aRatio.width, scale*aRatio.height); 1.162 + gfxPoint topLeft((aRect.Width() - scaledRatio.width)/2, 1.163 + (aRect.Height() - scaledRatio.height)/2); 1.164 + return gfxRect(aRect.TopLeft() + topLeft, scaledRatio); 1.165 +} 1.166 + 1.167 +already_AddRefed<Layer> 1.168 +nsVideoFrame::BuildLayer(nsDisplayListBuilder* aBuilder, 1.169 + LayerManager* aManager, 1.170 + nsDisplayItem* aItem, 1.171 + const ContainerLayerParameters& aContainerParameters) 1.172 +{ 1.173 + nsRect area = GetContentRect() - GetPosition() + aItem->ToReferenceFrame(); 1.174 + HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent()); 1.175 + nsIntSize videoSize; 1.176 + if (NS_FAILED(element->GetVideoSize(&videoSize)) || area.IsEmpty()) { 1.177 + return nullptr; 1.178 + } 1.179 + 1.180 + nsRefPtr<ImageContainer> container = element->GetImageContainer(); 1.181 + if (!container) 1.182 + return nullptr; 1.183 + 1.184 + // Retrieve the size of the decoded video frame, before being scaled 1.185 + // by pixel aspect ratio. 1.186 + mozilla::gfx::IntSize frameSize = container->GetCurrentSize(); 1.187 + if (frameSize.width == 0 || frameSize.height == 0) { 1.188 + // No image, or zero-sized image. No point creating a layer. 1.189 + return nullptr; 1.190 + } 1.191 + 1.192 + // Compute the rectangle in which to paint the video. We need to use 1.193 + // the largest rectangle that fills our content-box and has the 1.194 + // correct aspect ratio. 1.195 + nsPresContext* presContext = PresContext(); 1.196 + gfxRect r = gfxRect(presContext->AppUnitsToGfxUnits(area.x), 1.197 + presContext->AppUnitsToGfxUnits(area.y), 1.198 + presContext->AppUnitsToGfxUnits(area.width), 1.199 + presContext->AppUnitsToGfxUnits(area.height)); 1.200 + r = CorrectForAspectRatio(r, videoSize); 1.201 + r.Round(); 1.202 + if (r.IsEmpty()) { 1.203 + return nullptr; 1.204 + } 1.205 + IntSize scaleHint(static_cast<int32_t>(r.Width()), 1.206 + static_cast<int32_t>(r.Height())); 1.207 + container->SetScaleHint(scaleHint); 1.208 + 1.209 + nsRefPtr<ImageLayer> layer = static_cast<ImageLayer*> 1.210 + (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aItem)); 1.211 + if (!layer) { 1.212 + layer = aManager->CreateImageLayer(); 1.213 + if (!layer) 1.214 + return nullptr; 1.215 + } 1.216 + 1.217 + layer->SetContainer(container); 1.218 + layer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this)); 1.219 + layer->SetContentFlags(Layer::CONTENT_OPAQUE); 1.220 + // Set a transform on the layer to draw the video in the right place 1.221 + gfx::Matrix transform; 1.222 + gfxPoint p = r.TopLeft() + aContainerParameters.mOffset; 1.223 + transform.Translate(p.x, p.y); 1.224 + transform.Scale(r.Width()/frameSize.width, r.Height()/frameSize.height); 1.225 + layer->SetBaseTransform(gfx::Matrix4x4::From2D(transform)); 1.226 + layer->SetVisibleRegion(nsIntRect(0, 0, frameSize.width, frameSize.height)); 1.227 + nsRefPtr<Layer> result = layer.forget(); 1.228 + return result.forget(); 1.229 +} 1.230 + 1.231 +class DispatchResizeToControls : public nsRunnable 1.232 +{ 1.233 +public: 1.234 + DispatchResizeToControls(nsIContent* aContent) 1.235 + : mContent(aContent) {} 1.236 + NS_IMETHOD Run() MOZ_OVERRIDE { 1.237 + nsContentUtils::DispatchTrustedEvent(mContent->OwnerDoc(), mContent, 1.238 + NS_LITERAL_STRING("resizevideocontrols"), 1.239 + false, false); 1.240 + return NS_OK; 1.241 + } 1.242 + nsCOMPtr<nsIContent> mContent; 1.243 +}; 1.244 + 1.245 +nsresult 1.246 +nsVideoFrame::Reflow(nsPresContext* aPresContext, 1.247 + nsHTMLReflowMetrics& aMetrics, 1.248 + const nsHTMLReflowState& aReflowState, 1.249 + nsReflowStatus& aStatus) 1.250 +{ 1.251 + DO_GLOBAL_REFLOW_COUNT("nsVideoFrame"); 1.252 + DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus); 1.253 + NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, 1.254 + ("enter nsVideoFrame::Reflow: availSize=%d,%d", 1.255 + aReflowState.AvailableWidth(), aReflowState.AvailableHeight())); 1.256 + 1.257 + NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow"); 1.258 + 1.259 + aStatus = NS_FRAME_COMPLETE; 1.260 + 1.261 + aMetrics.Width() = aReflowState.ComputedWidth(); 1.262 + aMetrics.Height() = aReflowState.ComputedHeight(); 1.263 + 1.264 + // stash this away so we can compute our inner area later 1.265 + mBorderPadding = aReflowState.ComputedPhysicalBorderPadding(); 1.266 + 1.267 + aMetrics.Width() += mBorderPadding.left + mBorderPadding.right; 1.268 + aMetrics.Height() += mBorderPadding.top + mBorderPadding.bottom; 1.269 + 1.270 + // Reflow the child frames. We may have up to two, an image frame 1.271 + // which is the poster, and a box frame, which is the video controls. 1.272 + for (nsIFrame *child = mFrames.FirstChild(); 1.273 + child; 1.274 + child = child->GetNextSibling()) { 1.275 + if (child->GetContent() == mPosterImage) { 1.276 + // Reflow the poster frame. 1.277 + nsImageFrame* imageFrame = static_cast<nsImageFrame*>(child); 1.278 + nsHTMLReflowMetrics kidDesiredSize(aReflowState); 1.279 + nsSize availableSize = nsSize(aReflowState.AvailableWidth(), 1.280 + aReflowState.AvailableHeight()); 1.281 + nsHTMLReflowState kidReflowState(aPresContext, 1.282 + aReflowState, 1.283 + imageFrame, 1.284 + availableSize, 1.285 + aMetrics.Width(), 1.286 + aMetrics.Height()); 1.287 + 1.288 + uint32_t posterHeight, posterWidth; 1.289 + nsSize scaledPosterSize(0, 0); 1.290 + nsSize computedArea(aReflowState.ComputedWidth(), aReflowState.ComputedHeight()); 1.291 + nsPoint posterTopLeft(0, 0); 1.292 + 1.293 + nsCOMPtr<nsIDOMHTMLImageElement> posterImage = do_QueryInterface(mPosterImage); 1.294 + NS_ENSURE_TRUE(posterImage, NS_ERROR_FAILURE); 1.295 + posterImage->GetNaturalHeight(&posterHeight); 1.296 + posterImage->GetNaturalWidth(&posterWidth); 1.297 + 1.298 + if (ShouldDisplayPoster() && posterHeight && posterWidth) { 1.299 + gfxFloat scale = 1.300 + std::min(static_cast<float>(computedArea.width)/nsPresContext::CSSPixelsToAppUnits(static_cast<float>(posterWidth)), 1.301 + static_cast<float>(computedArea.height)/nsPresContext::CSSPixelsToAppUnits(static_cast<float>(posterHeight))); 1.302 + gfxSize scaledRatio = gfxSize(scale*posterWidth, scale*posterHeight); 1.303 + scaledPosterSize.width = nsPresContext::CSSPixelsToAppUnits(static_cast<float>(scaledRatio.width)); 1.304 + scaledPosterSize.height = nsPresContext::CSSPixelsToAppUnits(static_cast<int32_t>(scaledRatio.height)); 1.305 + } 1.306 + kidReflowState.SetComputedWidth(scaledPosterSize.width); 1.307 + kidReflowState.SetComputedHeight(scaledPosterSize.height); 1.308 + posterTopLeft.x = ((computedArea.width - scaledPosterSize.width) / 2) + mBorderPadding.left; 1.309 + posterTopLeft.y = ((computedArea.height - scaledPosterSize.height) / 2) + mBorderPadding.top; 1.310 + 1.311 + ReflowChild(imageFrame, aPresContext, kidDesiredSize, kidReflowState, 1.312 + posterTopLeft.x, posterTopLeft.y, 0, aStatus); 1.313 + FinishReflowChild(imageFrame, aPresContext, kidDesiredSize, &kidReflowState, 1.314 + posterTopLeft.x, posterTopLeft.y, 0); 1.315 + } else if (child->GetContent() == mVideoControls) { 1.316 + // Reflow the video controls frame. 1.317 + nsBoxLayoutState boxState(PresContext(), aReflowState.rendContext); 1.318 + nsSize size = child->GetSize(); 1.319 + nsBoxFrame::LayoutChildAt(boxState, 1.320 + child, 1.321 + nsRect(mBorderPadding.left, 1.322 + mBorderPadding.top, 1.323 + aReflowState.ComputedWidth(), 1.324 + aReflowState.ComputedHeight())); 1.325 + if (child->GetSize() != size) { 1.326 + nsRefPtr<nsRunnable> event = new DispatchResizeToControls(child->GetContent()); 1.327 + nsContentUtils::AddScriptRunner(event); 1.328 + } 1.329 + } else if (child->GetContent() == mCaptionDiv) { 1.330 + // Reflow to caption div 1.331 + nsHTMLReflowMetrics kidDesiredSize(aReflowState); 1.332 + nsSize availableSize = nsSize(aReflowState.AvailableWidth(), 1.333 + aReflowState.AvailableHeight()); 1.334 + nsHTMLReflowState kidReflowState(aPresContext, 1.335 + aReflowState, 1.336 + child, 1.337 + availableSize, 1.338 + aMetrics.Width(), 1.339 + aMetrics.Height()); 1.340 + nsSize size(aReflowState.ComputedWidth(), aReflowState.ComputedHeight()); 1.341 + size.width -= kidReflowState.ComputedPhysicalBorderPadding().LeftRight(); 1.342 + size.height -= kidReflowState.ComputedPhysicalBorderPadding().TopBottom(); 1.343 + 1.344 + kidReflowState.SetComputedWidth(std::max(size.width, 0)); 1.345 + kidReflowState.SetComputedHeight(std::max(size.height, 0)); 1.346 + 1.347 + ReflowChild(child, aPresContext, kidDesiredSize, kidReflowState, 1.348 + mBorderPadding.left, mBorderPadding.top, 0, aStatus); 1.349 + FinishReflowChild(child, aPresContext, 1.350 + kidDesiredSize, &kidReflowState, 1.351 + mBorderPadding.left, mBorderPadding.top, 0); 1.352 + } 1.353 + } 1.354 + aMetrics.SetOverflowAreasToDesiredBounds(); 1.355 + 1.356 + FinishAndStoreOverflow(&aMetrics); 1.357 + 1.358 + NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, 1.359 + ("exit nsVideoFrame::Reflow: size=%d,%d", 1.360 + aMetrics.Width(), aMetrics.Height())); 1.361 + NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics); 1.362 + 1.363 + return NS_OK; 1.364 +} 1.365 + 1.366 +class nsDisplayVideo : public nsDisplayItem { 1.367 +public: 1.368 + nsDisplayVideo(nsDisplayListBuilder* aBuilder, nsVideoFrame* aFrame) 1.369 + : nsDisplayItem(aBuilder, aFrame) 1.370 + { 1.371 + MOZ_COUNT_CTOR(nsDisplayVideo); 1.372 + } 1.373 +#ifdef NS_BUILD_REFCNT_LOGGING 1.374 + virtual ~nsDisplayVideo() { 1.375 + MOZ_COUNT_DTOR(nsDisplayVideo); 1.376 + } 1.377 +#endif 1.378 + 1.379 + NS_DISPLAY_DECL_NAME("Video", TYPE_VIDEO) 1.380 + 1.381 + // It would be great if we could override GetOpaqueRegion to return nonempty here, 1.382 + // but it's probably not safe to do so in general. Video frames are 1.383 + // updated asynchronously from decoder threads, and it's possible that 1.384 + // we might have an opaque video frame when GetOpaqueRegion is called, but 1.385 + // when we come to paint, the video frame is transparent or has gone 1.386 + // away completely (e.g. because of a decoder error). The problem would 1.387 + // be especially acute if we have off-main-thread rendering. 1.388 + 1.389 + virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) MOZ_OVERRIDE 1.390 + { 1.391 + *aSnap = true; 1.392 + nsIFrame* f = Frame(); 1.393 + return f->GetContentRect() - f->GetPosition() + ToReferenceFrame(); 1.394 + } 1.395 + 1.396 + virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder, 1.397 + LayerManager* aManager, 1.398 + const ContainerLayerParameters& aContainerParameters) MOZ_OVERRIDE 1.399 + { 1.400 + return static_cast<nsVideoFrame*>(mFrame)->BuildLayer(aBuilder, aManager, this, aContainerParameters); 1.401 + } 1.402 + 1.403 + virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder, 1.404 + LayerManager* aManager, 1.405 + const ContainerLayerParameters& aParameters) MOZ_OVERRIDE 1.406 + { 1.407 + if (aManager->IsCompositingCheap()) { 1.408 + // Since ImageLayers don't require additional memory of the 1.409 + // video frames we have to have anyway, we can't save much by 1.410 + // making layers inactive. Also, for many accelerated layer 1.411 + // managers calling imageContainer->GetCurrentAsSurface can be 1.412 + // very expensive. So just always be active when compositing is 1.413 + // cheap (i.e. hardware accelerated). 1.414 + return LAYER_ACTIVE; 1.415 + } 1.416 + HTMLMediaElement* elem = 1.417 + static_cast<HTMLMediaElement*>(mFrame->GetContent()); 1.418 + return elem->IsPotentiallyPlaying() ? LAYER_ACTIVE_FORCE : LAYER_INACTIVE; 1.419 + } 1.420 +}; 1.421 + 1.422 +void 1.423 +nsVideoFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.424 + const nsRect& aDirtyRect, 1.425 + const nsDisplayListSet& aLists) 1.426 +{ 1.427 + if (!IsVisibleForPainting(aBuilder)) 1.428 + return; 1.429 + 1.430 + DO_GLOBAL_REFLOW_COUNT_DSP("nsVideoFrame"); 1.431 + 1.432 + DisplayBorderBackgroundOutline(aBuilder, aLists); 1.433 + 1.434 + DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox 1.435 + clip(aBuilder, this, DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT); 1.436 + 1.437 + if (HasVideoElement() && !ShouldDisplayPoster()) { 1.438 + aLists.Content()->AppendNewToTop( 1.439 + new (aBuilder) nsDisplayVideo(aBuilder, this)); 1.440 + } 1.441 + 1.442 + // Add child frames to display list. We expect various children, 1.443 + // but only want to draw mPosterImage conditionally. Others we 1.444 + // always add to the display list. 1.445 + for (nsIFrame *child = mFrames.FirstChild(); 1.446 + child; 1.447 + child = child->GetNextSibling()) { 1.448 + if (child->GetContent() != mPosterImage || ShouldDisplayPoster()) { 1.449 + child->BuildDisplayListForStackingContext(aBuilder, 1.450 + aDirtyRect - child->GetOffsetTo(this), 1.451 + aLists.Content()); 1.452 + } else if (child->GetType() == nsGkAtoms::boxFrame) { 1.453 + child->BuildDisplayListForStackingContext(aBuilder, 1.454 + aDirtyRect - child->GetOffsetTo(this), 1.455 + aLists.Content()); 1.456 + } 1.457 + } 1.458 +} 1.459 + 1.460 +nsIAtom* 1.461 +nsVideoFrame::GetType() const 1.462 +{ 1.463 + return nsGkAtoms::HTMLVideoFrame; 1.464 +} 1.465 + 1.466 +#ifdef ACCESSIBILITY 1.467 +a11y::AccType 1.468 +nsVideoFrame::AccessibleType() 1.469 +{ 1.470 + return a11y::eHTMLMediaType; 1.471 +} 1.472 +#endif 1.473 + 1.474 +#ifdef DEBUG_FRAME_DUMP 1.475 +nsresult 1.476 +nsVideoFrame::GetFrameName(nsAString& aResult) const 1.477 +{ 1.478 + return MakeFrameName(NS_LITERAL_STRING("HTMLVideo"), aResult); 1.479 +} 1.480 +#endif 1.481 + 1.482 +nsSize nsVideoFrame::ComputeSize(nsRenderingContext *aRenderingContext, 1.483 + nsSize aCBSize, 1.484 + nscoord aAvailableWidth, 1.485 + nsSize aMargin, 1.486 + nsSize aBorder, 1.487 + nsSize aPadding, 1.488 + uint32_t aFlags) 1.489 +{ 1.490 + nsSize size = GetVideoIntrinsicSize(aRenderingContext); 1.491 + 1.492 + IntrinsicSize intrinsicSize; 1.493 + intrinsicSize.width.SetCoordValue(size.width); 1.494 + intrinsicSize.height.SetCoordValue(size.height); 1.495 + 1.496 + // Only video elements have an intrinsic ratio. 1.497 + nsSize intrinsicRatio = HasVideoElement() ? size : nsSize(0, 0); 1.498 + 1.499 + return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(aRenderingContext, 1.500 + this, 1.501 + intrinsicSize, 1.502 + intrinsicRatio, 1.503 + aCBSize, 1.504 + aMargin, 1.505 + aBorder, 1.506 + aPadding); 1.507 +} 1.508 + 1.509 +nscoord nsVideoFrame::GetMinWidth(nsRenderingContext *aRenderingContext) 1.510 +{ 1.511 + nscoord result = GetVideoIntrinsicSize(aRenderingContext).width; 1.512 + DISPLAY_MIN_WIDTH(this, result); 1.513 + return result; 1.514 +} 1.515 + 1.516 +nscoord nsVideoFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) 1.517 +{ 1.518 + nscoord result = GetVideoIntrinsicSize(aRenderingContext).width; 1.519 + DISPLAY_PREF_WIDTH(this, result); 1.520 + return result; 1.521 +} 1.522 + 1.523 +nsSize nsVideoFrame::GetIntrinsicRatio() 1.524 +{ 1.525 + if (!HasVideoElement()) { 1.526 + // Audio elements have no intrinsic ratio. 1.527 + return nsSize(0, 0); 1.528 + } 1.529 + 1.530 + return GetVideoIntrinsicSize(nullptr); 1.531 +} 1.532 + 1.533 +bool nsVideoFrame::ShouldDisplayPoster() 1.534 +{ 1.535 + if (!HasVideoElement()) 1.536 + return false; 1.537 + 1.538 + HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent()); 1.539 + if (element->GetPlayedOrSeeked() && HasVideoData()) 1.540 + return false; 1.541 + 1.542 + nsCOMPtr<nsIImageLoadingContent> imgContent = do_QueryInterface(mPosterImage); 1.543 + NS_ENSURE_TRUE(imgContent, false); 1.544 + 1.545 + nsCOMPtr<imgIRequest> request; 1.546 + nsresult res = imgContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, 1.547 + getter_AddRefs(request)); 1.548 + if (NS_FAILED(res) || !request) { 1.549 + return false; 1.550 + } 1.551 + 1.552 + uint32_t status = 0; 1.553 + res = request->GetImageStatus(&status); 1.554 + if (NS_FAILED(res) || (status & imgIRequest::STATUS_ERROR)) 1.555 + return false; 1.556 + 1.557 + return true; 1.558 +} 1.559 + 1.560 +nsSize 1.561 +nsVideoFrame::GetVideoIntrinsicSize(nsRenderingContext *aRenderingContext) 1.562 +{ 1.563 + // Defaulting size to 300x150 if no size given. 1.564 + nsIntSize size(300, 150); 1.565 + 1.566 + if (!HasVideoElement()) { 1.567 + if (!mFrames.FirstChild()) { 1.568 + return nsSize(0, 0); 1.569 + } 1.570 + 1.571 + // Ask the controls frame what its preferred height is 1.572 + nsBoxLayoutState boxState(PresContext(), aRenderingContext, 0); 1.573 + nscoord prefHeight = mFrames.LastChild()->GetPrefSize(boxState).height; 1.574 + return nsSize(nsPresContext::CSSPixelsToAppUnits(size.width), prefHeight); 1.575 + } 1.576 + 1.577 + HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent()); 1.578 + if (NS_FAILED(element->GetVideoSize(&size)) && ShouldDisplayPoster()) { 1.579 + // Use the poster image frame's size. 1.580 + nsIFrame *child = mPosterImage->GetPrimaryFrame(); 1.581 + nsImageFrame* imageFrame = do_QueryFrame(child); 1.582 + nsSize imgsize; 1.583 + if (NS_SUCCEEDED(imageFrame->GetIntrinsicImageSize(imgsize))) { 1.584 + return imgsize; 1.585 + } 1.586 + } 1.587 + 1.588 + return nsSize(nsPresContext::CSSPixelsToAppUnits(size.width), 1.589 + nsPresContext::CSSPixelsToAppUnits(size.height)); 1.590 +} 1.591 + 1.592 +void 1.593 +nsVideoFrame::UpdatePosterSource(bool aNotify) 1.594 +{ 1.595 + NS_ASSERTION(HasVideoElement(), "Only call this on <video> elements."); 1.596 + HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent()); 1.597 + 1.598 + if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::poster)) { 1.599 + nsAutoString posterStr; 1.600 + element->GetPoster(posterStr); 1.601 + mPosterImage->SetAttr(kNameSpaceID_None, 1.602 + nsGkAtoms::src, 1.603 + posterStr, 1.604 + aNotify); 1.605 + } else { 1.606 + mPosterImage->UnsetAttr(kNameSpaceID_None, nsGkAtoms::poster, aNotify); 1.607 + } 1.608 +} 1.609 + 1.610 +nsresult 1.611 +nsVideoFrame::AttributeChanged(int32_t aNameSpaceID, 1.612 + nsIAtom* aAttribute, 1.613 + int32_t aModType) 1.614 +{ 1.615 + if (aAttribute == nsGkAtoms::poster && HasVideoElement()) { 1.616 + UpdatePosterSource(true); 1.617 + } 1.618 + return nsContainerFrame::AttributeChanged(aNameSpaceID, 1.619 + aAttribute, 1.620 + aModType); 1.621 +} 1.622 + 1.623 +bool nsVideoFrame::HasVideoElement() { 1.624 + nsCOMPtr<nsIDOMHTMLVideoElement> videoDomElement = do_QueryInterface(mContent); 1.625 + return videoDomElement != nullptr; 1.626 +} 1.627 + 1.628 +bool nsVideoFrame::HasVideoData() 1.629 +{ 1.630 + if (!HasVideoElement()) 1.631 + return false; 1.632 + HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent()); 1.633 + nsIntSize size(0, 0); 1.634 + element->GetVideoSize(&size); 1.635 + return size != nsIntSize(0,0); 1.636 +}