LCOV - code coverage report
Current view: top level - layout/generic - nsVideoFrame.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1 374 0.3 %
Date: 2017-07-14 16:53:18 Functions: 2 43 4.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim:set ts=2 sw=2 sts=2 et cindent: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : /* rendering object for the HTML <video> element */
       8             : 
       9             : #include "nsVideoFrame.h"
      10             : 
      11             : #include "nsCOMPtr.h"
      12             : #include "nsGkAtoms.h"
      13             : 
      14             : #include "mozilla/dom/HTMLVideoElement.h"
      15             : #include "mozilla/layers/WebRenderLayerManager.h"
      16             : #include "nsIDOMHTMLImageElement.h"
      17             : #include "nsDisplayList.h"
      18             : #include "nsGenericHTMLElement.h"
      19             : #include "nsPresContext.h"
      20             : #include "nsContentCreatorFunctions.h"
      21             : #include "nsBoxLayoutState.h"
      22             : #include "nsBoxFrame.h"
      23             : #include "nsImageFrame.h"
      24             : #include "nsIImageLoadingContent.h"
      25             : #include "nsContentUtils.h"
      26             : #include "ImageContainer.h"
      27             : #include "ImageLayers.h"
      28             : #include "nsContentList.h"
      29             : #include "nsStyleUtil.h"
      30             : #include <algorithm>
      31             : 
      32             : using namespace mozilla;
      33             : using namespace mozilla::layers;
      34             : using namespace mozilla::dom;
      35             : using namespace mozilla::gfx;
      36             : 
      37             : nsIFrame*
      38           0 : NS_NewHTMLVideoFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
      39             : {
      40           0 :   return new (aPresShell) nsVideoFrame(aContext);
      41             : }
      42             : 
      43           0 : NS_IMPL_FRAMEARENA_HELPERS(nsVideoFrame)
      44             : 
      45             : // A matrix to obtain a correct-rotated video frame.
      46             : static Matrix
      47           0 : ComputeRotationMatrix(gfxFloat aRotatedWidth,
      48             :                       gfxFloat aRotatedHeight,
      49             :                       VideoInfo::Rotation aDegrees)
      50             : {
      51           0 :   Matrix shiftVideoCenterToOrigin;
      52           0 :   if (aDegrees == VideoInfo::Rotation::kDegree_90 ||
      53             :       aDegrees == VideoInfo::Rotation::kDegree_270) {
      54           0 :     shiftVideoCenterToOrigin = Matrix::Translation(-aRotatedHeight / 2.0,
      55           0 :                                                    -aRotatedWidth / 2.0);
      56             :   } else {
      57           0 :     shiftVideoCenterToOrigin = Matrix::Translation(-aRotatedWidth / 2.0,
      58           0 :                                                    -aRotatedHeight / 2.0);
      59             :   }
      60             : 
      61           0 :   Matrix rotation = Matrix::Rotation(gfx::Float(aDegrees / 180.0 * M_PI));
      62           0 :   Matrix shiftLeftTopToOrigin = Matrix::Translation(aRotatedWidth / 2.0,
      63           0 :                                                     aRotatedHeight / 2.0);
      64           0 :   return shiftVideoCenterToOrigin * rotation * shiftLeftTopToOrigin;
      65             : }
      66             : 
      67             : static void
      68           0 : SwapScaleWidthHeightForRotation(IntSize& aSize, VideoInfo::Rotation aDegrees)
      69             : {
      70           0 :   if (aDegrees == VideoInfo::Rotation::kDegree_90 ||
      71             :       aDegrees == VideoInfo::Rotation::kDegree_270) {
      72           0 :     int32_t tmpWidth = aSize.width;
      73           0 :     aSize.width = aSize.height;
      74           0 :     aSize.height = tmpWidth;
      75             :   }
      76           0 : }
      77             : 
      78           0 : nsVideoFrame::nsVideoFrame(nsStyleContext* aContext)
      79           0 :   : nsContainerFrame(aContext, kClassID)
      80             : {
      81           0 :   EnableVisibilityTracking();
      82           0 : }
      83             : 
      84           0 : nsVideoFrame::~nsVideoFrame()
      85             : {
      86           0 : }
      87             : 
      88           0 : NS_QUERYFRAME_HEAD(nsVideoFrame)
      89           0 :   NS_QUERYFRAME_ENTRY(nsVideoFrame)
      90           0 :   NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
      91           0 : NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
      92             : 
      93             : nsresult
      94           0 : nsVideoFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
      95             : {
      96           0 :   nsNodeInfoManager *nodeInfoManager = GetContent()->GetComposedDoc()->NodeInfoManager();
      97           0 :   RefPtr<NodeInfo> nodeInfo;
      98             :   Element *element;
      99             : 
     100           0 :   if (HasVideoElement()) {
     101             :     // Create an anonymous image element as a child to hold the poster
     102             :     // image. We may not have a poster image now, but one could be added
     103             :     // before we load, or on a subsequent load.
     104           0 :     nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::img,
     105             :                                             nullptr,
     106             :                                             kNameSpaceID_XHTML,
     107           0 :                                             nsIDOMNode::ELEMENT_NODE);
     108           0 :     NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
     109           0 :     element = NS_NewHTMLImageElement(nodeInfo.forget());
     110           0 :     mPosterImage = element;
     111           0 :     NS_ENSURE_TRUE(mPosterImage, NS_ERROR_OUT_OF_MEMORY);
     112             : 
     113             :     // Set the nsImageLoadingContent::ImageState() to 0. This means that the
     114             :     // image will always report its state as 0, so it will never be reframed
     115             :     // to show frames for loading or the broken image icon. This is important,
     116             :     // as the image is native anonymous, and so can't be reframed (currently).
     117           0 :     nsCOMPtr<nsIImageLoadingContent> imgContent = do_QueryInterface(mPosterImage);
     118           0 :     NS_ENSURE_TRUE(imgContent, NS_ERROR_FAILURE);
     119             : 
     120           0 :     imgContent->ForceImageState(true, 0);
     121             :     // And now have it update its internal state
     122           0 :     element->UpdateState(false);
     123             : 
     124           0 :     UpdatePosterSource(false);
     125             : 
     126           0 :     if (!aElements.AppendElement(mPosterImage))
     127           0 :       return NS_ERROR_OUT_OF_MEMORY;
     128             : 
     129             :     // Set up the caption overlay div for showing any TextTrack data
     130           0 :     nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::div,
     131             :                                             nullptr,
     132             :                                             kNameSpaceID_XHTML,
     133           0 :                                             nsIDOMNode::ELEMENT_NODE);
     134           0 :     NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
     135           0 :     mCaptionDiv = NS_NewHTMLDivElement(nodeInfo.forget());
     136           0 :     NS_ENSURE_TRUE(mCaptionDiv, NS_ERROR_OUT_OF_MEMORY);
     137           0 :     nsGenericHTMLElement* div = static_cast<nsGenericHTMLElement*>(mCaptionDiv.get());
     138           0 :     div->SetClassName(NS_LITERAL_STRING("caption-box"));
     139             : 
     140           0 :     if (!aElements.AppendElement(mCaptionDiv))
     141           0 :       return NS_ERROR_OUT_OF_MEMORY;
     142           0 :     UpdateTextTrack();
     143             :   }
     144             : 
     145             :   // Set up "videocontrols" XUL element which will be XBL-bound to the
     146             :   // actual controls.
     147           0 :   nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::videocontrols,
     148             :                                           nullptr,
     149             :                                           kNameSpaceID_XUL,
     150           0 :                                           nsIDOMNode::ELEMENT_NODE);
     151           0 :   NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
     152             : 
     153           0 :   NS_TrustedNewXULElement(getter_AddRefs(mVideoControls), nodeInfo.forget());
     154           0 :   if (!aElements.AppendElement(mVideoControls))
     155           0 :     return NS_ERROR_OUT_OF_MEMORY;
     156             : 
     157           0 :   return NS_OK;
     158             : }
     159             : 
     160             : void
     161           0 : nsVideoFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
     162             :                                        uint32_t aFliter)
     163             : {
     164           0 :   if (mPosterImage) {
     165           0 :     aElements.AppendElement(mPosterImage);
     166             :   }
     167             : 
     168           0 :   if (mVideoControls) {
     169           0 :     aElements.AppendElement(mVideoControls);
     170             :   }
     171             : 
     172           0 :   if (mCaptionDiv) {
     173           0 :     aElements.AppendElement(mCaptionDiv);
     174             :   }
     175           0 : }
     176             : 
     177             : void
     178           0 : nsVideoFrame::DestroyFrom(nsIFrame* aDestructRoot)
     179             : {
     180           0 :   nsContentUtils::DestroyAnonymousContent(&mCaptionDiv);
     181           0 :   nsContentUtils::DestroyAnonymousContent(&mVideoControls);
     182           0 :   nsContentUtils::DestroyAnonymousContent(&mPosterImage);
     183           0 :   nsContainerFrame::DestroyFrom(aDestructRoot);
     184           0 : }
     185             : 
     186             : already_AddRefed<Layer>
     187           0 : nsVideoFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
     188             :                          LayerManager* aManager,
     189             :                          nsDisplayItem* aItem,
     190             :                          const ContainerLayerParameters& aContainerParameters)
     191             : {
     192           0 :   nsRect area = GetContentRectRelativeToSelf() + aItem->ToReferenceFrame();
     193           0 :   HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
     194             : 
     195           0 :   nsIntSize videoSizeInPx;
     196           0 :   if (NS_FAILED(element->GetVideoSize(&videoSizeInPx)) ||
     197           0 :       area.IsEmpty()) {
     198           0 :     return nullptr;
     199             :   }
     200             : 
     201           0 :   RefPtr<ImageContainer> container = element->GetImageContainer();
     202           0 :   if (!container)
     203           0 :     return nullptr;
     204             : 
     205             :   // Retrieve the size of the decoded video frame, before being scaled
     206             :   // by pixel aspect ratio.
     207           0 :   mozilla::gfx::IntSize frameSize = container->GetCurrentSize();
     208           0 :   if (frameSize.width == 0 || frameSize.height == 0) {
     209             :     // No image, or zero-sized image. No point creating a layer.
     210           0 :     return nullptr;
     211             :   }
     212             : 
     213             :   // Convert video size from pixel units into app units, to get an aspect-ratio
     214             :   // (which has to be represented as a nsSize) and an IntrinsicSize that we
     215             :   // can pass to ComputeObjectRenderRect.
     216             :   nsSize aspectRatio(nsPresContext::CSSPixelsToAppUnits(videoSizeInPx.width),
     217           0 :                      nsPresContext::CSSPixelsToAppUnits(videoSizeInPx.height));
     218           0 :   IntrinsicSize intrinsicSize;
     219           0 :   intrinsicSize.width.SetCoordValue(aspectRatio.width);
     220           0 :   intrinsicSize.height.SetCoordValue(aspectRatio.height);
     221             : 
     222             :   nsRect dest = nsLayoutUtils::ComputeObjectDestRect(area,
     223             :                                                      intrinsicSize,
     224             :                                                      aspectRatio,
     225           0 :                                                      StylePosition());
     226             : 
     227           0 :   gfxRect destGFXRect = PresContext()->AppUnitsToGfxUnits(dest);
     228           0 :   destGFXRect.Round();
     229           0 :   if (destGFXRect.IsEmpty()) {
     230           0 :     return nullptr;
     231             :   }
     232             : 
     233           0 :   VideoInfo::Rotation rotationDeg = element->RotationDegrees();
     234           0 :   IntSize scaleHint(static_cast<int32_t>(destGFXRect.Width()),
     235           0 :                     static_cast<int32_t>(destGFXRect.Height()));
     236             :   // scaleHint is set regardless of rotation, so swap w/h if needed.
     237           0 :   SwapScaleWidthHeightForRotation(scaleHint, rotationDeg);
     238           0 :   container->SetScaleHint(scaleHint);
     239             : 
     240             :   RefPtr<ImageLayer> layer = static_cast<ImageLayer*>
     241           0 :     (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aItem));
     242           0 :   if (!layer) {
     243           0 :     layer = aManager->CreateImageLayer();
     244           0 :     if (!layer)
     245           0 :       return nullptr;
     246             :   }
     247             : 
     248           0 :   layer->SetContainer(container);
     249           0 :   layer->SetSamplingFilter(nsLayoutUtils::GetSamplingFilterForFrame(this));
     250             :   // Set a transform on the layer to draw the video in the right place
     251           0 :   gfxPoint p = destGFXRect.TopLeft() + aContainerParameters.mOffset;
     252             : 
     253             :   Matrix preTransform = ComputeRotationMatrix(destGFXRect.Width(),
     254             :                                               destGFXRect.Height(),
     255           0 :                                               rotationDeg);
     256             : 
     257           0 :   Matrix transform = preTransform * Matrix::Translation(p.x, p.y);
     258             : 
     259           0 :   layer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
     260           0 :   layer->SetScaleToSize(scaleHint, ScaleMode::STRETCH);
     261           0 :   RefPtr<Layer> result = layer.forget();
     262           0 :   return result.forget();
     263             : }
     264             : 
     265           0 : class DispatchResizeToControls : public Runnable
     266             : {
     267             : public:
     268           0 :   explicit DispatchResizeToControls(nsIContent* aContent)
     269           0 :     : mozilla::Runnable("DispatchResizeToControls")
     270           0 :     , mContent(aContent)
     271             :   {
     272           0 :   }
     273           0 :   NS_IMETHOD Run() override {
     274           0 :     nsContentUtils::DispatchTrustedEvent(mContent->OwnerDoc(), mContent,
     275           0 :                                          NS_LITERAL_STRING("resizevideocontrols"),
     276           0 :                                          false, false);
     277           0 :     return NS_OK;
     278             :   }
     279             :   nsCOMPtr<nsIContent> mContent;
     280             : };
     281             : 
     282             : void
     283           0 : nsVideoFrame::Reflow(nsPresContext* aPresContext,
     284             :                      ReflowOutput& aMetrics,
     285             :                      const ReflowInput& aReflowInput,
     286             :                      nsReflowStatus& aStatus)
     287             : {
     288           0 :   MarkInReflow();
     289           0 :   DO_GLOBAL_REFLOW_COUNT("nsVideoFrame");
     290           0 :   DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus);
     291           0 :   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
     292             :                  ("enter nsVideoFrame::Reflow: availSize=%d,%d",
     293             :                   aReflowInput.AvailableWidth(),
     294             :                   aReflowInput.AvailableHeight()));
     295             : 
     296           0 :   NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
     297             : 
     298           0 :   aStatus.Reset();
     299             : 
     300           0 :   const WritingMode myWM = aReflowInput.GetWritingMode();
     301           0 :   nscoord contentBoxBSize = aReflowInput.ComputedBSize();
     302           0 :   const nscoord borderBoxISize = aReflowInput.ComputedISize() +
     303           0 :     aReflowInput.ComputedLogicalBorderPadding().IStartEnd(myWM);
     304           0 :   const bool isBSizeShrinkWrapping = (contentBoxBSize == NS_INTRINSICSIZE);
     305             : 
     306             :   nscoord borderBoxBSize;
     307           0 :   if (!isBSizeShrinkWrapping) {
     308           0 :     borderBoxBSize = contentBoxBSize +
     309           0 :       aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM);
     310             :   }
     311             : 
     312           0 :   nsMargin borderPadding = aReflowInput.ComputedPhysicalBorderPadding();
     313             : 
     314             :   // Reflow the child frames. We may have up to three: an image
     315             :   // frame (for the poster image), a container frame for the controls,
     316             :   // and a container frame for the caption.
     317           0 :   for (nsIFrame* child : mFrames) {
     318           0 :     nsSize oldChildSize = child->GetSize();
     319             : 
     320           0 :     if (child->GetContent() == mPosterImage) {
     321             :       // Reflow the poster frame.
     322           0 :       nsImageFrame* imageFrame = static_cast<nsImageFrame*>(child);
     323           0 :       ReflowOutput kidDesiredSize(aReflowInput);
     324           0 :       WritingMode wm = imageFrame->GetWritingMode();
     325           0 :       LogicalSize availableSize = aReflowInput.AvailableSize(wm);
     326           0 :       LogicalSize cbSize = aMetrics.Size(aMetrics.GetWritingMode()).
     327           0 :                              ConvertTo(wm, aMetrics.GetWritingMode());
     328             :       ReflowInput kidReflowInput(aPresContext,
     329             :                                        aReflowInput,
     330             :                                        imageFrame,
     331             :                                        availableSize,
     332           0 :                                        &cbSize);
     333             : 
     334           0 :       nsRect posterRenderRect;
     335           0 :       if (ShouldDisplayPoster()) {
     336           0 :         posterRenderRect =
     337           0 :           nsRect(nsPoint(borderPadding.left, borderPadding.top),
     338           0 :                  nsSize(aReflowInput.ComputedWidth(),
     339             :                         aReflowInput.ComputedHeight()));
     340             :       }
     341           0 :       kidReflowInput.SetComputedWidth(posterRenderRect.width);
     342           0 :       kidReflowInput.SetComputedHeight(posterRenderRect.height);
     343           0 :       ReflowChild(imageFrame, aPresContext, kidDesiredSize, kidReflowInput,
     344           0 :                   posterRenderRect.x, posterRenderRect.y, 0, aStatus);
     345           0 :       FinishReflowChild(imageFrame, aPresContext,
     346             :                         kidDesiredSize, &kidReflowInput,
     347           0 :                         posterRenderRect.x, posterRenderRect.y, 0);
     348             : 
     349             : // Android still uses XUL media controls & hence needs this XUL-friendly
     350             : // custom reflow code. This will go away in bug 1310907.
     351             : #ifdef ANDROID
     352             :     } else if (child->GetContent() == mVideoControls) {
     353             :       // Reflow the video controls frame.
     354             :       nsBoxLayoutState boxState(PresContext(), aReflowInput.mRenderingContext);
     355             :       nsBoxFrame::LayoutChildAt(boxState,
     356             :                                 child,
     357             :                                 nsRect(borderPadding.left,
     358             :                                        borderPadding.top,
     359             :                                        aReflowInput.ComputedWidth(),
     360             :                                        aReflowInput.ComputedHeight()));
     361             : 
     362             : #endif // ANDROID
     363           0 :     } else if (child->GetContent() == mCaptionDiv ||
     364           0 :                child->GetContent() == mVideoControls) {
     365             :       // Reflow the caption and control bar frames.
     366           0 :       WritingMode wm = child->GetWritingMode();
     367           0 :       LogicalSize availableSize = aReflowInput.ComputedSize(wm);
     368           0 :       availableSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
     369             : 
     370             :       ReflowInput kidReflowInput(aPresContext,
     371             :                                        aReflowInput,
     372             :                                        child,
     373           0 :                                        availableSize);
     374           0 :       ReflowOutput kidDesiredSize(kidReflowInput);
     375           0 :       ReflowChild(child, aPresContext, kidDesiredSize, kidReflowInput,
     376           0 :                   borderPadding.left, borderPadding.top, 0, aStatus);
     377             : 
     378           0 :       if (child->GetContent() == mVideoControls && isBSizeShrinkWrapping) {
     379             :         // Resolve our own BSize based on the controls' size in the same axis.
     380           0 :         contentBoxBSize = myWM.IsOrthogonalTo(wm) ?
     381           0 :           kidDesiredSize.ISize(wm) : kidDesiredSize.BSize(wm);
     382             :       }
     383             : 
     384           0 :       FinishReflowChild(child, aPresContext,
     385             :                         kidDesiredSize, &kidReflowInput,
     386           0 :                         borderPadding.left, borderPadding.top, 0);
     387             :     }
     388             : 
     389           0 :     if (child->GetContent() == mVideoControls && child->GetSize() != oldChildSize) {
     390           0 :       RefPtr<Runnable> event = new DispatchResizeToControls(child->GetContent());
     391           0 :       nsContentUtils::AddScriptRunner(event);
     392             :     }
     393             :   }
     394             : 
     395           0 :   if (isBSizeShrinkWrapping) {
     396           0 :     if (contentBoxBSize == NS_INTRINSICSIZE) {
     397             :       // We didn't get a BSize from our intrinsic size/ratio, nor did we
     398             :       // get one from our controls. Just use BSize of 0.
     399           0 :       contentBoxBSize = 0;
     400             :     }
     401           0 :     contentBoxBSize = NS_CSS_MINMAX(contentBoxBSize,
     402             :                                     aReflowInput.ComputedMinBSize(),
     403           0 :                                     aReflowInput.ComputedMaxBSize());
     404           0 :     borderBoxBSize = contentBoxBSize +
     405           0 :       aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM);
     406             :   }
     407             : 
     408           0 :   LogicalSize logicalDesiredSize(myWM, borderBoxISize, borderBoxBSize);
     409           0 :   aMetrics.SetSize(myWM, logicalDesiredSize);
     410             : 
     411           0 :   aMetrics.SetOverflowAreasToDesiredBounds();
     412             : 
     413           0 :   FinishAndStoreOverflow(&aMetrics);
     414             : 
     415           0 :   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
     416             :                  ("exit nsVideoFrame::Reflow: size=%d,%d",
     417             :                   aMetrics.Width(), aMetrics.Height()));
     418           0 :   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics);
     419           0 : }
     420             : 
     421             : class nsDisplayVideo : public nsDisplayItem {
     422             : public:
     423           0 :   nsDisplayVideo(nsDisplayListBuilder* aBuilder, nsVideoFrame* aFrame)
     424           0 :     : nsDisplayItem(aBuilder, aFrame)
     425             :   {
     426           0 :     MOZ_COUNT_CTOR(nsDisplayVideo);
     427           0 :   }
     428             : #ifdef NS_BUILD_REFCNT_LOGGING
     429           0 :   virtual ~nsDisplayVideo() {
     430           0 :     MOZ_COUNT_DTOR(nsDisplayVideo);
     431           0 :   }
     432             : #endif
     433             : 
     434           0 :   NS_DISPLAY_DECL_NAME("Video", TYPE_VIDEO)
     435             : 
     436           0 :   virtual bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
     437             :                                        const mozilla::layers::StackingContextHelper& aSc,
     438             :                                        nsTArray<mozilla::layers::WebRenderParentCommand>& aParentCommands,
     439             :                                        mozilla::layers::WebRenderLayerManager* aManager,
     440             :                                        nsDisplayListBuilder* aDisplayListBuilder) override
     441             :   {
     442           0 :     nsRect area = Frame()->GetContentRectRelativeToSelf() + ToReferenceFrame();
     443           0 :     HTMLVideoElement* element = static_cast<HTMLVideoElement*>(Frame()->GetContent());
     444             : 
     445           0 :     nsIntSize videoSizeInPx;
     446           0 :     if (NS_FAILED(element->GetVideoSize(&videoSizeInPx)) || area.IsEmpty()) {
     447           0 :       return false;
     448             :     }
     449             : 
     450           0 :     RefPtr<ImageContainer> container = element->GetImageContainer();
     451           0 :     if (!container) {
     452           0 :       return false;
     453             :     }
     454             : 
     455             :     // Retrieve the size of the decoded video frame, before being scaled
     456             :     // by pixel aspect ratio.
     457           0 :     mozilla::gfx::IntSize frameSize = container->GetCurrentSize();
     458           0 :     if (frameSize.width == 0 || frameSize.height == 0) {
     459             :       // No image, or zero-sized image. Don't render.
     460           0 :       return true;
     461             :     }
     462             : 
     463             :     // Convert video size from pixel units into app units, to get an aspect-ratio
     464             :     // (which has to be represented as a nsSize) and an IntrinsicSize that we
     465             :     // can pass to ComputeObjectRenderRect.
     466             :     nsSize aspectRatio(nsPresContext::CSSPixelsToAppUnits(videoSizeInPx.width),
     467           0 :                        nsPresContext::CSSPixelsToAppUnits(videoSizeInPx.height));
     468           0 :     IntrinsicSize intrinsicSize;
     469           0 :     intrinsicSize.width.SetCoordValue(aspectRatio.width);
     470           0 :     intrinsicSize.height.SetCoordValue(aspectRatio.height);
     471             : 
     472             :     nsRect dest = nsLayoutUtils::ComputeObjectDestRect(area,
     473             :                                                        intrinsicSize,
     474             :                                                        aspectRatio,
     475           0 :                                                        Frame()->StylePosition());
     476             : 
     477           0 :     gfxRect destGFXRect = Frame()->PresContext()->AppUnitsToGfxUnits(dest);
     478           0 :     destGFXRect.Round();
     479           0 :     if (destGFXRect.IsEmpty()) {
     480           0 :       return false;
     481             :     }
     482             : 
     483           0 :     VideoInfo::Rotation rotationDeg = element->RotationDegrees();
     484           0 :     IntSize scaleHint(static_cast<int32_t>(destGFXRect.Width()),
     485           0 :                       static_cast<int32_t>(destGFXRect.Height()));
     486             :     // scaleHint is set regardless of rotation, so swap w/h if needed.
     487           0 :     SwapScaleWidthHeightForRotation(scaleHint, rotationDeg);
     488           0 :     container->SetScaleHint(scaleHint);
     489             : 
     490           0 :     LayerRect rect(destGFXRect.x, destGFXRect.y, destGFXRect.width, destGFXRect.height);
     491           0 :     return aManager->PushImage(this, container, aBuilder, aSc, rect);
     492             :   }
     493             : 
     494             :   // It would be great if we could override GetOpaqueRegion to return nonempty here,
     495             :   // but it's probably not safe to do so in general. Video frames are
     496             :   // updated asynchronously from decoder threads, and it's possible that
     497             :   // we might have an opaque video frame when GetOpaqueRegion is called, but
     498             :   // when we come to paint, the video frame is transparent or has gone
     499             :   // away completely (e.g. because of a decoder error). The problem would
     500             :   // be especially acute if we have off-main-thread rendering.
     501             : 
     502           0 :   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override
     503             :   {
     504           0 :     *aSnap = true;
     505           0 :     nsIFrame* f = Frame();
     506           0 :     return f->GetContentRectRelativeToSelf() + ToReferenceFrame();
     507             :   }
     508             : 
     509           0 :   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
     510             :                                              LayerManager* aManager,
     511             :                                              const ContainerLayerParameters& aContainerParameters) override
     512             :   {
     513           0 :     return static_cast<nsVideoFrame*>(mFrame)->BuildLayer(aBuilder, aManager, this, aContainerParameters);
     514             :   }
     515             : 
     516           0 :   virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
     517             :                                    LayerManager* aManager,
     518             :                                    const ContainerLayerParameters& aParameters) override
     519             :   {
     520           0 :     if (aManager->IsCompositingCheap()) {
     521             :       // Since ImageLayers don't require additional memory of the
     522             :       // video frames we have to have anyway, we can't save much by
     523             :       // making layers inactive. Also, for many accelerated layer
     524             :       // managers calling imageContainer->GetCurrentAsSurface can be
     525             :       // very expensive. So just always be active when compositing is
     526             :       // cheap (i.e. hardware accelerated).
     527           0 :       return LAYER_ACTIVE;
     528             :     }
     529             :     HTMLMediaElement* elem =
     530           0 :       static_cast<HTMLMediaElement*>(mFrame->GetContent());
     531           0 :     return elem->IsPotentiallyPlaying() ? LAYER_ACTIVE_FORCE : LAYER_INACTIVE;
     532             :   }
     533             : };
     534             : 
     535             : void
     536           0 : nsVideoFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
     537             :                                const nsRect&           aDirtyRect,
     538             :                                const nsDisplayListSet& aLists)
     539             : {
     540           0 :   if (!IsVisibleForPainting(aBuilder))
     541           0 :     return;
     542             : 
     543           0 :   DO_GLOBAL_REFLOW_COUNT_DSP("nsVideoFrame");
     544             : 
     545           0 :   DisplayBorderBackgroundOutline(aBuilder, aLists);
     546             : 
     547           0 :   const bool shouldDisplayPoster = ShouldDisplayPoster();
     548             : 
     549             :   // NOTE: If we're displaying a poster image (instead of video data), we can
     550             :   // trust the nsImageFrame to constrain its drawing to its content rect
     551             :   // (which happens to be the same as our content rect).
     552             :   uint32_t clipFlags;
     553           0 :   if (shouldDisplayPoster ||
     554           0 :       !nsStyleUtil::ObjectPropsMightCauseOverflow(StylePosition())) {
     555           0 :     clipFlags =
     556             :       DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT;
     557             :   } else {
     558           0 :     clipFlags = 0;
     559             :   }
     560             : 
     561             :   DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
     562           0 :     clip(aBuilder, this, clipFlags);
     563             : 
     564           0 :   if (HasVideoElement() && !shouldDisplayPoster) {
     565           0 :     aLists.Content()->AppendNewToTop(
     566           0 :       new (aBuilder) nsDisplayVideo(aBuilder, this));
     567             :   }
     568             : 
     569             :   // Add child frames to display list. We expect various children,
     570             :   // but only want to draw mPosterImage conditionally. Others we
     571             :   // always add to the display list.
     572           0 :   for (nsIFrame* child : mFrames) {
     573           0 :     if (child->GetContent() != mPosterImage || shouldDisplayPoster) {
     574           0 :       child->BuildDisplayListForStackingContext(aBuilder,
     575           0 :                                                 aDirtyRect - child->GetOffsetTo(this),
     576           0 :                                                 aLists.Content());
     577           0 :     } else if (child->IsBoxFrame()) {
     578           0 :       child->BuildDisplayListForStackingContext(aBuilder,
     579           0 :                                                 aDirtyRect - child->GetOffsetTo(this),
     580           0 :                                                 aLists.Content());
     581             :     }
     582             :   }
     583             : }
     584             : 
     585             : #ifdef ACCESSIBILITY
     586             : a11y::AccType
     587           0 : nsVideoFrame::AccessibleType()
     588             : {
     589           0 :   return a11y::eHTMLMediaType;
     590             : }
     591             : #endif
     592             : 
     593             : #ifdef DEBUG_FRAME_DUMP
     594             : nsresult
     595           0 : nsVideoFrame::GetFrameName(nsAString& aResult) const
     596             : {
     597           0 :   return MakeFrameName(NS_LITERAL_STRING("HTMLVideo"), aResult);
     598             : }
     599             : #endif
     600             : 
     601             : LogicalSize
     602           0 : nsVideoFrame::ComputeSize(gfxContext *aRenderingContext,
     603             :                           WritingMode aWM,
     604             :                           const LogicalSize& aCBSize,
     605             :                           nscoord aAvailableISize,
     606             :                           const LogicalSize& aMargin,
     607             :                           const LogicalSize& aBorder,
     608             :                           const LogicalSize& aPadding,
     609             :                           ComputeSizeFlags aFlags)
     610             : {
     611             : // When in no video scenario, it should fall back to inherited method.
     612             : // We keep old codepath here since Android still uses XUL media controls.
     613             : // This will go away in bug 1310907.
     614             : #ifndef ANDROID
     615           0 :   if (!HasVideoElement()) {
     616             :     return nsContainerFrame::ComputeSize(aRenderingContext,
     617             :                                          aWM,
     618             :                                          aCBSize,
     619             :                                          aAvailableISize,
     620             :                                          aMargin,
     621             :                                          aBorder,
     622             :                                          aPadding,
     623           0 :                                          aFlags);
     624             :   }
     625             : #endif // ANDROID
     626             : 
     627           0 :   nsSize size = GetVideoIntrinsicSize(aRenderingContext);
     628             : 
     629           0 :   IntrinsicSize intrinsicSize;
     630           0 :   intrinsicSize.width.SetCoordValue(size.width);
     631           0 :   intrinsicSize.height.SetCoordValue(size.height);
     632             : 
     633             :   // Only video elements have an intrinsic ratio.
     634           0 :   nsSize intrinsicRatio = HasVideoElement() ? size : nsSize(0, 0);
     635             : 
     636             :   return ComputeSizeWithIntrinsicDimensions(aRenderingContext, aWM,
     637             :                                             intrinsicSize, intrinsicRatio,
     638             :                                             aCBSize, aMargin, aBorder, aPadding,
     639           0 :                                             aFlags);
     640             : }
     641             : 
     642           0 : nscoord nsVideoFrame::GetMinISize(gfxContext *aRenderingContext)
     643             : {
     644             :   nscoord result;
     645           0 :   DISPLAY_MIN_WIDTH(this, result);
     646             : 
     647           0 :   if (HasVideoElement()) {
     648           0 :     nsSize size = GetVideoIntrinsicSize(aRenderingContext);
     649           0 :     result = GetWritingMode().IsVertical() ? size.height : size.width;
     650             :   } else {
     651             :     // We expect last and only child of audio elements to be control if
     652             :     // "controls" attribute is present.
     653           0 :     nsIFrame* kid = mFrames.LastChild();
     654           0 :     if (kid) {
     655           0 :       result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
     656             :                                                     kid,
     657             :                                                     nsLayoutUtils::MIN_ISIZE);
     658             :     } else {
     659           0 :       result = 0;
     660             :     }
     661             :   }
     662             : 
     663           0 :   return result;
     664             : }
     665             : 
     666           0 : nscoord nsVideoFrame::GetPrefISize(gfxContext *aRenderingContext)
     667             : {
     668             :   nscoord result;
     669           0 :   DISPLAY_PREF_WIDTH(this, result);
     670             : 
     671           0 :   if (HasVideoElement()) {
     672           0 :     nsSize size = GetVideoIntrinsicSize(aRenderingContext);
     673           0 :     result = GetWritingMode().IsVertical() ? size.height : size.width;
     674             :   } else {
     675             :     // We expect last and only child of audio elements to be control if
     676             :     // "controls" attribute is present.
     677           0 :     nsIFrame* kid = mFrames.LastChild();
     678           0 :     if (kid) {
     679           0 :       result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
     680             :                                                     kid,
     681             :                                                     nsLayoutUtils::PREF_ISIZE);
     682             :     } else {
     683           0 :       result = 0;
     684             :     }
     685             :   }
     686             : 
     687           0 :   return result;
     688             : }
     689             : 
     690           0 : nsSize nsVideoFrame::GetIntrinsicRatio()
     691             : {
     692           0 :   if (!HasVideoElement()) {
     693             :     // Audio elements have no intrinsic ratio.
     694           0 :     return nsSize(0, 0);
     695             :   }
     696             : 
     697           0 :   return GetVideoIntrinsicSize(nullptr);
     698             : }
     699             : 
     700           0 : bool nsVideoFrame::ShouldDisplayPoster()
     701             : {
     702           0 :   if (!HasVideoElement())
     703           0 :     return false;
     704             : 
     705           0 :   HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
     706           0 :   if (element->GetPlayedOrSeeked() && HasVideoData())
     707           0 :     return false;
     708             : 
     709           0 :   nsCOMPtr<nsIImageLoadingContent> imgContent = do_QueryInterface(mPosterImage);
     710           0 :   NS_ENSURE_TRUE(imgContent, false);
     711             : 
     712           0 :   nsCOMPtr<imgIRequest> request;
     713           0 :   nsresult res = imgContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
     714           0 :                                         getter_AddRefs(request));
     715           0 :   if (NS_FAILED(res) || !request) {
     716           0 :     return false;
     717             :   }
     718             : 
     719           0 :   uint32_t status = 0;
     720           0 :   res = request->GetImageStatus(&status);
     721           0 :   if (NS_FAILED(res) || (status & imgIRequest::STATUS_ERROR))
     722           0 :     return false;
     723             : 
     724           0 :   return true;
     725             : }
     726             : 
     727             : nsSize
     728           0 : nsVideoFrame::GetVideoIntrinsicSize(gfxContext *aRenderingContext)
     729             : {
     730             :   // Defaulting size to 300x150 if no size given.
     731           0 :   nsIntSize size(300, 150);
     732             : 
     733             : // All media controls have been converted to HTML except Android. Hence
     734             : // we keep this codepath for Android until removal in bug 1310907.
     735             : #ifdef ANDROID
     736             :   if (!HasVideoElement()) {
     737             :     if (!mFrames.FirstChild()) {
     738             :       return nsSize(0, 0);
     739             :     }
     740             : 
     741             :     // Ask the controls frame what its preferred height is
     742             :     nsBoxLayoutState boxState(PresContext(), aRenderingContext, 0);
     743             :     nscoord prefHeight = mFrames.LastChild()->GetXULPrefSize(boxState).height;
     744             :     return nsSize(nsPresContext::CSSPixelsToAppUnits(size.width), prefHeight);
     745             :   }
     746             : #endif // ANDROID
     747             : 
     748           0 :   HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
     749           0 :   if (NS_FAILED(element->GetVideoSize(&size)) && ShouldDisplayPoster()) {
     750             :     // Use the poster image frame's size.
     751           0 :     nsIFrame *child = mPosterImage->GetPrimaryFrame();
     752           0 :     nsImageFrame* imageFrame = do_QueryFrame(child);
     753           0 :     nsSize imgsize;
     754           0 :     if (NS_SUCCEEDED(imageFrame->GetIntrinsicImageSize(imgsize))) {
     755           0 :       return imgsize;
     756             :     }
     757             :   }
     758             : 
     759           0 :   return nsSize(nsPresContext::CSSPixelsToAppUnits(size.width),
     760           0 :                 nsPresContext::CSSPixelsToAppUnits(size.height));
     761             : }
     762             : 
     763             : void
     764           0 : nsVideoFrame::UpdatePosterSource(bool aNotify)
     765             : {
     766           0 :   NS_ASSERTION(HasVideoElement(), "Only call this on <video> elements.");
     767           0 :   HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
     768             : 
     769           0 :   if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::poster) &&
     770           0 :       !element->AttrValueIs(kNameSpaceID_None,
     771             :                             nsGkAtoms::poster,
     772             :                             nsGkAtoms::_empty,
     773             :                             eIgnoreCase)) {
     774           0 :     nsAutoString posterStr;
     775           0 :     element->GetPoster(posterStr);
     776           0 :     mPosterImage->SetAttr(kNameSpaceID_None,
     777             :                           nsGkAtoms::src,
     778             :                           posterStr,
     779           0 :                           aNotify);
     780             :   } else {
     781           0 :     mPosterImage->UnsetAttr(kNameSpaceID_None, nsGkAtoms::src, aNotify);
     782             :   }
     783           0 : }
     784             : 
     785             : nsresult
     786           0 : nsVideoFrame::AttributeChanged(int32_t aNameSpaceID,
     787             :                                nsIAtom* aAttribute,
     788             :                                int32_t aModType)
     789             : {
     790           0 :   if (aAttribute == nsGkAtoms::poster && HasVideoElement()) {
     791           0 :     UpdatePosterSource(true);
     792             :   }
     793           0 :   return nsContainerFrame::AttributeChanged(aNameSpaceID,
     794             :                                             aAttribute,
     795           0 :                                             aModType);
     796             : }
     797             : 
     798             : void
     799           0 : nsVideoFrame::OnVisibilityChange(Visibility aNewVisibility,
     800             :                                  const Maybe<OnNonvisible>& aNonvisibleAction)
     801             : {
     802           0 :   if (HasVideoElement()) {
     803           0 :     nsCOMPtr<nsIDOMHTMLMediaElement> mediaDomElement = do_QueryInterface(mContent);
     804           0 :     mediaDomElement->OnVisibilityChange(aNewVisibility);
     805             :   }
     806             : 
     807           0 :   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mPosterImage);
     808           0 :   if (imageLoader) {
     809           0 :     imageLoader->OnVisibilityChange(aNewVisibility,
     810           0 :                                     aNonvisibleAction);
     811             :   }
     812             : 
     813           0 :   nsContainerFrame::OnVisibilityChange(aNewVisibility, aNonvisibleAction);
     814           0 : }
     815             : 
     816           0 : bool nsVideoFrame::HasVideoElement() {
     817           0 :   nsCOMPtr<nsIDOMHTMLMediaElement> mediaDomElement = do_QueryInterface(mContent);
     818           0 :   return mediaDomElement->IsVideo();
     819             : }
     820             : 
     821           0 : bool nsVideoFrame::HasVideoData()
     822             : {
     823           0 :   if (!HasVideoElement())
     824           0 :     return false;
     825           0 :   HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
     826           0 :   nsIntSize size(0, 0);
     827           0 :   element->GetVideoSize(&size);
     828           0 :   return size != nsIntSize(0,0);
     829             : }
     830             : 
     831           0 : void nsVideoFrame::UpdateTextTrack()
     832             : {
     833           0 :   HTMLMediaElement* element = static_cast<HTMLMediaElement*>(GetContent());
     834           0 :   if (element) {
     835           0 :     element->NotifyCueDisplayStatesChanged();
     836             :   }
     837           9 : }

Generated by: LCOV version 1.13