layout/generic/nsVideoFrame.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     7 /* rendering object for the HTML <video> element */
     9 #include "nsVideoFrame.h"
    11 #include "nsCOMPtr.h"
    12 #include "nsGkAtoms.h"
    14 #include "mozilla/dom/HTMLVideoElement.h"
    15 #include "nsIDOMHTMLVideoElement.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 <algorithm>
    31 using namespace mozilla;
    32 using namespace mozilla::layers;
    33 using namespace mozilla::dom;
    34 using namespace mozilla::gfx;
    36 nsIFrame*
    37 NS_NewHTMLVideoFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
    38 {
    39   return new (aPresShell) nsVideoFrame(aContext);
    40 }
    42 NS_IMPL_FRAMEARENA_HELPERS(nsVideoFrame)
    44 nsVideoFrame::nsVideoFrame(nsStyleContext* aContext) :
    45   nsContainerFrame(aContext)
    46 {
    47 }
    49 nsVideoFrame::~nsVideoFrame()
    50 {
    51 }
    53 NS_QUERYFRAME_HEAD(nsVideoFrame)
    54   NS_QUERYFRAME_ENTRY(nsVideoFrame)
    55   NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
    56 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
    58 nsresult
    59 nsVideoFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
    60 {
    61   nsNodeInfoManager *nodeInfoManager = GetContent()->GetCurrentDoc()->NodeInfoManager();
    62   nsCOMPtr<nsINodeInfo> nodeInfo;
    63   Element *element;
    65   if (HasVideoElement()) {
    66     // Create an anonymous image element as a child to hold the poster
    67     // image. We may not have a poster image now, but one could be added
    68     // before we load, or on a subsequent load.
    69     nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::img,
    70                                             nullptr,
    71                                             kNameSpaceID_XHTML,
    72                                             nsIDOMNode::ELEMENT_NODE);
    73     NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
    74     element = NS_NewHTMLImageElement(nodeInfo.forget());
    75     mPosterImage = element;
    76     NS_ENSURE_TRUE(mPosterImage, NS_ERROR_OUT_OF_MEMORY);
    78     // Set the nsImageLoadingContent::ImageState() to 0. This means that the
    79     // image will always report its state as 0, so it will never be reframed
    80     // to show frames for loading or the broken image icon. This is important,
    81     // as the image is native anonymous, and so can't be reframed (currently).
    82     nsCOMPtr<nsIImageLoadingContent> imgContent = do_QueryInterface(mPosterImage);
    83     NS_ENSURE_TRUE(imgContent, NS_ERROR_FAILURE);
    85     imgContent->ForceImageState(true, 0);
    86     // And now have it update its internal state
    87     element->UpdateState(false);
    89     UpdatePosterSource(false);
    91     if (!aElements.AppendElement(mPosterImage))
    92       return NS_ERROR_OUT_OF_MEMORY;
    94     // Set up the caption overlay div for showing any TextTrack data
    95     nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::div,
    96                                             nullptr,
    97                                             kNameSpaceID_XHTML,
    98                                             nsIDOMNode::ELEMENT_NODE);
    99     NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
   100     mCaptionDiv = NS_NewHTMLDivElement(nodeInfo.forget());
   101     NS_ENSURE_TRUE(mCaptionDiv, NS_ERROR_OUT_OF_MEMORY);
   102     nsGenericHTMLElement* div = static_cast<nsGenericHTMLElement*>(mCaptionDiv.get());
   103     div->SetClassName(NS_LITERAL_STRING("caption-box"));
   105     if (!aElements.AppendElement(mCaptionDiv))
   106       return NS_ERROR_OUT_OF_MEMORY;
   107   }
   109   // Set up "videocontrols" XUL element which will be XBL-bound to the
   110   // actual controls.
   111   nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::videocontrols,
   112                                           nullptr,
   113                                           kNameSpaceID_XUL,
   114                                           nsIDOMNode::ELEMENT_NODE);
   115   NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
   117   NS_TrustedNewXULElement(getter_AddRefs(mVideoControls), nodeInfo.forget());
   118   if (!aElements.AppendElement(mVideoControls))
   119     return NS_ERROR_OUT_OF_MEMORY;
   121   return NS_OK;
   122 }
   124 void
   125 nsVideoFrame::AppendAnonymousContentTo(nsBaseContentList& aElements,
   126                                        uint32_t aFliter)
   127 {
   128   aElements.MaybeAppendElement(mPosterImage);
   129   aElements.MaybeAppendElement(mVideoControls);
   130   aElements.MaybeAppendElement(mCaptionDiv);
   131 }
   133 void
   134 nsVideoFrame::DestroyFrom(nsIFrame* aDestructRoot)
   135 {
   136   nsContentUtils::DestroyAnonymousContent(&mCaptionDiv);
   137   nsContentUtils::DestroyAnonymousContent(&mVideoControls);
   138   nsContentUtils::DestroyAnonymousContent(&mPosterImage);
   139   nsContainerFrame::DestroyFrom(aDestructRoot);
   140 }
   142 bool
   143 nsVideoFrame::IsLeaf() const
   144 {
   145   return true;
   146 }
   148 // Return the largest rectangle that fits in aRect and has the
   149 // same aspect ratio as aRatio, centered at the center of aRect
   150 static gfxRect
   151 CorrectForAspectRatio(const gfxRect& aRect, const nsIntSize& aRatio)
   152 {
   153   NS_ASSERTION(aRatio.width > 0 && aRatio.height > 0 && !aRect.IsEmpty(),
   154                "Nothing to draw");
   155   // Choose scale factor that scales aRatio to just fit into aRect
   156   gfxFloat scale =
   157     std::min(aRect.Width()/aRatio.width, aRect.Height()/aRatio.height);
   158   gfxSize scaledRatio(scale*aRatio.width, scale*aRatio.height);
   159   gfxPoint topLeft((aRect.Width() - scaledRatio.width)/2,
   160                    (aRect.Height() - scaledRatio.height)/2);
   161   return gfxRect(aRect.TopLeft() + topLeft, scaledRatio);
   162 }
   164 already_AddRefed<Layer>
   165 nsVideoFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
   166                          LayerManager* aManager,
   167                          nsDisplayItem* aItem,
   168                          const ContainerLayerParameters& aContainerParameters)
   169 {
   170   nsRect area = GetContentRect() - GetPosition() + aItem->ToReferenceFrame();
   171   HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
   172   nsIntSize videoSize;
   173   if (NS_FAILED(element->GetVideoSize(&videoSize)) || area.IsEmpty()) {
   174     return nullptr;
   175   }
   177   nsRefPtr<ImageContainer> container = element->GetImageContainer();
   178   if (!container)
   179     return nullptr;
   181   // Retrieve the size of the decoded video frame, before being scaled
   182   // by pixel aspect ratio.
   183   mozilla::gfx::IntSize frameSize = container->GetCurrentSize();
   184   if (frameSize.width == 0 || frameSize.height == 0) {
   185     // No image, or zero-sized image. No point creating a layer.
   186     return nullptr;
   187   }
   189   // Compute the rectangle in which to paint the video. We need to use
   190   // the largest rectangle that fills our content-box and has the
   191   // correct aspect ratio.
   192   nsPresContext* presContext = PresContext();
   193   gfxRect r = gfxRect(presContext->AppUnitsToGfxUnits(area.x),
   194                       presContext->AppUnitsToGfxUnits(area.y),
   195                       presContext->AppUnitsToGfxUnits(area.width),
   196                       presContext->AppUnitsToGfxUnits(area.height));
   197   r = CorrectForAspectRatio(r, videoSize);
   198   r.Round();
   199   if (r.IsEmpty()) {
   200     return nullptr;
   201   }
   202   IntSize scaleHint(static_cast<int32_t>(r.Width()),
   203                     static_cast<int32_t>(r.Height()));
   204   container->SetScaleHint(scaleHint);
   206   nsRefPtr<ImageLayer> layer = static_cast<ImageLayer*>
   207     (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aItem));
   208   if (!layer) {
   209     layer = aManager->CreateImageLayer();
   210     if (!layer)
   211       return nullptr;
   212   }
   214   layer->SetContainer(container);
   215   layer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this));
   216   layer->SetContentFlags(Layer::CONTENT_OPAQUE);
   217   // Set a transform on the layer to draw the video in the right place
   218   gfx::Matrix transform;
   219   gfxPoint p = r.TopLeft() + aContainerParameters.mOffset;
   220   transform.Translate(p.x, p.y);
   221   transform.Scale(r.Width()/frameSize.width, r.Height()/frameSize.height);
   222   layer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
   223   layer->SetVisibleRegion(nsIntRect(0, 0, frameSize.width, frameSize.height));
   224   nsRefPtr<Layer> result = layer.forget();
   225   return result.forget();
   226 }
   228 class DispatchResizeToControls : public nsRunnable
   229 {
   230 public:
   231   DispatchResizeToControls(nsIContent* aContent)
   232     : mContent(aContent) {}
   233   NS_IMETHOD Run() MOZ_OVERRIDE {
   234     nsContentUtils::DispatchTrustedEvent(mContent->OwnerDoc(), mContent,
   235                                          NS_LITERAL_STRING("resizevideocontrols"),
   236                                          false, false);
   237     return NS_OK;
   238   }
   239   nsCOMPtr<nsIContent> mContent;
   240 };
   242 nsresult
   243 nsVideoFrame::Reflow(nsPresContext*           aPresContext,
   244                      nsHTMLReflowMetrics&     aMetrics,
   245                      const nsHTMLReflowState& aReflowState,
   246                      nsReflowStatus&          aStatus)
   247 {
   248   DO_GLOBAL_REFLOW_COUNT("nsVideoFrame");
   249   DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
   250   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
   251                   ("enter nsVideoFrame::Reflow: availSize=%d,%d",
   252                   aReflowState.AvailableWidth(), aReflowState.AvailableHeight()));
   254   NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
   256   aStatus = NS_FRAME_COMPLETE;
   258   aMetrics.Width() = aReflowState.ComputedWidth();
   259   aMetrics.Height() = aReflowState.ComputedHeight();
   261   // stash this away so we can compute our inner area later
   262   mBorderPadding   = aReflowState.ComputedPhysicalBorderPadding();
   264   aMetrics.Width() += mBorderPadding.left + mBorderPadding.right;
   265   aMetrics.Height() += mBorderPadding.top + mBorderPadding.bottom;
   267   // Reflow the child frames. We may have up to two, an image frame
   268   // which is the poster, and a box frame, which is the video controls.
   269   for (nsIFrame *child = mFrames.FirstChild();
   270        child;
   271        child = child->GetNextSibling()) {
   272     if (child->GetContent() == mPosterImage) {
   273       // Reflow the poster frame.
   274       nsImageFrame* imageFrame = static_cast<nsImageFrame*>(child);
   275       nsHTMLReflowMetrics kidDesiredSize(aReflowState);
   276       nsSize availableSize = nsSize(aReflowState.AvailableWidth(),
   277                                     aReflowState.AvailableHeight());
   278       nsHTMLReflowState kidReflowState(aPresContext,
   279                                        aReflowState,
   280                                        imageFrame,
   281                                        availableSize,
   282                                        aMetrics.Width(),
   283                                        aMetrics.Height());
   285       uint32_t posterHeight, posterWidth;
   286       nsSize scaledPosterSize(0, 0);
   287       nsSize computedArea(aReflowState.ComputedWidth(), aReflowState.ComputedHeight());
   288       nsPoint posterTopLeft(0, 0);
   290       nsCOMPtr<nsIDOMHTMLImageElement> posterImage = do_QueryInterface(mPosterImage);
   291       NS_ENSURE_TRUE(posterImage, NS_ERROR_FAILURE);
   292       posterImage->GetNaturalHeight(&posterHeight);
   293       posterImage->GetNaturalWidth(&posterWidth);
   295       if (ShouldDisplayPoster() && posterHeight && posterWidth) {
   296         gfxFloat scale =
   297           std::min(static_cast<float>(computedArea.width)/nsPresContext::CSSPixelsToAppUnits(static_cast<float>(posterWidth)),
   298                  static_cast<float>(computedArea.height)/nsPresContext::CSSPixelsToAppUnits(static_cast<float>(posterHeight)));
   299         gfxSize scaledRatio = gfxSize(scale*posterWidth, scale*posterHeight);
   300         scaledPosterSize.width = nsPresContext::CSSPixelsToAppUnits(static_cast<float>(scaledRatio.width));
   301         scaledPosterSize.height = nsPresContext::CSSPixelsToAppUnits(static_cast<int32_t>(scaledRatio.height));
   302       }
   303       kidReflowState.SetComputedWidth(scaledPosterSize.width);
   304       kidReflowState.SetComputedHeight(scaledPosterSize.height);
   305       posterTopLeft.x = ((computedArea.width - scaledPosterSize.width) / 2) + mBorderPadding.left;
   306       posterTopLeft.y = ((computedArea.height - scaledPosterSize.height) / 2) + mBorderPadding.top;
   308       ReflowChild(imageFrame, aPresContext, kidDesiredSize, kidReflowState,
   309                         posterTopLeft.x, posterTopLeft.y, 0, aStatus);
   310       FinishReflowChild(imageFrame, aPresContext, kidDesiredSize, &kidReflowState,
   311                         posterTopLeft.x, posterTopLeft.y, 0);
   312     } else if (child->GetContent() == mVideoControls) {
   313       // Reflow the video controls frame.
   314       nsBoxLayoutState boxState(PresContext(), aReflowState.rendContext);
   315       nsSize size = child->GetSize();
   316       nsBoxFrame::LayoutChildAt(boxState,
   317                                 child,
   318                                 nsRect(mBorderPadding.left,
   319                                        mBorderPadding.top,
   320                                        aReflowState.ComputedWidth(),
   321                                        aReflowState.ComputedHeight()));
   322       if (child->GetSize() != size) {
   323         nsRefPtr<nsRunnable> event = new DispatchResizeToControls(child->GetContent());
   324         nsContentUtils::AddScriptRunner(event);
   325       }
   326     } else if (child->GetContent() == mCaptionDiv) {
   327       // Reflow to caption div
   328       nsHTMLReflowMetrics kidDesiredSize(aReflowState);
   329       nsSize availableSize = nsSize(aReflowState.AvailableWidth(),
   330                                     aReflowState.AvailableHeight());
   331       nsHTMLReflowState kidReflowState(aPresContext,
   332                                        aReflowState,
   333                                        child,
   334                                        availableSize,
   335                                        aMetrics.Width(),
   336                                        aMetrics.Height());
   337       nsSize size(aReflowState.ComputedWidth(), aReflowState.ComputedHeight());
   338       size.width -= kidReflowState.ComputedPhysicalBorderPadding().LeftRight();
   339       size.height -= kidReflowState.ComputedPhysicalBorderPadding().TopBottom();
   341       kidReflowState.SetComputedWidth(std::max(size.width, 0));
   342       kidReflowState.SetComputedHeight(std::max(size.height, 0));
   344       ReflowChild(child, aPresContext, kidDesiredSize, kidReflowState,
   345                   mBorderPadding.left, mBorderPadding.top, 0, aStatus);
   346       FinishReflowChild(child, aPresContext,
   347                         kidDesiredSize, &kidReflowState,
   348                         mBorderPadding.left, mBorderPadding.top, 0);
   349     }
   350   }
   351   aMetrics.SetOverflowAreasToDesiredBounds();
   353   FinishAndStoreOverflow(&aMetrics);
   355   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
   356                   ("exit nsVideoFrame::Reflow: size=%d,%d",
   357                   aMetrics.Width(), aMetrics.Height()));
   358   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
   360   return NS_OK;
   361 }
   363 class nsDisplayVideo : public nsDisplayItem {
   364 public:
   365   nsDisplayVideo(nsDisplayListBuilder* aBuilder, nsVideoFrame* aFrame)
   366     : nsDisplayItem(aBuilder, aFrame)
   367   {
   368     MOZ_COUNT_CTOR(nsDisplayVideo);
   369   }
   370 #ifdef NS_BUILD_REFCNT_LOGGING
   371   virtual ~nsDisplayVideo() {
   372     MOZ_COUNT_DTOR(nsDisplayVideo);
   373   }
   374 #endif
   376   NS_DISPLAY_DECL_NAME("Video", TYPE_VIDEO)
   378   // It would be great if we could override GetOpaqueRegion to return nonempty here,
   379   // but it's probably not safe to do so in general. Video frames are
   380   // updated asynchronously from decoder threads, and it's possible that
   381   // we might have an opaque video frame when GetOpaqueRegion is called, but
   382   // when we come to paint, the video frame is transparent or has gone
   383   // away completely (e.g. because of a decoder error). The problem would
   384   // be especially acute if we have off-main-thread rendering.
   386   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) MOZ_OVERRIDE
   387   {
   388     *aSnap = true;
   389     nsIFrame* f = Frame();
   390     return f->GetContentRect() - f->GetPosition() + ToReferenceFrame();
   391   }
   393   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
   394                                              LayerManager* aManager,
   395                                              const ContainerLayerParameters& aContainerParameters) MOZ_OVERRIDE
   396   {
   397     return static_cast<nsVideoFrame*>(mFrame)->BuildLayer(aBuilder, aManager, this, aContainerParameters);
   398   }
   400   virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
   401                                    LayerManager* aManager,
   402                                    const ContainerLayerParameters& aParameters) MOZ_OVERRIDE
   403   {
   404     if (aManager->IsCompositingCheap()) {
   405       // Since ImageLayers don't require additional memory of the
   406       // video frames we have to have anyway, we can't save much by
   407       // making layers inactive. Also, for many accelerated layer
   408       // managers calling imageContainer->GetCurrentAsSurface can be
   409       // very expensive. So just always be active when compositing is
   410       // cheap (i.e. hardware accelerated).
   411       return LAYER_ACTIVE;
   412     }
   413     HTMLMediaElement* elem =
   414       static_cast<HTMLMediaElement*>(mFrame->GetContent());
   415     return elem->IsPotentiallyPlaying() ? LAYER_ACTIVE_FORCE : LAYER_INACTIVE;
   416   }
   417 };
   419 void
   420 nsVideoFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
   421                                const nsRect&           aDirtyRect,
   422                                const nsDisplayListSet& aLists)
   423 {
   424   if (!IsVisibleForPainting(aBuilder))
   425     return;
   427   DO_GLOBAL_REFLOW_COUNT_DSP("nsVideoFrame");
   429   DisplayBorderBackgroundOutline(aBuilder, aLists);
   431   DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
   432     clip(aBuilder, this, DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT);
   434   if (HasVideoElement() && !ShouldDisplayPoster()) {
   435     aLists.Content()->AppendNewToTop(
   436       new (aBuilder) nsDisplayVideo(aBuilder, this));
   437   }
   439   // Add child frames to display list. We expect various children,
   440   // but only want to draw mPosterImage conditionally. Others we
   441   // always add to the display list.
   442   for (nsIFrame *child = mFrames.FirstChild();
   443        child;
   444        child = child->GetNextSibling()) {
   445     if (child->GetContent() != mPosterImage || ShouldDisplayPoster()) {
   446       child->BuildDisplayListForStackingContext(aBuilder,
   447                                                 aDirtyRect - child->GetOffsetTo(this),
   448                                                 aLists.Content());
   449     } else if (child->GetType() == nsGkAtoms::boxFrame) {
   450       child->BuildDisplayListForStackingContext(aBuilder,
   451                                                 aDirtyRect - child->GetOffsetTo(this),
   452                                                 aLists.Content());
   453     }
   454   }
   455 }
   457 nsIAtom*
   458 nsVideoFrame::GetType() const
   459 {
   460   return nsGkAtoms::HTMLVideoFrame;
   461 }
   463 #ifdef ACCESSIBILITY
   464 a11y::AccType
   465 nsVideoFrame::AccessibleType()
   466 {
   467   return a11y::eHTMLMediaType;
   468 }
   469 #endif
   471 #ifdef DEBUG_FRAME_DUMP
   472 nsresult
   473 nsVideoFrame::GetFrameName(nsAString& aResult) const
   474 {
   475   return MakeFrameName(NS_LITERAL_STRING("HTMLVideo"), aResult);
   476 }
   477 #endif
   479 nsSize nsVideoFrame::ComputeSize(nsRenderingContext *aRenderingContext,
   480                                      nsSize aCBSize,
   481                                      nscoord aAvailableWidth,
   482                                      nsSize aMargin,
   483                                      nsSize aBorder,
   484                                      nsSize aPadding,
   485                                      uint32_t aFlags)
   486 {
   487   nsSize size = GetVideoIntrinsicSize(aRenderingContext);
   489   IntrinsicSize intrinsicSize;
   490   intrinsicSize.width.SetCoordValue(size.width);
   491   intrinsicSize.height.SetCoordValue(size.height);
   493   // Only video elements have an intrinsic ratio.
   494   nsSize intrinsicRatio = HasVideoElement() ? size : nsSize(0, 0);
   496   return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(aRenderingContext,
   497                                                            this,
   498                                                            intrinsicSize,
   499                                                            intrinsicRatio,
   500                                                            aCBSize,
   501                                                            aMargin,
   502                                                            aBorder,
   503                                                            aPadding);
   504 }
   506 nscoord nsVideoFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
   507 {
   508   nscoord result = GetVideoIntrinsicSize(aRenderingContext).width;
   509   DISPLAY_MIN_WIDTH(this, result);
   510   return result;
   511 }
   513 nscoord nsVideoFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
   514 {
   515   nscoord result = GetVideoIntrinsicSize(aRenderingContext).width;
   516   DISPLAY_PREF_WIDTH(this, result);
   517   return result;
   518 }
   520 nsSize nsVideoFrame::GetIntrinsicRatio()
   521 {
   522   if (!HasVideoElement()) {
   523     // Audio elements have no intrinsic ratio.
   524     return nsSize(0, 0);
   525   }
   527   return GetVideoIntrinsicSize(nullptr);
   528 }
   530 bool nsVideoFrame::ShouldDisplayPoster()
   531 {
   532   if (!HasVideoElement())
   533     return false;
   535   HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
   536   if (element->GetPlayedOrSeeked() && HasVideoData())
   537     return false;
   539   nsCOMPtr<nsIImageLoadingContent> imgContent = do_QueryInterface(mPosterImage);
   540   NS_ENSURE_TRUE(imgContent, false);
   542   nsCOMPtr<imgIRequest> request;
   543   nsresult res = imgContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
   544                                         getter_AddRefs(request));
   545   if (NS_FAILED(res) || !request) {
   546     return false;
   547   }
   549   uint32_t status = 0;
   550   res = request->GetImageStatus(&status);
   551   if (NS_FAILED(res) || (status & imgIRequest::STATUS_ERROR))
   552     return false;
   554   return true;
   555 }
   557 nsSize
   558 nsVideoFrame::GetVideoIntrinsicSize(nsRenderingContext *aRenderingContext)
   559 {
   560   // Defaulting size to 300x150 if no size given.
   561   nsIntSize size(300, 150);
   563   if (!HasVideoElement()) {
   564     if (!mFrames.FirstChild()) {
   565       return nsSize(0, 0);
   566     }
   568     // Ask the controls frame what its preferred height is
   569     nsBoxLayoutState boxState(PresContext(), aRenderingContext, 0);
   570     nscoord prefHeight = mFrames.LastChild()->GetPrefSize(boxState).height;
   571     return nsSize(nsPresContext::CSSPixelsToAppUnits(size.width), prefHeight);
   572   }
   574   HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
   575   if (NS_FAILED(element->GetVideoSize(&size)) && ShouldDisplayPoster()) {
   576     // Use the poster image frame's size.
   577     nsIFrame *child = mPosterImage->GetPrimaryFrame();
   578     nsImageFrame* imageFrame = do_QueryFrame(child);
   579     nsSize imgsize;
   580     if (NS_SUCCEEDED(imageFrame->GetIntrinsicImageSize(imgsize))) {
   581       return imgsize;
   582     }
   583   }
   585   return nsSize(nsPresContext::CSSPixelsToAppUnits(size.width),
   586                 nsPresContext::CSSPixelsToAppUnits(size.height));
   587 }
   589 void
   590 nsVideoFrame::UpdatePosterSource(bool aNotify)
   591 {
   592   NS_ASSERTION(HasVideoElement(), "Only call this on <video> elements.");
   593   HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
   595   if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::poster)) {
   596     nsAutoString posterStr;
   597     element->GetPoster(posterStr);
   598     mPosterImage->SetAttr(kNameSpaceID_None,
   599                           nsGkAtoms::src,
   600                           posterStr,
   601                           aNotify);
   602   } else {
   603     mPosterImage->UnsetAttr(kNameSpaceID_None, nsGkAtoms::poster, aNotify);
   604   }
   605 }
   607 nsresult
   608 nsVideoFrame::AttributeChanged(int32_t aNameSpaceID,
   609                                nsIAtom* aAttribute,
   610                                int32_t aModType)
   611 {
   612   if (aAttribute == nsGkAtoms::poster && HasVideoElement()) {
   613     UpdatePosterSource(true);
   614   }
   615   return nsContainerFrame::AttributeChanged(aNameSpaceID,
   616                                             aAttribute,
   617                                             aModType);
   618 }
   620 bool nsVideoFrame::HasVideoElement() {
   621   nsCOMPtr<nsIDOMHTMLVideoElement> videoDomElement = do_QueryInterface(mContent);
   622   return videoDomElement != nullptr;
   623 }
   625 bool nsVideoFrame::HasVideoData()
   626 {
   627   if (!HasVideoElement())
   628     return false;
   629   HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
   630   nsIntSize size(0, 0);
   631   element->GetVideoSize(&size);
   632   return size != nsIntSize(0,0);
   633 }

mercurial