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