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 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla code.
17 : *
18 : * The Initial Developer of the Original Code is the Mozilla Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 2007
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Chris Double <chris.double@double.co.nz>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : /* rendering object for the HTML <video> element */
40 :
41 : #include "nsHTMLParts.h"
42 : #include "nsCOMPtr.h"
43 : #include "nsIServiceManager.h"
44 : #include "nsGkAtoms.h"
45 :
46 : #include "nsVideoFrame.h"
47 : #include "nsHTMLVideoElement.h"
48 : #include "nsIDOMHTMLVideoElement.h"
49 : #include "nsDisplayList.h"
50 : #include "gfxContext.h"
51 : #include "gfxImageSurface.h"
52 : #include "nsPresContext.h"
53 : #include "nsTransform2D.h"
54 : #include "nsContentCreatorFunctions.h"
55 : #include "nsBoxLayoutState.h"
56 : #include "nsBoxFrame.h"
57 : #include "nsImageFrame.h"
58 : #include "nsIImageLoadingContent.h"
59 : #include "nsCSSRendering.h"
60 : #include "nsContentUtils.h"
61 :
62 : #ifdef ACCESSIBILITY
63 : #include "nsAccessibilityService.h"
64 : #endif
65 :
66 : using namespace mozilla;
67 : using namespace mozilla::layers;
68 : using namespace mozilla::dom;
69 :
70 : nsIFrame*
71 0 : NS_NewHTMLVideoFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
72 : {
73 0 : return new (aPresShell) nsVideoFrame(aContext);
74 : }
75 :
76 0 : NS_IMPL_FRAMEARENA_HELPERS(nsVideoFrame)
77 :
78 0 : nsVideoFrame::nsVideoFrame(nsStyleContext* aContext) :
79 0 : nsContainerFrame(aContext)
80 : {
81 0 : }
82 :
83 0 : nsVideoFrame::~nsVideoFrame()
84 : {
85 0 : }
86 :
87 0 : NS_QUERYFRAME_HEAD(nsVideoFrame)
88 0 : NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
89 0 : NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
90 :
91 : nsresult
92 0 : nsVideoFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
93 : {
94 0 : nsNodeInfoManager *nodeInfoManager = GetContent()->GetCurrentDoc()->NodeInfoManager();
95 0 : nsCOMPtr<nsINodeInfo> nodeInfo;
96 0 : if (HasVideoElement()) {
97 : // Create an anonymous image element as a child to hold the poster
98 : // image. We may not have a poster image now, but one could be added
99 : // before we load, or on a subsequent load.
100 : nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::img,
101 : nsnull,
102 : kNameSpaceID_XHTML,
103 0 : nsIDOMNode::ELEMENT_NODE);
104 0 : NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
105 0 : Element* element = NS_NewHTMLImageElement(nodeInfo.forget());
106 0 : mPosterImage = element;
107 0 : NS_ENSURE_TRUE(mPosterImage, NS_ERROR_OUT_OF_MEMORY);
108 :
109 : // Push a null JSContext on the stack so that code that runs
110 : // within the below code doesn't think it's being called by
111 : // JS. See bug 604262.
112 0 : nsCxPusher pusher;
113 0 : pusher.PushNull();
114 :
115 : // Set the nsImageLoadingContent::ImageState() to 0. This means that the
116 : // image will always report its state as 0, so it will never be reframed
117 : // to show frames for loading or the broken image icon. This is important,
118 : // as the image is native anonymous, and so can't be reframed (currently).
119 0 : nsCOMPtr<nsIImageLoadingContent> imgContent = do_QueryInterface(mPosterImage);
120 0 : NS_ENSURE_TRUE(imgContent, NS_ERROR_FAILURE);
121 :
122 0 : imgContent->ForceImageState(true, 0);
123 : // And now have it update its internal state
124 0 : element->UpdateState(false);
125 :
126 0 : nsresult res = UpdatePosterSource(false);
127 0 : NS_ENSURE_SUCCESS(res,res);
128 :
129 0 : if (!aElements.AppendElement(mPosterImage))
130 0 : return NS_ERROR_OUT_OF_MEMORY;
131 : }
132 :
133 : // Set up "videocontrols" XUL element which will be XBL-bound to the
134 : // actual controls.
135 : nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::videocontrols,
136 : nsnull,
137 : kNameSpaceID_XUL,
138 0 : nsIDOMNode::ELEMENT_NODE);
139 0 : NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
140 :
141 0 : NS_TrustedNewXULElement(getter_AddRefs(mVideoControls), nodeInfo.forget());
142 0 : if (!aElements.AppendElement(mVideoControls))
143 0 : return NS_ERROR_OUT_OF_MEMORY;
144 :
145 0 : return NS_OK;
146 : }
147 :
148 : void
149 0 : nsVideoFrame::AppendAnonymousContentTo(nsBaseContentList& aElements,
150 : PRUint32 aFliter)
151 : {
152 0 : aElements.MaybeAppendElement(mPosterImage);
153 0 : aElements.MaybeAppendElement(mVideoControls);
154 0 : }
155 :
156 : void
157 0 : nsVideoFrame::DestroyFrom(nsIFrame* aDestructRoot)
158 : {
159 0 : nsContentUtils::DestroyAnonymousContent(&mVideoControls);
160 0 : nsContentUtils::DestroyAnonymousContent(&mPosterImage);
161 0 : nsContainerFrame::DestroyFrom(aDestructRoot);
162 0 : }
163 :
164 : bool
165 0 : nsVideoFrame::IsLeaf() const
166 : {
167 0 : return true;
168 : }
169 :
170 : // Return the largest rectangle that fits in aRect and has the
171 : // same aspect ratio as aRatio, centered at the center of aRect
172 : static gfxRect
173 0 : CorrectForAspectRatio(const gfxRect& aRect, const nsIntSize& aRatio)
174 : {
175 0 : NS_ASSERTION(aRatio.width > 0 && aRatio.height > 0 && !aRect.IsEmpty(),
176 : "Nothing to draw");
177 : // Choose scale factor that scales aRatio to just fit into aRect
178 : gfxFloat scale =
179 0 : NS_MIN(aRect.Width()/aRatio.width, aRect.Height()/aRatio.height);
180 0 : gfxSize scaledRatio(scale*aRatio.width, scale*aRatio.height);
181 0 : gfxPoint topLeft((aRect.Width() - scaledRatio.width)/2,
182 0 : (aRect.Height() - scaledRatio.height)/2);
183 0 : return gfxRect(aRect.TopLeft() + topLeft, scaledRatio);
184 : }
185 :
186 : already_AddRefed<Layer>
187 0 : nsVideoFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
188 : LayerManager* aManager,
189 : nsDisplayItem* aItem)
190 : {
191 0 : nsRect area = GetContentRect() - GetPosition() + aItem->ToReferenceFrame();
192 0 : nsHTMLVideoElement* element = static_cast<nsHTMLVideoElement*>(GetContent());
193 0 : nsIntSize videoSize = element->GetVideoSize(nsIntSize(0, 0));
194 0 : if (videoSize.width <= 0 || videoSize.height <= 0 || area.IsEmpty())
195 0 : return nsnull;
196 :
197 0 : nsRefPtr<ImageContainer> container = element->GetImageContainer();
198 :
199 : // Retrieve the size of the decoded video frame, before being scaled
200 : // by pixel aspect ratio.
201 0 : gfxIntSize frameSize = container->GetCurrentSize();
202 0 : if (frameSize.width == 0 || frameSize.height == 0) {
203 : // No image, or zero-sized image. No point creating a layer.
204 0 : return nsnull;
205 : }
206 :
207 : // Compute the rectangle in which to paint the video. We need to use
208 : // the largest rectangle that fills our content-box and has the
209 : // correct aspect ratio.
210 0 : nsPresContext* presContext = PresContext();
211 : gfxRect r = gfxRect(presContext->AppUnitsToGfxUnits(area.x),
212 : presContext->AppUnitsToGfxUnits(area.y),
213 : presContext->AppUnitsToGfxUnits(area.width),
214 0 : presContext->AppUnitsToGfxUnits(area.height));
215 0 : r = CorrectForAspectRatio(r, videoSize);
216 0 : r.Round();
217 0 : gfxIntSize scaleHint(static_cast<PRInt32>(r.Width()),
218 0 : static_cast<PRInt32>(r.Height()));
219 0 : container->SetScaleHint(scaleHint);
220 :
221 : nsRefPtr<ImageLayer> layer = static_cast<ImageLayer*>
222 0 : (aBuilder->LayerBuilder()->GetLeafLayerFor(aBuilder, aManager, aItem));
223 0 : if (!layer) {
224 0 : layer = aManager->CreateImageLayer();
225 0 : if (!layer)
226 0 : return nsnull;
227 : }
228 :
229 0 : layer->SetContainer(container);
230 0 : layer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this));
231 0 : layer->SetContentFlags(Layer::CONTENT_OPAQUE);
232 : // Set a transform on the layer to draw the video in the right place
233 0 : gfxMatrix transform;
234 0 : transform.Translate(r.TopLeft());
235 0 : transform.Scale(r.Width()/frameSize.width, r.Height()/frameSize.height);
236 0 : layer->SetTransform(gfx3DMatrix::From2D(transform));
237 0 : layer->SetVisibleRegion(nsIntRect(0, 0, frameSize.width, frameSize.height));
238 0 : nsRefPtr<Layer> result = layer.forget();
239 0 : return result.forget();
240 : }
241 :
242 : NS_IMETHODIMP
243 0 : nsVideoFrame::Reflow(nsPresContext* aPresContext,
244 : nsHTMLReflowMetrics& aMetrics,
245 : const nsHTMLReflowState& aReflowState,
246 : nsReflowStatus& aStatus)
247 : {
248 0 : DO_GLOBAL_REFLOW_COUNT("nsVideoFrame");
249 0 : DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
250 0 : NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
251 : ("enter nsVideoFrame::Reflow: availSize=%d,%d",
252 : aReflowState.availableWidth, aReflowState.availableHeight));
253 :
254 0 : NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
255 :
256 0 : aStatus = NS_FRAME_COMPLETE;
257 :
258 0 : aMetrics.width = aReflowState.ComputedWidth();
259 0 : aMetrics.height = aReflowState.ComputedHeight();
260 :
261 : // stash this away so we can compute our inner area later
262 0 : mBorderPadding = aReflowState.mComputedBorderPadding;
263 :
264 0 : aMetrics.width += mBorderPadding.left + mBorderPadding.right;
265 0 : aMetrics.height += mBorderPadding.top + mBorderPadding.bottom;
266 :
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 0 : for (nsIFrame *child = mFrames.FirstChild();
270 : child;
271 : child = child->GetNextSibling()) {
272 0 : if (child->GetType() == nsGkAtoms::imageFrame) {
273 : // Reflow the poster frame.
274 0 : nsImageFrame* imageFrame = static_cast<nsImageFrame*>(child);
275 0 : nsHTMLReflowMetrics kidDesiredSize;
276 : nsSize availableSize = nsSize(aReflowState.availableWidth,
277 0 : aReflowState.availableHeight);
278 : nsHTMLReflowState kidReflowState(aPresContext,
279 : aReflowState,
280 : imageFrame,
281 : availableSize,
282 : aMetrics.width,
283 0 : aMetrics.height);
284 0 : if (ShouldDisplayPoster()) {
285 0 : kidReflowState.SetComputedWidth(aReflowState.ComputedWidth());
286 0 : kidReflowState.SetComputedHeight(aReflowState.ComputedHeight());
287 : } else {
288 0 : kidReflowState.SetComputedWidth(0);
289 0 : kidReflowState.SetComputedHeight(0);
290 : }
291 : ReflowChild(imageFrame, aPresContext, kidDesiredSize, kidReflowState,
292 0 : mBorderPadding.left, mBorderPadding.top, 0, aStatus);
293 : FinishReflowChild(imageFrame, aPresContext,
294 : &kidReflowState, kidDesiredSize,
295 0 : mBorderPadding.left, mBorderPadding.top, 0);
296 0 : } else if (child->GetType() == nsGkAtoms::boxFrame) {
297 : // Reflow the video controls frame.
298 0 : nsBoxLayoutState boxState(PresContext(), aReflowState.rendContext);
299 : nsBoxFrame::LayoutChildAt(boxState,
300 : child,
301 : nsRect(mBorderPadding.left,
302 : mBorderPadding.top,
303 : aReflowState.ComputedWidth(),
304 0 : aReflowState.ComputedHeight()));
305 : }
306 : }
307 0 : aMetrics.SetOverflowAreasToDesiredBounds();
308 :
309 0 : FinishAndStoreOverflow(&aMetrics);
310 :
311 0 : if (mRect.width != aMetrics.width || mRect.height != aMetrics.height) {
312 0 : Invalidate(nsRect(0, 0, mRect.width, mRect.height));
313 : }
314 :
315 0 : NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
316 : ("exit nsVideoFrame::Reflow: size=%d,%d",
317 : aMetrics.width, aMetrics.height));
318 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
319 :
320 0 : return NS_OK;
321 : }
322 :
323 : class nsDisplayVideo : public nsDisplayItem {
324 : public:
325 0 : nsDisplayVideo(nsDisplayListBuilder* aBuilder, nsVideoFrame* aFrame)
326 0 : : nsDisplayItem(aBuilder, aFrame)
327 : {
328 0 : MOZ_COUNT_CTOR(nsDisplayVideo);
329 0 : }
330 : #ifdef NS_BUILD_REFCNT_LOGGING
331 0 : virtual ~nsDisplayVideo() {
332 0 : MOZ_COUNT_DTOR(nsDisplayVideo);
333 0 : }
334 : #endif
335 :
336 0 : NS_DISPLAY_DECL_NAME("Video", TYPE_VIDEO)
337 :
338 : // It would be great if we could override GetOpaqueRegion to return nonempty here,
339 : // but it's probably not safe to do so in general. Video frames are
340 : // updated asynchronously from decoder threads, and it's possible that
341 : // we might have an opaque video frame when GetOpaqueRegion is called, but
342 : // when we come to paint, the video frame is transparent or has gone
343 : // away completely (e.g. because of a decoder error). The problem would
344 : // be especially acute if we have off-main-thread rendering.
345 :
346 0 : virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder)
347 : {
348 0 : nsIFrame* f = GetUnderlyingFrame();
349 0 : return f->GetContentRect() - f->GetPosition() + ToReferenceFrame();
350 : }
351 :
352 0 : virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
353 : LayerManager* aManager,
354 : const ContainerParameters& aContainerParameters)
355 : {
356 0 : return static_cast<nsVideoFrame*>(mFrame)->BuildLayer(aBuilder, aManager, this);
357 : }
358 :
359 0 : virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
360 : LayerManager* aManager)
361 : {
362 0 : if (aManager->GetBackendType() != LayerManager::LAYERS_BASIC) {
363 : // For non-basic layer managers we can assume that compositing
364 : // layers is very cheap, and since ImageLayers don't require
365 : // additional memory of the video frames we have to have anyway,
366 : // we can't save much by making layers inactive. Also, for many
367 : // accelerated layer managers calling
368 : // imageContainer->GetCurrentAsSurface can be very expensive. So
369 : // just always be active for these managers.
370 0 : return LAYER_ACTIVE;
371 : }
372 : nsHTMLMediaElement* elem =
373 0 : static_cast<nsHTMLMediaElement*>(mFrame->GetContent());
374 0 : return elem->IsPotentiallyPlaying() ? LAYER_ACTIVE : LAYER_INACTIVE;
375 : }
376 : };
377 :
378 : NS_IMETHODIMP
379 0 : nsVideoFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
380 : const nsRect& aDirtyRect,
381 : const nsDisplayListSet& aLists)
382 : {
383 0 : if (!IsVisibleForPainting(aBuilder))
384 0 : return NS_OK;
385 :
386 0 : DO_GLOBAL_REFLOW_COUNT_DSP("nsVideoFrame");
387 :
388 0 : nsresult rv = DisplayBorderBackgroundOutline(aBuilder, aLists);
389 0 : NS_ENSURE_SUCCESS(rv, rv);
390 :
391 0 : nsDisplayList replacedContent;
392 :
393 0 : if (HasVideoElement() && !ShouldDisplayPoster()) {
394 : rv = replacedContent.AppendNewToTop(
395 0 : new (aBuilder) nsDisplayVideo(aBuilder, this));
396 0 : NS_ENSURE_SUCCESS(rv, rv);
397 : }
398 :
399 : // Add child frames to display list. We expect up to two children, an image
400 : // frame for the poster, and the box frame for the video controls.
401 0 : for (nsIFrame *child = mFrames.FirstChild();
402 : child;
403 : child = child->GetNextSibling()) {
404 0 : if (child->GetType() == nsGkAtoms::imageFrame && ShouldDisplayPoster()) {
405 : rv = child->BuildDisplayListForStackingContext(aBuilder,
406 0 : aDirtyRect - child->GetOffsetTo(this),
407 0 : &replacedContent);
408 0 : NS_ENSURE_SUCCESS(rv,rv);
409 0 : } else if (child->GetType() == nsGkAtoms::boxFrame) {
410 : rv = child->BuildDisplayListForStackingContext(aBuilder,
411 0 : aDirtyRect - child->GetOffsetTo(this),
412 0 : &replacedContent);
413 0 : NS_ENSURE_SUCCESS(rv,rv);
414 : }
415 : }
416 :
417 0 : WrapReplacedContentForBorderRadius(aBuilder, &replacedContent, aLists);
418 :
419 0 : return NS_OK;
420 : }
421 :
422 : nsIAtom*
423 0 : nsVideoFrame::GetType() const
424 : {
425 0 : return nsGkAtoms::HTMLVideoFrame;
426 : }
427 :
428 : #ifdef ACCESSIBILITY
429 : already_AddRefed<nsAccessible>
430 0 : nsVideoFrame::CreateAccessible()
431 : {
432 0 : nsAccessibilityService* accService = nsIPresShell::AccService();
433 : return accService ?
434 0 : accService->CreateHTMLMediaAccessible(mContent, PresContext()->PresShell()) :
435 0 : nsnull;
436 : }
437 : #endif
438 :
439 : #ifdef DEBUG
440 : NS_IMETHODIMP
441 0 : nsVideoFrame::GetFrameName(nsAString& aResult) const
442 : {
443 0 : return MakeFrameName(NS_LITERAL_STRING("HTMLVideo"), aResult);
444 : }
445 : #endif
446 :
447 0 : nsSize nsVideoFrame::ComputeSize(nsRenderingContext *aRenderingContext,
448 : nsSize aCBSize,
449 : nscoord aAvailableWidth,
450 : nsSize aMargin,
451 : nsSize aBorder,
452 : nsSize aPadding,
453 : bool aShrinkWrap)
454 : {
455 0 : nsSize size = GetVideoIntrinsicSize(aRenderingContext);
456 :
457 0 : IntrinsicSize intrinsicSize;
458 0 : intrinsicSize.width.SetCoordValue(size.width);
459 0 : intrinsicSize.height.SetCoordValue(size.height);
460 :
461 0 : nsSize& intrinsicRatio = size; // won't actually be used
462 :
463 : return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(aRenderingContext,
464 : this,
465 : intrinsicSize,
466 : intrinsicRatio,
467 : aCBSize,
468 : aMargin,
469 : aBorder,
470 0 : aPadding);
471 : }
472 :
473 0 : nscoord nsVideoFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
474 : {
475 0 : nscoord result = GetVideoIntrinsicSize(aRenderingContext).width;
476 0 : DISPLAY_MIN_WIDTH(this, result);
477 0 : return result;
478 : }
479 :
480 0 : nscoord nsVideoFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
481 : {
482 0 : nscoord result = GetVideoIntrinsicSize(aRenderingContext).width;
483 0 : DISPLAY_PREF_WIDTH(this, result);
484 0 : return result;
485 : }
486 :
487 0 : nsSize nsVideoFrame::GetIntrinsicRatio()
488 : {
489 0 : return GetVideoIntrinsicSize(nsnull);
490 : }
491 :
492 0 : bool nsVideoFrame::ShouldDisplayPoster()
493 : {
494 0 : if (!HasVideoElement())
495 0 : return false;
496 :
497 0 : nsHTMLVideoElement* element = static_cast<nsHTMLVideoElement*>(GetContent());
498 0 : if (element->GetPlayedOrSeeked() && HasVideoData())
499 0 : return false;
500 :
501 0 : nsCOMPtr<nsIImageLoadingContent> imgContent = do_QueryInterface(mPosterImage);
502 0 : NS_ENSURE_TRUE(imgContent, false);
503 :
504 0 : nsCOMPtr<imgIRequest> request;
505 0 : nsresult res = imgContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
506 0 : getter_AddRefs(request));
507 0 : if (NS_FAILED(res) || !request) {
508 0 : return false;
509 : }
510 :
511 0 : PRUint32 status = 0;
512 0 : res = request->GetImageStatus(&status);
513 0 : if (NS_FAILED(res) || (status & imgIRequest::STATUS_ERROR))
514 0 : return false;
515 :
516 0 : return true;
517 : }
518 :
519 : nsSize
520 0 : nsVideoFrame::GetVideoIntrinsicSize(nsRenderingContext *aRenderingContext)
521 : {
522 : // Defaulting size to 300x150 if no size given.
523 0 : nsIntSize size(300, 150);
524 :
525 0 : if (ShouldDisplayPoster()) {
526 : // Use the poster image frame's size.
527 0 : nsIFrame *child = mFrames.FirstChild();
528 0 : if (child && child->GetType() == nsGkAtoms::imageFrame) {
529 0 : nsImageFrame* imageFrame = static_cast<nsImageFrame*>(child);
530 0 : nsSize imgsize;
531 0 : if (NS_SUCCEEDED(imageFrame->GetIntrinsicImageSize(imgsize))) {
532 0 : return imgsize;
533 : }
534 : }
535 : }
536 :
537 0 : if (!HasVideoElement()) {
538 0 : if (!aRenderingContext || !mFrames.FirstChild()) {
539 : // We just want our intrinsic ratio, but audio elements need no
540 : // intrinsic ratio, so just return "no ratio". Also, if there's
541 : // no controls frame, we prefer to be zero-sized.
542 0 : return nsSize(0, 0);
543 : }
544 :
545 : // Ask the controls frame what its preferred height is
546 0 : nsBoxLayoutState boxState(PresContext(), aRenderingContext, 0);
547 0 : nscoord prefHeight = mFrames.LastChild()->GetPrefSize(boxState).height;
548 0 : return nsSize(nsPresContext::CSSPixelsToAppUnits(size.width), prefHeight);
549 : }
550 :
551 0 : nsHTMLVideoElement* element = static_cast<nsHTMLVideoElement*>(GetContent());
552 0 : size = element->GetVideoSize(size);
553 :
554 : return nsSize(nsPresContext::CSSPixelsToAppUnits(size.width),
555 0 : nsPresContext::CSSPixelsToAppUnits(size.height));
556 : }
557 :
558 : nsresult
559 0 : nsVideoFrame::UpdatePosterSource(bool aNotify)
560 : {
561 0 : NS_ASSERTION(HasVideoElement(), "Only call this on <video> elements.");
562 0 : nsHTMLVideoElement* element = static_cast<nsHTMLVideoElement*>(GetContent());
563 :
564 0 : nsAutoString posterStr;
565 0 : element->GetPoster(posterStr);
566 : nsresult res = mPosterImage->SetAttr(kNameSpaceID_None,
567 : nsGkAtoms::src,
568 : posterStr,
569 0 : aNotify);
570 0 : NS_ENSURE_SUCCESS(res,res);
571 0 : return NS_OK;
572 : }
573 :
574 : NS_IMETHODIMP
575 0 : nsVideoFrame::AttributeChanged(PRInt32 aNameSpaceID,
576 : nsIAtom* aAttribute,
577 : PRInt32 aModType)
578 : {
579 0 : if (aAttribute == nsGkAtoms::poster && HasVideoElement()) {
580 0 : nsresult res = UpdatePosterSource(true);
581 0 : NS_ENSURE_SUCCESS(res,res);
582 : }
583 : return nsContainerFrame::AttributeChanged(aNameSpaceID,
584 : aAttribute,
585 0 : aModType);
586 : }
587 :
588 0 : bool nsVideoFrame::HasVideoElement() {
589 0 : nsCOMPtr<nsIDOMHTMLVideoElement> videoDomElement = do_QueryInterface(mContent);
590 0 : return videoDomElement != nsnull;
591 : }
592 :
593 0 : bool nsVideoFrame::HasVideoData()
594 : {
595 0 : if (!HasVideoElement())
596 0 : return false;
597 0 : nsHTMLVideoElement* element = static_cast<nsHTMLVideoElement*>(GetContent());
598 0 : nsIntSize size = element->GetVideoSize(nsIntSize(0,0));
599 0 : return size != nsIntSize(0,0);
600 : }
|