layout/generic/nsFrame.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:c2582ab2825a
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim:cindent:ts=2:et:sw=2:
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 /* base class of all rendering objects */
8
9 #include "nsFrame.h"
10
11 #include "mozilla/Attributes.h"
12 #include "mozilla/DebugOnly.h"
13
14 #include "nsCOMPtr.h"
15 #include "nsFrameList.h"
16 #include "nsPlaceholderFrame.h"
17 #include "nsIContent.h"
18 #include "nsContentUtils.h"
19 #include "nsIAtom.h"
20 #include "nsString.h"
21 #include "nsReadableUtils.h"
22 #include "nsStyleContext.h"
23 #include "nsTableOuterFrame.h"
24 #include "nsView.h"
25 #include "nsViewManager.h"
26 #include "nsIScrollableFrame.h"
27 #include "nsPresContext.h"
28 #include "nsStyleConsts.h"
29 #include "nsIPresShell.h"
30 #include "prlog.h"
31 #include "prprf.h"
32 #include <stdarg.h>
33 #include "nsFrameManager.h"
34 #include "nsLayoutUtils.h"
35
36 #include "nsIDOMNode.h"
37 #include "nsISelection.h"
38 #include "nsISelectionPrivate.h"
39 #include "nsFrameSelection.h"
40 #include "nsGkAtoms.h"
41 #include "nsCSSAnonBoxes.h"
42
43 #include "nsFrameTraversal.h"
44 #include "nsRange.h"
45 #include "nsITextControlFrame.h"
46 #include "nsNameSpaceManager.h"
47 #include "nsIPercentHeightObserver.h"
48 #include "nsStyleStructInlines.h"
49 #include <algorithm>
50
51 #include "nsBidiPresUtils.h"
52
53 // For triple-click pref
54 #include "imgIContainer.h"
55 #include "imgIRequest.h"
56 #include "nsError.h"
57 #include "nsContainerFrame.h"
58 #include "nsBoxLayoutState.h"
59 #include "nsBlockFrame.h"
60 #include "nsDisplayList.h"
61 #include "nsSVGIntegrationUtils.h"
62 #include "nsSVGEffects.h"
63 #include "nsChangeHint.h"
64 #include "nsDeckFrame.h"
65 #include "nsSubDocumentFrame.h"
66 #include "SVGTextFrame.h"
67
68 #include "gfxContext.h"
69 #include "nsRenderingContext.h"
70 #include "nsAbsoluteContainingBlock.h"
71 #include "StickyScrollContainer.h"
72 #include "nsFontInflationData.h"
73 #include "gfxASurface.h"
74 #include "nsRegion.h"
75 #include "nsIFrameInlines.h"
76
77 #include "mozilla/AsyncEventDispatcher.h"
78 #include "mozilla/EventListenerManager.h"
79 #include "mozilla/EventStateManager.h"
80 #include "mozilla/EventStates.h"
81 #include "mozilla/Preferences.h"
82 #include "mozilla/LookAndFeel.h"
83 #include "mozilla/MouseEvents.h"
84 #include "mozilla/css/ImageLoader.h"
85 #include "mozilla/gfx/Tools.h"
86 #include "nsPrintfCString.h"
87
88 using namespace mozilla;
89 using namespace mozilla::css;
90 using namespace mozilla::dom;
91 using namespace mozilla::layers;
92 using namespace mozilla::layout;
93
94 // Struct containing cached metrics for box-wrapped frames.
95 struct nsBoxLayoutMetrics
96 {
97 nsSize mPrefSize;
98 nsSize mMinSize;
99 nsSize mMaxSize;
100
101 nsSize mBlockMinSize;
102 nsSize mBlockPrefSize;
103 nscoord mBlockAscent;
104
105 nscoord mFlex;
106 nscoord mAscent;
107
108 nsSize mLastSize;
109 };
110
111 struct nsContentAndOffset
112 {
113 nsIContent* mContent;
114 int32_t mOffset;
115 };
116
117 // Some Misc #defines
118 #define SELECTION_DEBUG 0
119 #define FORCE_SELECTION_UPDATE 1
120 #define CALC_DEBUG 0
121
122
123 #include "nsILineIterator.h"
124
125 //non Hack prototypes
126 #if 0
127 static void RefreshContentFrames(nsPresContext* aPresContext, nsIContent * aStartContent, nsIContent * aEndContent);
128 #endif
129
130 #include "prenv.h"
131
132 // Formerly the nsIFrameDebug interface
133
134 #ifdef DEBUG
135 static bool gShowFrameBorders = false;
136
137 void nsFrame::ShowFrameBorders(bool aEnable)
138 {
139 gShowFrameBorders = aEnable;
140 }
141
142 bool nsFrame::GetShowFrameBorders()
143 {
144 return gShowFrameBorders;
145 }
146
147 static bool gShowEventTargetFrameBorder = false;
148
149 void nsFrame::ShowEventTargetFrameBorder(bool aEnable)
150 {
151 gShowEventTargetFrameBorder = aEnable;
152 }
153
154 bool nsFrame::GetShowEventTargetFrameBorder()
155 {
156 return gShowEventTargetFrameBorder;
157 }
158
159 /**
160 * Note: the log module is created during library initialization which
161 * means that you cannot perform logging before then.
162 */
163 static PRLogModuleInfo* gLogModule;
164
165 static PRLogModuleInfo* gStyleVerifyTreeLogModuleInfo;
166
167 static uint32_t gStyleVerifyTreeEnable = 0x55;
168
169 bool
170 nsFrame::GetVerifyStyleTreeEnable()
171 {
172 if (gStyleVerifyTreeEnable == 0x55) {
173 if (nullptr == gStyleVerifyTreeLogModuleInfo) {
174 gStyleVerifyTreeLogModuleInfo = PR_NewLogModule("styleverifytree");
175 gStyleVerifyTreeEnable = 0 != gStyleVerifyTreeLogModuleInfo->level;
176 }
177 }
178 return gStyleVerifyTreeEnable;
179 }
180
181 void
182 nsFrame::SetVerifyStyleTreeEnable(bool aEnabled)
183 {
184 gStyleVerifyTreeEnable = aEnabled;
185 }
186
187 PRLogModuleInfo*
188 nsFrame::GetLogModuleInfo()
189 {
190 if (nullptr == gLogModule) {
191 gLogModule = PR_NewLogModule("frame");
192 }
193 return gLogModule;
194 }
195
196 #endif
197
198 static void
199 DestroyAbsoluteContainingBlock(void* aPropertyValue)
200 {
201 delete static_cast<nsAbsoluteContainingBlock*>(aPropertyValue);
202 }
203
204 NS_DECLARE_FRAME_PROPERTY(AbsoluteContainingBlockProperty, DestroyAbsoluteContainingBlock)
205
206 bool
207 nsIFrame::HasAbsolutelyPositionedChildren() const {
208 return IsAbsoluteContainer() && GetAbsoluteContainingBlock()->HasAbsoluteFrames();
209 }
210
211 nsAbsoluteContainingBlock*
212 nsIFrame::GetAbsoluteContainingBlock() const {
213 NS_ASSERTION(IsAbsoluteContainer(), "The frame is not marked as an abspos container correctly");
214 nsAbsoluteContainingBlock* absCB = static_cast<nsAbsoluteContainingBlock*>
215 (Properties().Get(AbsoluteContainingBlockProperty()));
216 NS_ASSERTION(absCB, "The frame is marked as an abspos container but doesn't have the property");
217 return absCB;
218 }
219
220 void
221 nsIFrame::MarkAsAbsoluteContainingBlock()
222 {
223 MOZ_ASSERT(GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
224 NS_ASSERTION(!Properties().Get(AbsoluteContainingBlockProperty()),
225 "Already has an abs-pos containing block property?");
226 NS_ASSERTION(!HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN),
227 "Already has NS_FRAME_HAS_ABSPOS_CHILDREN state bit?");
228 AddStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
229 Properties().Set(AbsoluteContainingBlockProperty(), new nsAbsoluteContainingBlock(GetAbsoluteListID()));
230 }
231
232 void
233 nsIFrame::MarkAsNotAbsoluteContainingBlock()
234 {
235 NS_ASSERTION(!HasAbsolutelyPositionedChildren(), "Think of the children!");
236 NS_ASSERTION(Properties().Get(AbsoluteContainingBlockProperty()),
237 "Should have an abs-pos containing block property");
238 NS_ASSERTION(HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN),
239 "Should have NS_FRAME_HAS_ABSPOS_CHILDREN state bit");
240 MOZ_ASSERT(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN));
241 RemoveStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
242 Properties().Delete(AbsoluteContainingBlockProperty());
243 }
244
245 bool
246 nsIFrame::CheckAndClearPaintedState()
247 {
248 bool result = (GetStateBits() & NS_FRAME_PAINTED_THEBES);
249 RemoveStateBits(NS_FRAME_PAINTED_THEBES);
250
251 nsIFrame::ChildListIterator lists(this);
252 for (; !lists.IsDone(); lists.Next()) {
253 nsFrameList::Enumerator childFrames(lists.CurrentList());
254 for (; !childFrames.AtEnd(); childFrames.Next()) {
255 nsIFrame* child = childFrames.get();
256 if (child->CheckAndClearPaintedState()) {
257 result = true;
258 }
259 }
260 }
261 return result;
262 }
263
264 bool
265 nsIFrame::IsVisibleConsideringAncestors(uint32_t aFlags) const
266 {
267 if (!StyleVisibility()->IsVisible()) {
268 return false;
269 }
270
271 const nsIFrame* frame = this;
272 while (frame) {
273 nsView* view = frame->GetView();
274 if (view && view->GetVisibility() == nsViewVisibility_kHide)
275 return false;
276
277 nsIFrame* parent = frame->GetParent();
278 nsDeckFrame* deck = do_QueryFrame(parent);
279 if (deck) {
280 if (deck->GetSelectedBox() != frame)
281 return false;
282 }
283
284 if (parent) {
285 frame = parent;
286 } else {
287 parent = nsLayoutUtils::GetCrossDocParentFrame(frame);
288 if (!parent)
289 break;
290
291 if ((aFlags & nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) == 0 &&
292 parent->PresContext()->IsChrome() && !frame->PresContext()->IsChrome()) {
293 break;
294 }
295
296 if (!parent->StyleVisibility()->IsVisible())
297 return false;
298
299 frame = parent;
300 }
301 }
302
303 return true;
304 }
305
306 void
307 nsIFrame::FindCloserFrameForSelection(
308 nsPoint aPoint,
309 nsIFrame::FrameWithDistance* aCurrentBestFrame)
310 {
311 if (nsLayoutUtils::PointIsCloserToRect(aPoint, mRect,
312 aCurrentBestFrame->mXDistance,
313 aCurrentBestFrame->mYDistance)) {
314 aCurrentBestFrame->mFrame = this;
315 }
316 }
317
318 void
319 nsIFrame::ContentStatesChanged(mozilla::EventStates aStates)
320 {
321 }
322
323 void
324 NS_MergeReflowStatusInto(nsReflowStatus* aPrimary, nsReflowStatus aSecondary)
325 {
326 *aPrimary |= aSecondary &
327 (NS_FRAME_NOT_COMPLETE | NS_FRAME_OVERFLOW_INCOMPLETE |
328 NS_FRAME_TRUNCATED | NS_FRAME_REFLOW_NEXTINFLOW);
329 if (*aPrimary & NS_FRAME_NOT_COMPLETE) {
330 *aPrimary &= ~NS_FRAME_OVERFLOW_INCOMPLETE;
331 }
332 }
333
334 void
335 nsWeakFrame::Init(nsIFrame* aFrame)
336 {
337 Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
338 mFrame = aFrame;
339 if (mFrame) {
340 nsIPresShell* shell = mFrame->PresContext()->GetPresShell();
341 NS_WARN_IF_FALSE(shell, "Null PresShell in nsWeakFrame!");
342 if (shell) {
343 shell->AddWeakFrame(this);
344 } else {
345 mFrame = nullptr;
346 }
347 }
348 }
349
350 nsIFrame*
351 NS_NewEmptyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
352 {
353 return new (aPresShell) nsFrame(aContext);
354 }
355
356 nsFrame::nsFrame(nsStyleContext* aContext)
357 {
358 MOZ_COUNT_CTOR(nsFrame);
359
360 mState = NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY;
361 mStyleContext = aContext;
362 mStyleContext->AddRef();
363 }
364
365 nsFrame::~nsFrame()
366 {
367 MOZ_COUNT_DTOR(nsFrame);
368
369 NS_IF_RELEASE(mContent);
370 mStyleContext->Release();
371 }
372
373 NS_IMPL_FRAMEARENA_HELPERS(nsFrame)
374
375 // Dummy operator delete. Will never be called, but must be defined
376 // to satisfy some C++ ABIs.
377 void
378 nsFrame::operator delete(void *, size_t)
379 {
380 NS_RUNTIMEABORT("nsFrame::operator delete should never be called");
381 }
382
383 NS_QUERYFRAME_HEAD(nsFrame)
384 NS_QUERYFRAME_ENTRY(nsIFrame)
385 NS_QUERYFRAME_TAIL_INHERITANCE_ROOT
386
387 /////////////////////////////////////////////////////////////////////////////
388 // nsIFrame
389
390 static bool
391 IsFontSizeInflationContainer(nsIFrame* aFrame,
392 const nsStyleDisplay* aStyleDisplay)
393 {
394 /*
395 * Font size inflation is built around the idea that we're inflating
396 * the fonts for a pan-and-zoom UI so that when the user scales up a
397 * block or other container to fill the width of the device, the fonts
398 * will be readable. To do this, we need to pick what counts as a
399 * container.
400 *
401 * From a code perspective, the only hard requirement is that frames
402 * that are line participants
403 * (nsIFrame::IsFrameOfType(nsIFrame::eLineParticipant)) are never
404 * containers, since line layout assumes that the inflation is
405 * consistent within a line.
406 *
407 * This is not an imposition, since we obviously want a bunch of text
408 * (possibly with inline elements) flowing within a block to count the
409 * block (or higher) as its container.
410 *
411 * We also want form controls, including the text in the anonymous
412 * content inside of them, to match each other and the text next to
413 * them, so they and their anonymous content should also not be a
414 * container.
415 *
416 * However, because we can't reliably compute sizes across XUL during
417 * reflow, any XUL frame with a XUL parent is always a container.
418 *
419 * There are contexts where it would be nice if some blocks didn't
420 * count as a container, so that, for example, an indented quotation
421 * didn't end up with a smaller font size. However, it's hard to
422 * distinguish these situations where we really do want the indented
423 * thing to count as a container, so we don't try, and blocks are
424 * always containers.
425 */
426
427 // The root frame should always be an inflation container.
428 if (!aFrame->GetParent()) {
429 return true;
430 }
431
432 nsIContent *content = aFrame->GetContent();
433 bool isInline = (aFrame->GetDisplay() == NS_STYLE_DISPLAY_INLINE ||
434 (aFrame->IsFloating() &&
435 aFrame->GetType() == nsGkAtoms::letterFrame) ||
436 // Given multiple frames for the same node, only the
437 // outer one should be considered a container.
438 // (Important, e.g., for nsSelectsAreaFrame.)
439 (aFrame->GetParent()->GetContent() == content) ||
440 (content && (content->IsHTML(nsGkAtoms::option) ||
441 content->IsHTML(nsGkAtoms::optgroup) ||
442 content->IsHTML(nsGkAtoms::select) ||
443 content->IsInNativeAnonymousSubtree()))) &&
444 !(aFrame->IsBoxFrame() && aFrame->GetParent()->IsBoxFrame());
445 NS_ASSERTION(!aFrame->IsFrameOfType(nsIFrame::eLineParticipant) ||
446 isInline ||
447 // br frames and mathml frames report being line
448 // participants even when their position or display is
449 // set
450 aFrame->GetType() == nsGkAtoms::brFrame ||
451 aFrame->IsFrameOfType(nsIFrame::eMathML),
452 "line participants must not be containers");
453 NS_ASSERTION(aFrame->GetType() != nsGkAtoms::bulletFrame || isInline,
454 "bullets should not be containers");
455 return !isInline;
456 }
457
458 void
459 nsFrame::Init(nsIContent* aContent,
460 nsIFrame* aParent,
461 nsIFrame* aPrevInFlow)
462 {
463 NS_PRECONDITION(!mContent, "Double-initing a frame?");
464 NS_ASSERTION(IsFrameOfType(eDEBUGAllFrames) &&
465 !IsFrameOfType(eDEBUGNoFrames),
466 "IsFrameOfType implementation that doesn't call base class");
467
468 mContent = aContent;
469 mParent = aParent;
470
471 if (aContent) {
472 NS_ADDREF(aContent);
473 }
474
475 if (aPrevInFlow) {
476 // Make sure the general flags bits are the same
477 nsFrameState state = aPrevInFlow->GetStateBits();
478
479 // Make bits that are currently off (see constructor) the same:
480 mState |= state & (NS_FRAME_INDEPENDENT_SELECTION |
481 NS_FRAME_PART_OF_IBSPLIT |
482 NS_FRAME_MAY_BE_TRANSFORMED |
483 NS_FRAME_MAY_HAVE_GENERATED_CONTENT |
484 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
485 }
486 if (mParent) {
487 nsFrameState state = mParent->GetStateBits();
488
489 // Make bits that are currently off (see constructor) the same:
490 mState |= state & (NS_FRAME_INDEPENDENT_SELECTION |
491 NS_FRAME_GENERATED_CONTENT |
492 NS_FRAME_IS_SVG_TEXT |
493 NS_FRAME_IN_POPUP |
494 NS_FRAME_IS_NONDISPLAY);
495 }
496 const nsStyleDisplay *disp = StyleDisplay();
497 if (disp->HasTransform(this)) {
498 // The frame gets reconstructed if we toggle the -moz-transform
499 // property, so we can set this bit here and then ignore it.
500 mState |= NS_FRAME_MAY_BE_TRANSFORMED;
501 }
502 if (disp->mPosition == NS_STYLE_POSITION_STICKY &&
503 !aPrevInFlow &&
504 !(mState & NS_FRAME_IS_NONDISPLAY) &&
505 !disp->IsInnerTableStyle()) {
506 // Note that we only add first continuations, but we really only
507 // want to add first continuation-or-ib-split-siblings. But since we
508 // don't yet know if we're a later part of a block-in-inline split,
509 // we'll just add later members of a block-in-inline split here, and
510 // then StickyScrollContainer will remove them later.
511 // We don't currently support relative positioning of inner table
512 // elements (bug 35168), so exclude them from sticky positioning too.
513 StickyScrollContainer* ssc =
514 StickyScrollContainer::GetStickyScrollContainerForFrame(this);
515 if (ssc) {
516 ssc->AddFrame(this);
517 }
518 }
519
520 if (nsLayoutUtils::FontSizeInflationEnabled(PresContext()) || !GetParent()
521 #ifdef DEBUG
522 // We have assertions that check inflation invariants even when
523 // font size inflation is not enabled.
524 || true
525 #endif
526 ) {
527 if (IsFontSizeInflationContainer(this, disp)) {
528 AddStateBits(NS_FRAME_FONT_INFLATION_CONTAINER);
529 if (!GetParent() ||
530 // I'd use NS_FRAME_OUT_OF_FLOW, but it's not set yet.
531 disp->IsFloating(this) || disp->IsAbsolutelyPositioned(this)) {
532 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
533 }
534 }
535 NS_ASSERTION(GetParent() ||
536 (GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER),
537 "root frame should always be a container");
538 }
539
540 DidSetStyleContext(nullptr);
541
542 if (IsBoxWrapped())
543 InitBoxMetrics(false);
544 }
545
546 nsresult nsFrame::SetInitialChildList(ChildListID aListID,
547 nsFrameList& aChildList)
548 {
549 // XXX This shouldn't be getting called at all, but currently is for backwards
550 // compatility reasons...
551 #if 0
552 NS_ERROR("not a container");
553 return NS_ERROR_UNEXPECTED;
554 #else
555 NS_ASSERTION(aChildList.IsEmpty(), "not a container");
556 return NS_OK;
557 #endif
558 }
559
560 nsresult
561 nsFrame::AppendFrames(ChildListID aListID,
562 nsFrameList& aFrameList)
563 {
564 NS_PRECONDITION(false, "not a container");
565 return NS_ERROR_UNEXPECTED;
566 }
567
568 nsresult
569 nsFrame::InsertFrames(ChildListID aListID,
570 nsIFrame* aPrevFrame,
571 nsFrameList& aFrameList)
572 {
573 NS_PRECONDITION(false, "not a container");
574 return NS_ERROR_UNEXPECTED;
575 }
576
577 nsresult
578 nsFrame::RemoveFrame(ChildListID aListID,
579 nsIFrame* aOldFrame)
580 {
581 NS_PRECONDITION(false, "not a container");
582 return NS_ERROR_UNEXPECTED;
583 }
584
585 void
586 nsFrame::DestroyFrom(nsIFrame* aDestructRoot)
587 {
588 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
589 "destroy called on frame while scripts not blocked");
590 NS_ASSERTION(!GetNextSibling() && !GetPrevSibling(),
591 "Frames should be removed before destruction.");
592 NS_ASSERTION(aDestructRoot, "Must specify destruct root");
593 MOZ_ASSERT(!HasAbsolutelyPositionedChildren());
594
595 nsSVGEffects::InvalidateDirectRenderingObservers(this);
596
597 if (StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY) {
598 StickyScrollContainer* ssc =
599 StickyScrollContainer::GetStickyScrollContainerForFrame(this);
600 if (ssc) {
601 ssc->RemoveFrame(this);
602 }
603 }
604
605 // Get the view pointer now before the frame properties disappear
606 // when we call NotifyDestroyingFrame()
607 nsView* view = GetView();
608 nsPresContext* presContext = PresContext();
609
610 nsIPresShell *shell = presContext->GetPresShell();
611 if (mState & NS_FRAME_OUT_OF_FLOW) {
612 nsPlaceholderFrame* placeholder =
613 shell->FrameManager()->GetPlaceholderFrameFor(this);
614 NS_ASSERTION(!placeholder || (aDestructRoot != this),
615 "Don't call Destroy() on OOFs, call Destroy() on the placeholder.");
616 NS_ASSERTION(!placeholder ||
617 nsLayoutUtils::IsProperAncestorFrame(aDestructRoot, placeholder),
618 "Placeholder relationship should have been torn down already; "
619 "this might mean we have a stray placeholder in the tree.");
620 if (placeholder) {
621 shell->FrameManager()->UnregisterPlaceholderFrame(placeholder);
622 placeholder->SetOutOfFlowFrame(nullptr);
623 }
624 }
625
626 // If we have any IB split siblings, clear their references to us.
627 // (Note: This has to happen before we call shell->NotifyDestroyingFrame,
628 // because that clears our Properties() table.)
629 if (mState & NS_FRAME_PART_OF_IBSPLIT) {
630 // Delete previous sibling's reference to me.
631 nsIFrame* prevSib = static_cast<nsIFrame*>
632 (Properties().Get(nsIFrame::IBSplitPrevSibling()));
633 if (prevSib) {
634 NS_WARN_IF_FALSE(this ==
635 prevSib->Properties().Get(nsIFrame::IBSplitSibling()),
636 "IB sibling chain is inconsistent");
637 prevSib->Properties().Delete(nsIFrame::IBSplitSibling());
638 }
639
640 // Delete next sibling's reference to me.
641 nsIFrame* nextSib = static_cast<nsIFrame*>
642 (Properties().Get(nsIFrame::IBSplitSibling()));
643 if (nextSib) {
644 NS_WARN_IF_FALSE(this ==
645 nextSib->Properties().Get(nsIFrame::IBSplitPrevSibling()),
646 "IB sibling chain is inconsistent");
647 nextSib->Properties().Delete(nsIFrame::IBSplitPrevSibling());
648 }
649 }
650
651 shell->NotifyDestroyingFrame(this);
652
653 if (mState & NS_FRAME_EXTERNAL_REFERENCE) {
654 shell->ClearFrameRefs(this);
655 }
656
657 if (view) {
658 // Break association between view and frame
659 view->SetFrame(nullptr);
660
661 // Destroy the view
662 view->Destroy();
663 }
664
665 // Make sure that our deleted frame can't be returned from GetPrimaryFrame()
666 if (mContent && mContent->GetPrimaryFrame() == this) {
667 mContent->SetPrimaryFrame(nullptr);
668 }
669
670 // Must retrieve the object ID before calling destructors, so the
671 // vtable is still valid.
672 //
673 // Note to future tweakers: having the method that returns the
674 // object size call the destructor will not avoid an indirect call;
675 // the compiler cannot devirtualize the call to the destructor even
676 // if it's from a method defined in the same class.
677
678 nsQueryFrame::FrameIID id = GetFrameId();
679 this->~nsFrame();
680
681 // Now that we're totally cleaned out, we need to add ourselves to
682 // the presshell's recycler.
683 shell->FreeFrame(id, this);
684 }
685
686 nsresult
687 nsFrame::GetOffsets(int32_t &aStart, int32_t &aEnd) const
688 {
689 aStart = 0;
690 aEnd = 0;
691 return NS_OK;
692 }
693
694 // Subclass hook for style post processing
695 /* virtual */ void
696 nsFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
697 {
698 if (IsSVGText()) {
699 SVGTextFrame* svgTextFrame = static_cast<SVGTextFrame*>(
700 nsLayoutUtils::GetClosestFrameOfType(this, nsGkAtoms::svgTextFrame));
701 nsIFrame* anonBlock = svgTextFrame->GetFirstPrincipalChild();
702 // Just as in SVGTextFrame::DidSetStyleContext, we need to ensure that
703 // any non-display SVGTextFrames get reflowed when a child text frame
704 // gets new style.
705 //
706 // Note that we must check NS_FRAME_FIRST_REFLOW on our SVGTextFrame's
707 // anonymous block frame rather than our self, since NS_FRAME_FIRST_REFLOW
708 // may be set on us if we're a new frame that has been inserted after the
709 // document's first reflow. (In which case this DidSetStyleContext call may
710 // be happening under frame construction under a Reflow() call.)
711 if (anonBlock && !(anonBlock->GetStateBits() & NS_FRAME_FIRST_REFLOW) &&
712 (svgTextFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) &&
713 !(svgTextFrame->GetStateBits() & NS_STATE_SVG_TEXT_IN_REFLOW)) {
714 svgTextFrame->ScheduleReflowSVGNonDisplayText();
715 }
716 }
717
718 ImageLoader* imageLoader = PresContext()->Document()->StyleImageLoader();
719
720 // If the old context had a background image image and new context
721 // does not have the same image, clear the image load notifier
722 // (which keeps the image loading, if it still is) for the frame.
723 // We want to do this conservatively because some frames paint their
724 // backgrounds from some other frame's style data, and we don't want
725 // to clear those notifiers unless we have to. (They'll be reset
726 // when we paint, although we could miss a notification in that
727 // interval.)
728 const nsStyleBackground *oldBG = aOldStyleContext ?
729 aOldStyleContext->StyleBackground() :
730 nullptr;
731 const nsStyleBackground *newBG = StyleBackground();
732 if (oldBG) {
733 NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, oldBG) {
734 // If there is an image in oldBG that's not in newBG, drop it.
735 if (i >= newBG->mImageCount ||
736 !oldBG->mLayers[i].mImage.ImageDataEquals(newBG->mLayers[i].mImage)) {
737 const nsStyleImage& oldImage = oldBG->mLayers[i].mImage;
738 if (oldImage.GetType() != eStyleImageType_Image) {
739 continue;
740 }
741
742 imageLoader->DisassociateRequestFromFrame(oldImage.GetImageData(),
743 this);
744 }
745 }
746 }
747
748 NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, newBG) {
749 // If there is an image in newBG that's not in oldBG, add it.
750 if (!oldBG || i >= oldBG->mImageCount ||
751 !newBG->mLayers[i].mImage.ImageDataEquals(oldBG->mLayers[i].mImage)) {
752 const nsStyleImage& newImage = newBG->mLayers[i].mImage;
753 if (newImage.GetType() != eStyleImageType_Image) {
754 continue;
755 }
756
757 imageLoader->AssociateRequestToFrame(newImage.GetImageData(), this);
758 }
759 }
760
761 if (aOldStyleContext) {
762 // If we detect a change on margin, padding or border, we store the old
763 // values on the frame itself between now and reflow, so if someone
764 // calls GetUsed(Margin|Border|Padding)() before the next reflow, we
765 // can give an accurate answer.
766 // We don't want to set the property if one already exists.
767 FrameProperties props = Properties();
768 nsMargin oldValue(0, 0, 0, 0);
769 nsMargin newValue(0, 0, 0, 0);
770 const nsStyleMargin* oldMargin = aOldStyleContext->PeekStyleMargin();
771 if (oldMargin && oldMargin->GetMargin(oldValue)) {
772 if ((!StyleMargin()->GetMargin(newValue) || oldValue != newValue) &&
773 !props.Get(UsedMarginProperty())) {
774 props.Set(UsedMarginProperty(), new nsMargin(oldValue));
775 }
776 }
777
778 const nsStylePadding* oldPadding = aOldStyleContext->PeekStylePadding();
779 if (oldPadding && oldPadding->GetPadding(oldValue)) {
780 if ((!StylePadding()->GetPadding(newValue) || oldValue != newValue) &&
781 !props.Get(UsedPaddingProperty())) {
782 props.Set(UsedPaddingProperty(), new nsMargin(oldValue));
783 }
784 }
785
786 const nsStyleBorder* oldBorder = aOldStyleContext->PeekStyleBorder();
787 if (oldBorder) {
788 oldValue = oldBorder->GetComputedBorder();
789 newValue = StyleBorder()->GetComputedBorder();
790 if (oldValue != newValue &&
791 !props.Get(UsedBorderProperty())) {
792 props.Set(UsedBorderProperty(), new nsMargin(oldValue));
793 }
794 }
795 }
796
797 imgIRequest *oldBorderImage = aOldStyleContext
798 ? aOldStyleContext->StyleBorder()->GetBorderImageRequest()
799 : nullptr;
800 imgIRequest *newBorderImage = StyleBorder()->GetBorderImageRequest();
801 // FIXME (Bug 759996): The following is no longer true.
802 // For border-images, we can't be as conservative (we need to set the
803 // new loaders if there has been any change) since the CalcDifference
804 // call depended on the result of GetComputedBorder() and that result
805 // depends on whether the image has loaded, start the image load now
806 // so that we'll get notified when it completes loading and can do a
807 // restyle. Otherwise, the image might finish loading from the
808 // network before we start listening to its notifications, and then
809 // we'll never know that it's finished loading. Likewise, we want to
810 // do this for freshly-created frames to prevent a similar race if the
811 // image loads between reflow (which can depend on whether the image
812 // is loaded) and paint. We also don't really care about any callers
813 // who try to paint borders with a different style context, because
814 // they won't have the correct size for the border either.
815 if (oldBorderImage != newBorderImage) {
816 // stop and restart the image loading/notification
817 if (oldBorderImage) {
818 imageLoader->DisassociateRequestFromFrame(oldBorderImage, this);
819 }
820 if (newBorderImage) {
821 imageLoader->AssociateRequestToFrame(newBorderImage, this);
822 }
823 }
824
825 // If the page contains markup that overrides text direction, and
826 // does not contain any characters that would activate the Unicode
827 // bidi algorithm, we need to call |SetBidiEnabled| on the pres
828 // context before reflow starts. See bug 115921.
829 if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
830 PresContext()->SetBidiEnabled();
831 }
832 }
833
834 // MSVC fails with link error "one or more multiply defined symbols found",
835 // gcc fails with "hidden symbol `nsIFrame::kPrincipalList' isn't defined"
836 // etc if they are not defined.
837 #ifndef _MSC_VER
838 // static nsIFrame constants; initialized in the header file.
839 const nsIFrame::ChildListID nsIFrame::kPrincipalList;
840 const nsIFrame::ChildListID nsIFrame::kAbsoluteList;
841 const nsIFrame::ChildListID nsIFrame::kBulletList;
842 const nsIFrame::ChildListID nsIFrame::kCaptionList;
843 const nsIFrame::ChildListID nsIFrame::kColGroupList;
844 const nsIFrame::ChildListID nsIFrame::kExcessOverflowContainersList;
845 const nsIFrame::ChildListID nsIFrame::kFixedList;
846 const nsIFrame::ChildListID nsIFrame::kFloatList;
847 const nsIFrame::ChildListID nsIFrame::kOverflowContainersList;
848 const nsIFrame::ChildListID nsIFrame::kOverflowList;
849 const nsIFrame::ChildListID nsIFrame::kOverflowOutOfFlowList;
850 const nsIFrame::ChildListID nsIFrame::kPopupList;
851 const nsIFrame::ChildListID nsIFrame::kPushedFloatsList;
852 const nsIFrame::ChildListID nsIFrame::kSelectPopupList;
853 const nsIFrame::ChildListID nsIFrame::kNoReflowPrincipalList;
854 #endif
855
856 /* virtual */ nsMargin
857 nsIFrame::GetUsedMargin() const
858 {
859 nsMargin margin(0, 0, 0, 0);
860 if (((mState & NS_FRAME_FIRST_REFLOW) &&
861 !(mState & NS_FRAME_IN_REFLOW)) ||
862 IsSVGText())
863 return margin;
864
865 nsMargin *m = static_cast<nsMargin*>
866 (Properties().Get(UsedMarginProperty()));
867 if (m) {
868 margin = *m;
869 } else {
870 DebugOnly<bool> hasMargin = StyleMargin()->GetMargin(margin);
871 NS_ASSERTION(hasMargin, "We should have a margin here! (out of memory?)");
872 }
873 return margin;
874 }
875
876 /* virtual */ nsMargin
877 nsIFrame::GetUsedBorder() const
878 {
879 nsMargin border(0, 0, 0, 0);
880 if (((mState & NS_FRAME_FIRST_REFLOW) &&
881 !(mState & NS_FRAME_IN_REFLOW)) ||
882 IsSVGText())
883 return border;
884
885 // Theme methods don't use const-ness.
886 nsIFrame *mutable_this = const_cast<nsIFrame*>(this);
887
888 const nsStyleDisplay *disp = StyleDisplay();
889 if (mutable_this->IsThemed(disp)) {
890 nsIntMargin result;
891 nsPresContext *presContext = PresContext();
892 presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(),
893 mutable_this, disp->mAppearance,
894 &result);
895 border.left = presContext->DevPixelsToAppUnits(result.left);
896 border.top = presContext->DevPixelsToAppUnits(result.top);
897 border.right = presContext->DevPixelsToAppUnits(result.right);
898 border.bottom = presContext->DevPixelsToAppUnits(result.bottom);
899 return border;
900 }
901
902 nsMargin *b = static_cast<nsMargin*>
903 (Properties().Get(UsedBorderProperty()));
904 if (b) {
905 border = *b;
906 } else {
907 border = StyleBorder()->GetComputedBorder();
908 }
909 return border;
910 }
911
912 /* virtual */ nsMargin
913 nsIFrame::GetUsedPadding() const
914 {
915 nsMargin padding(0, 0, 0, 0);
916 if (((mState & NS_FRAME_FIRST_REFLOW) &&
917 !(mState & NS_FRAME_IN_REFLOW)) ||
918 IsSVGText())
919 return padding;
920
921 // Theme methods don't use const-ness.
922 nsIFrame *mutable_this = const_cast<nsIFrame*>(this);
923
924 const nsStyleDisplay *disp = StyleDisplay();
925 if (mutable_this->IsThemed(disp)) {
926 nsPresContext *presContext = PresContext();
927 nsIntMargin widget;
928 if (presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(),
929 mutable_this,
930 disp->mAppearance,
931 &widget)) {
932 padding.top = presContext->DevPixelsToAppUnits(widget.top);
933 padding.right = presContext->DevPixelsToAppUnits(widget.right);
934 padding.bottom = presContext->DevPixelsToAppUnits(widget.bottom);
935 padding.left = presContext->DevPixelsToAppUnits(widget.left);
936 return padding;
937 }
938 }
939
940 nsMargin *p = static_cast<nsMargin*>
941 (Properties().Get(UsedPaddingProperty()));
942 if (p) {
943 padding = *p;
944 } else {
945 DebugOnly<bool> hasPadding = StylePadding()->GetPadding(padding);
946 NS_ASSERTION(hasPadding, "We should have padding here! (out of memory?)");
947 }
948 return padding;
949 }
950
951 int
952 nsIFrame::GetSkipSides(const nsHTMLReflowState* aReflowState) const
953 {
954 // Convert the logical skip sides to physical sides using the frame's
955 // writing mode
956 WritingMode writingMode = GetWritingMode();
957 int logicalSkip = GetLogicalSkipSides(aReflowState);
958 int skip = 0;
959
960 if (logicalSkip & LOGICAL_SIDE_B_START) {
961 if (writingMode.IsVertical()) {
962 skip |= 1 << (writingMode.IsVerticalLR() ? NS_SIDE_LEFT : NS_SIDE_RIGHT);
963 } else {
964 skip |= 1 << NS_SIDE_TOP;
965 }
966 }
967
968 if (logicalSkip & LOGICAL_SIDE_B_END) {
969 if (writingMode.IsVertical()) {
970 skip |= 1 << (writingMode.IsVerticalLR() ? NS_SIDE_RIGHT : NS_SIDE_LEFT);
971 } else {
972 skip |= 1 << NS_SIDE_BOTTOM;
973 }
974 }
975
976 if (logicalSkip & LOGICAL_SIDE_I_START) {
977 if (writingMode.IsVertical()) {
978 skip |= 1 << NS_SIDE_TOP;
979 } else {
980 skip |= 1 << (writingMode.IsBidiLTR() ? NS_SIDE_LEFT : NS_SIDE_RIGHT);
981 }
982 }
983
984 if (logicalSkip & LOGICAL_SIDE_I_END) {
985 if (writingMode.IsVertical()) {
986 skip |= 1 << NS_SIDE_BOTTOM;
987 } else {
988 skip |= 1 << (writingMode.IsBidiLTR() ? NS_SIDE_RIGHT : NS_SIDE_LEFT);
989 }
990 }
991
992 return skip;
993 }
994
995
996 void
997 nsIFrame::ApplySkipSides(nsMargin& aMargin,
998 const nsHTMLReflowState* aReflowState) const
999 {
1000 int skipSides = GetSkipSides(aReflowState);
1001 if (skipSides & (1 << NS_SIDE_TOP)) {
1002 aMargin.top = 0;
1003 }
1004 if (skipSides & (1 << NS_SIDE_RIGHT)) {
1005 aMargin.right = 0;
1006 }
1007 if (skipSides & (1 << NS_SIDE_BOTTOM)) {
1008 aMargin.bottom = 0;
1009 }
1010 if (skipSides & (1 << NS_SIDE_LEFT)) {
1011 aMargin.left = 0;
1012 }
1013 }
1014
1015 void
1016 nsIFrame::ApplyLogicalSkipSides(LogicalMargin& aMargin,
1017 const nsHTMLReflowState* aReflowState) const
1018 {
1019 int skipSides = GetLogicalSkipSides(aReflowState);
1020 if (skipSides & (LOGICAL_SIDE_B_START)) {
1021 aMargin.BStart(GetWritingMode()) = 0;
1022 }
1023 if (skipSides & (LOGICAL_SIDE_I_END)) {
1024 aMargin.IEnd(GetWritingMode()) = 0;
1025 }
1026 if (skipSides & (LOGICAL_SIDE_B_END)) {
1027 aMargin.BEnd(GetWritingMode()) = 0;
1028 }
1029 if (skipSides & (LOGICAL_SIDE_I_START)) {
1030 aMargin.IStart(GetWritingMode()) = 0;
1031 }
1032 }
1033
1034 nsRect
1035 nsIFrame::GetPaddingRectRelativeToSelf() const
1036 {
1037 nsMargin border(GetUsedBorder());
1038 ApplySkipSides(border);
1039 nsRect r(0, 0, mRect.width, mRect.height);
1040 r.Deflate(border);
1041 return r;
1042 }
1043
1044 nsRect
1045 nsIFrame::GetPaddingRect() const
1046 {
1047 return GetPaddingRectRelativeToSelf() + GetPosition();
1048 }
1049
1050 WritingMode
1051 nsIFrame::GetWritingMode(nsIFrame* aSubFrame) const
1052 {
1053 WritingMode writingMode = GetWritingMode();
1054
1055 if (!writingMode.IsVertical() &&
1056 (StyleTextReset()->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT)) {
1057 nsBidiLevel frameLevel = nsBidiPresUtils::GetFrameBaseLevel(aSubFrame);
1058 writingMode.SetDirectionFromBidiLevel(frameLevel);
1059 }
1060
1061 return writingMode;
1062 }
1063
1064 nsRect
1065 nsIFrame::GetMarginRectRelativeToSelf() const
1066 {
1067 nsMargin m = GetUsedMargin();
1068 ApplySkipSides(m);
1069 nsRect r(0, 0, mRect.width, mRect.height);
1070 r.Inflate(m);
1071 return r;
1072 }
1073
1074 bool
1075 nsIFrame::IsTransformed() const
1076 {
1077 return ((mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
1078 (StyleDisplay()->HasTransform(this) ||
1079 IsSVGTransformed() ||
1080 (mContent &&
1081 nsLayoutUtils::HasAnimationsForCompositor(mContent,
1082 eCSSProperty_transform) &&
1083 IsFrameOfType(eSupportsCSSTransforms) &&
1084 mContent->GetPrimaryFrame() == this)));
1085 }
1086
1087 bool
1088 nsIFrame::HasOpacityInternal(float aThreshold) const
1089 {
1090 MOZ_ASSERT(0.0 <= aThreshold && aThreshold <= 1.0, "Invalid argument");
1091 const nsStyleDisplay* displayStyle = StyleDisplay();
1092 return StyleDisplay()->mOpacity < aThreshold ||
1093 (displayStyle->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) ||
1094 (mContent &&
1095 nsLayoutUtils::HasAnimationsForCompositor(mContent,
1096 eCSSProperty_opacity) &&
1097 mContent->GetPrimaryFrame() == this);
1098 }
1099
1100 bool
1101 nsIFrame::IsSVGTransformed(gfx::Matrix *aOwnTransforms,
1102 gfx::Matrix *aFromParentTransforms) const
1103 {
1104 return false;
1105 }
1106
1107 bool
1108 nsIFrame::Preserves3DChildren() const
1109 {
1110 const nsStyleDisplay* disp = StyleDisplay();
1111 if (disp->mTransformStyle != NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D ||
1112 !IsFrameOfType(nsIFrame::eSupportsCSSTransforms)) {
1113 return false;
1114 }
1115
1116 // If we're all scroll frame, then all descendants will be clipped, so we can't preserve 3d.
1117 if (GetType() == nsGkAtoms::scrollFrame) {
1118 return false;
1119 }
1120
1121 nsRect temp;
1122 return !nsFrame::ShouldApplyOverflowClipping(this, disp) &&
1123 !GetClipPropClipRect(disp, &temp, GetSize()) &&
1124 !nsSVGIntegrationUtils::UsingEffectsForFrame(this);
1125 }
1126
1127 bool
1128 nsIFrame::Preserves3D() const
1129 {
1130 if (!GetParent() || !GetParent()->Preserves3DChildren()) {
1131 return false;
1132 }
1133 return StyleDisplay()->HasTransform(this) || StyleDisplay()->BackfaceIsHidden();
1134 }
1135
1136 bool
1137 nsIFrame::HasPerspective() const
1138 {
1139 if (!IsTransformed()) {
1140 return false;
1141 }
1142 nsStyleContext* parentStyleContext = StyleContext()->GetParent();
1143 if (!parentStyleContext) {
1144 return false;
1145 }
1146 const nsStyleDisplay* parentDisp = parentStyleContext->StyleDisplay();
1147 return parentDisp->mChildPerspective.GetUnit() == eStyleUnit_Coord;
1148 }
1149
1150 bool
1151 nsIFrame::ChildrenHavePerspective() const
1152 {
1153 return StyleDisplay()->HasPerspectiveStyle();
1154 }
1155
1156 nsRect
1157 nsIFrame::GetContentRectRelativeToSelf() const
1158 {
1159 nsMargin bp(GetUsedBorderAndPadding());
1160 ApplySkipSides(bp);
1161 nsRect r(0, 0, mRect.width, mRect.height);
1162 r.Deflate(bp);
1163 return r;
1164 }
1165
1166 nsRect
1167 nsIFrame::GetContentRect() const
1168 {
1169 return GetContentRectRelativeToSelf() + GetPosition();
1170 }
1171
1172 bool
1173 nsIFrame::ComputeBorderRadii(const nsStyleCorners& aBorderRadius,
1174 const nsSize& aFrameSize,
1175 const nsSize& aBorderArea,
1176 int aSkipSides,
1177 nscoord aRadii[8])
1178 {
1179 // Percentages are relative to whichever side they're on.
1180 NS_FOR_CSS_HALF_CORNERS(i) {
1181 const nsStyleCoord c = aBorderRadius.Get(i);
1182 nscoord axis =
1183 NS_HALF_CORNER_IS_X(i) ? aFrameSize.width : aFrameSize.height;
1184
1185 if (c.IsCoordPercentCalcUnit()) {
1186 aRadii[i] = nsRuleNode::ComputeCoordPercentCalc(c, axis);
1187 if (aRadii[i] < 0) {
1188 // clamp calc()
1189 aRadii[i] = 0;
1190 }
1191 } else {
1192 NS_NOTREACHED("ComputeBorderRadii: bad unit");
1193 aRadii[i] = 0;
1194 }
1195 }
1196
1197 if (aSkipSides & (1 << NS_SIDE_TOP)) {
1198 aRadii[NS_CORNER_TOP_LEFT_X] = 0;
1199 aRadii[NS_CORNER_TOP_LEFT_Y] = 0;
1200 aRadii[NS_CORNER_TOP_RIGHT_X] = 0;
1201 aRadii[NS_CORNER_TOP_RIGHT_Y] = 0;
1202 }
1203
1204 if (aSkipSides & (1 << NS_SIDE_RIGHT)) {
1205 aRadii[NS_CORNER_TOP_RIGHT_X] = 0;
1206 aRadii[NS_CORNER_TOP_RIGHT_Y] = 0;
1207 aRadii[NS_CORNER_BOTTOM_RIGHT_X] = 0;
1208 aRadii[NS_CORNER_BOTTOM_RIGHT_Y] = 0;
1209 }
1210
1211 if (aSkipSides & (1 << NS_SIDE_BOTTOM)) {
1212 aRadii[NS_CORNER_BOTTOM_RIGHT_X] = 0;
1213 aRadii[NS_CORNER_BOTTOM_RIGHT_Y] = 0;
1214 aRadii[NS_CORNER_BOTTOM_LEFT_X] = 0;
1215 aRadii[NS_CORNER_BOTTOM_LEFT_Y] = 0;
1216 }
1217
1218 if (aSkipSides & (1 << NS_SIDE_LEFT)) {
1219 aRadii[NS_CORNER_BOTTOM_LEFT_X] = 0;
1220 aRadii[NS_CORNER_BOTTOM_LEFT_Y] = 0;
1221 aRadii[NS_CORNER_TOP_LEFT_X] = 0;
1222 aRadii[NS_CORNER_TOP_LEFT_Y] = 0;
1223 }
1224
1225 // css3-background specifies this algorithm for reducing
1226 // corner radii when they are too big.
1227 bool haveRadius = false;
1228 double ratio = 1.0f;
1229 NS_FOR_CSS_SIDES(side) {
1230 uint32_t hc1 = NS_SIDE_TO_HALF_CORNER(side, false, true);
1231 uint32_t hc2 = NS_SIDE_TO_HALF_CORNER(side, true, true);
1232 nscoord length =
1233 NS_SIDE_IS_VERTICAL(side) ? aBorderArea.height : aBorderArea.width;
1234 nscoord sum = aRadii[hc1] + aRadii[hc2];
1235 if (sum)
1236 haveRadius = true;
1237
1238 // avoid floating point division in the normal case
1239 if (length < sum)
1240 ratio = std::min(ratio, double(length)/sum);
1241 }
1242 if (ratio < 1.0) {
1243 NS_FOR_CSS_HALF_CORNERS(corner) {
1244 aRadii[corner] *= ratio;
1245 }
1246 }
1247
1248 return haveRadius;
1249 }
1250
1251 /* static */ void
1252 nsIFrame::InsetBorderRadii(nscoord aRadii[8], const nsMargin &aOffsets)
1253 {
1254 NS_FOR_CSS_SIDES(side) {
1255 nscoord offset = aOffsets.Side(side);
1256 uint32_t hc1 = NS_SIDE_TO_HALF_CORNER(side, false, false);
1257 uint32_t hc2 = NS_SIDE_TO_HALF_CORNER(side, true, false);
1258 aRadii[hc1] = std::max(0, aRadii[hc1] - offset);
1259 aRadii[hc2] = std::max(0, aRadii[hc2] - offset);
1260 }
1261 }
1262
1263 /* static */ void
1264 nsIFrame::OutsetBorderRadii(nscoord aRadii[8], const nsMargin &aOffsets)
1265 {
1266 NS_FOR_CSS_SIDES(side) {
1267 nscoord offset = aOffsets.Side(side);
1268 uint32_t hc1 = NS_SIDE_TO_HALF_CORNER(side, false, false);
1269 uint32_t hc2 = NS_SIDE_TO_HALF_CORNER(side, true, false);
1270 if (aRadii[hc1] > 0)
1271 aRadii[hc1] += offset;
1272 if (aRadii[hc2] > 0)
1273 aRadii[hc2] += offset;
1274 }
1275 }
1276
1277 /* virtual */ bool
1278 nsIFrame::GetBorderRadii(nscoord aRadii[8]) const
1279 {
1280 if (IsThemed()) {
1281 // When we're themed, the native theme code draws the border and
1282 // background, and therefore it doesn't make sense to tell other
1283 // code that's interested in border-radius that we have any radii.
1284 //
1285 // In an ideal world, we might have a way for the them to tell us an
1286 // border radius, but since we don't, we're better off assuming
1287 // zero.
1288 NS_FOR_CSS_HALF_CORNERS(corner) {
1289 aRadii[corner] = 0;
1290 }
1291 return false;
1292 }
1293 nsSize size = GetSize();
1294 return ComputeBorderRadii(StyleBorder()->mBorderRadius, size, size,
1295 GetSkipSides(), aRadii);
1296 }
1297
1298 bool
1299 nsIFrame::GetPaddingBoxBorderRadii(nscoord aRadii[8]) const
1300 {
1301 if (!GetBorderRadii(aRadii))
1302 return false;
1303 InsetBorderRadii(aRadii, GetUsedBorder());
1304 NS_FOR_CSS_HALF_CORNERS(corner) {
1305 if (aRadii[corner])
1306 return true;
1307 }
1308 return false;
1309 }
1310
1311 bool
1312 nsIFrame::GetContentBoxBorderRadii(nscoord aRadii[8]) const
1313 {
1314 if (!GetBorderRadii(aRadii))
1315 return false;
1316 InsetBorderRadii(aRadii, GetUsedBorderAndPadding());
1317 NS_FOR_CSS_HALF_CORNERS(corner) {
1318 if (aRadii[corner])
1319 return true;
1320 }
1321 return false;
1322 }
1323
1324 nsStyleContext*
1325 nsFrame::GetAdditionalStyleContext(int32_t aIndex) const
1326 {
1327 NS_PRECONDITION(aIndex >= 0, "invalid index number");
1328 return nullptr;
1329 }
1330
1331 void
1332 nsFrame::SetAdditionalStyleContext(int32_t aIndex,
1333 nsStyleContext* aStyleContext)
1334 {
1335 NS_PRECONDITION(aIndex >= 0, "invalid index number");
1336 }
1337
1338 nscoord
1339 nsFrame::GetBaseline() const
1340 {
1341 NS_ASSERTION(!NS_SUBTREE_DIRTY(this),
1342 "frame must not be dirty");
1343 // Default to the bottom margin edge, per CSS2.1's definition of the
1344 // 'baseline' value of 'vertical-align'.
1345 return mRect.height + GetUsedMargin().bottom;
1346 }
1347
1348 const nsFrameList&
1349 nsFrame::GetChildList(ChildListID aListID) const
1350 {
1351 if (IsAbsoluteContainer() &&
1352 aListID == GetAbsoluteListID()) {
1353 return GetAbsoluteContainingBlock()->GetChildList();
1354 } else {
1355 return nsFrameList::EmptyList();
1356 }
1357 }
1358
1359 void
1360 nsFrame::GetChildLists(nsTArray<ChildList>* aLists) const
1361 {
1362 if (IsAbsoluteContainer()) {
1363 nsFrameList absoluteList = GetAbsoluteContainingBlock()->GetChildList();
1364 absoluteList.AppendIfNonempty(aLists, GetAbsoluteListID());
1365 }
1366 }
1367
1368 void
1369 nsIFrame::GetCrossDocChildLists(nsTArray<ChildList>* aLists)
1370 {
1371 nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(this);
1372 if (subdocumentFrame) {
1373 // Descend into the subdocument
1374 nsIFrame* root = subdocumentFrame->GetSubdocumentRootFrame();
1375 if (root) {
1376 aLists->AppendElement(nsIFrame::ChildList(
1377 nsFrameList(root, nsLayoutUtils::GetLastSibling(root)),
1378 nsIFrame::kPrincipalList));
1379 }
1380 }
1381
1382 GetChildLists(aLists);
1383 }
1384
1385 static nsIFrame*
1386 GetActiveSelectionFrame(nsPresContext* aPresContext, nsIFrame* aFrame)
1387 {
1388 nsIContent* capturingContent = nsIPresShell::GetCapturingContent();
1389 if (capturingContent) {
1390 nsIFrame* activeFrame = aPresContext->GetPrimaryFrameFor(capturingContent);
1391 return activeFrame ? activeFrame : aFrame;
1392 }
1393
1394 return aFrame;
1395 }
1396
1397 int16_t
1398 nsFrame::DisplaySelection(nsPresContext* aPresContext, bool isOkToTurnOn)
1399 {
1400 int16_t selType = nsISelectionController::SELECTION_OFF;
1401
1402 nsCOMPtr<nsISelectionController> selCon;
1403 nsresult result = GetSelectionController(aPresContext, getter_AddRefs(selCon));
1404 if (NS_SUCCEEDED(result) && selCon) {
1405 result = selCon->GetDisplaySelection(&selType);
1406 if (NS_SUCCEEDED(result) && (selType != nsISelectionController::SELECTION_OFF)) {
1407 // Check whether style allows selection.
1408 bool selectable;
1409 IsSelectable(&selectable, nullptr);
1410 if (!selectable) {
1411 selType = nsISelectionController::SELECTION_OFF;
1412 isOkToTurnOn = false;
1413 }
1414 }
1415 if (isOkToTurnOn && (selType == nsISelectionController::SELECTION_OFF)) {
1416 selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
1417 selType = nsISelectionController::SELECTION_ON;
1418 }
1419 }
1420 return selType;
1421 }
1422
1423 class nsDisplaySelectionOverlay : public nsDisplayItem {
1424 public:
1425 nsDisplaySelectionOverlay(nsDisplayListBuilder* aBuilder,
1426 nsFrame* aFrame, int16_t aSelectionValue)
1427 : nsDisplayItem(aBuilder, aFrame), mSelectionValue(aSelectionValue) {
1428 MOZ_COUNT_CTOR(nsDisplaySelectionOverlay);
1429 }
1430 #ifdef NS_BUILD_REFCNT_LOGGING
1431 virtual ~nsDisplaySelectionOverlay() {
1432 MOZ_COUNT_DTOR(nsDisplaySelectionOverlay);
1433 }
1434 #endif
1435
1436 virtual void Paint(nsDisplayListBuilder* aBuilder,
1437 nsRenderingContext* aCtx) MOZ_OVERRIDE;
1438 NS_DISPLAY_DECL_NAME("SelectionOverlay", TYPE_SELECTION_OVERLAY)
1439 private:
1440 int16_t mSelectionValue;
1441 };
1442
1443 void nsDisplaySelectionOverlay::Paint(nsDisplayListBuilder* aBuilder,
1444 nsRenderingContext* aCtx)
1445 {
1446 LookAndFeel::ColorID colorID;
1447 if (mSelectionValue == nsISelectionController::SELECTION_ON) {
1448 colorID = LookAndFeel::eColorID_TextSelectBackground;
1449 } else if (mSelectionValue == nsISelectionController::SELECTION_ATTENTION) {
1450 colorID = LookAndFeel::eColorID_TextSelectBackgroundAttention;
1451 } else {
1452 colorID = LookAndFeel::eColorID_TextSelectBackgroundDisabled;
1453 }
1454
1455 nscolor color = LookAndFeel::GetColor(colorID, NS_RGB(255, 255, 255));
1456
1457 gfxRGBA c(color);
1458 c.a = .5;
1459
1460 gfxContext *ctx = aCtx->ThebesContext();
1461 ctx->SetColor(c);
1462
1463 nsIntRect pxRect =
1464 mVisibleRect.ToOutsidePixels(mFrame->PresContext()->AppUnitsPerDevPixel());
1465 ctx->NewPath();
1466 ctx->Rectangle(gfxRect(pxRect.x, pxRect.y, pxRect.width, pxRect.height), true);
1467 ctx->Fill();
1468 }
1469
1470 /********************************************************
1471 * Refreshes each content's frame
1472 *********************************************************/
1473
1474 void
1475 nsFrame::DisplaySelectionOverlay(nsDisplayListBuilder* aBuilder,
1476 nsDisplayList* aList,
1477 uint16_t aContentType)
1478 {
1479 if (!IsSelected() || !IsVisibleForPainting(aBuilder))
1480 return;
1481
1482 nsPresContext* presContext = PresContext();
1483 nsIPresShell *shell = presContext->PresShell();
1484 if (!shell)
1485 return;
1486
1487 int16_t displaySelection = shell->GetSelectionFlags();
1488 if (!(displaySelection & aContentType))
1489 return;
1490
1491 const nsFrameSelection* frameSelection = GetConstFrameSelection();
1492 int16_t selectionValue = frameSelection->GetDisplaySelection();
1493
1494 if (selectionValue <= nsISelectionController::SELECTION_HIDDEN)
1495 return; // selection is hidden or off
1496
1497 nsIContent *newContent = mContent->GetParent();
1498
1499 //check to see if we are anonymous content
1500 int32_t offset = 0;
1501 if (newContent) {
1502 // XXXbz there has GOT to be a better way of determining this!
1503 offset = newContent->IndexOf(mContent);
1504 }
1505
1506 SelectionDetails *details;
1507 //look up to see what selection(s) are on this frame
1508 details = frameSelection->LookUpSelection(newContent, offset, 1, false);
1509 if (!details)
1510 return;
1511
1512 bool normal = false;
1513 while (details) {
1514 if (details->mType == nsISelectionController::SELECTION_NORMAL) {
1515 normal = true;
1516 }
1517 SelectionDetails *next = details->mNext;
1518 delete details;
1519 details = next;
1520 }
1521
1522 if (!normal && aContentType == nsISelectionDisplay::DISPLAY_IMAGES) {
1523 // Don't overlay an image if it's not in the primary selection.
1524 return;
1525 }
1526
1527 aList->AppendNewToTop(new (aBuilder)
1528 nsDisplaySelectionOverlay(aBuilder, this, selectionValue));
1529 }
1530
1531 void
1532 nsFrame::DisplayOutlineUnconditional(nsDisplayListBuilder* aBuilder,
1533 const nsDisplayListSet& aLists)
1534 {
1535 if (StyleOutline()->GetOutlineStyle() == NS_STYLE_BORDER_STYLE_NONE)
1536 return;
1537
1538 aLists.Outlines()->AppendNewToTop(
1539 new (aBuilder) nsDisplayOutline(aBuilder, this));
1540 }
1541
1542 void
1543 nsFrame::DisplayOutline(nsDisplayListBuilder* aBuilder,
1544 const nsDisplayListSet& aLists)
1545 {
1546 if (!IsVisibleForPainting(aBuilder))
1547 return;
1548
1549 DisplayOutlineUnconditional(aBuilder, aLists);
1550 }
1551
1552 void
1553 nsIFrame::DisplayCaret(nsDisplayListBuilder* aBuilder,
1554 const nsRect& aDirtyRect, nsDisplayList* aList)
1555 {
1556 if (!IsVisibleForPainting(aBuilder))
1557 return;
1558
1559 aList->AppendNewToTop(
1560 new (aBuilder) nsDisplayCaret(aBuilder, this, aBuilder->GetCaret()));
1561 }
1562
1563 nscolor
1564 nsIFrame::GetCaretColorAt(int32_t aOffset)
1565 {
1566 nscolor color = NS_RGB(0, 0, 0);
1567 if (nsLayoutUtils::GetNativeTextColor(this, color))
1568 return color;
1569
1570 // Use CSS text color.
1571 return StyleColor()->mColor;
1572 }
1573
1574 bool
1575 nsFrame::DisplayBackgroundUnconditional(nsDisplayListBuilder* aBuilder,
1576 const nsDisplayListSet& aLists,
1577 bool aForceBackground)
1578 {
1579 // Here we don't try to detect background propagation. Frames that might
1580 // receive a propagated background should just set aForceBackground to
1581 // true.
1582 if (aBuilder->IsForEventDelivery() || aForceBackground ||
1583 !StyleBackground()->IsTransparent() || StyleDisplay()->mAppearance) {
1584 return nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
1585 aBuilder, this, aLists.BorderBackground());
1586 }
1587 return false;
1588 }
1589
1590 void
1591 nsFrame::DisplayBorderBackgroundOutline(nsDisplayListBuilder* aBuilder,
1592 const nsDisplayListSet& aLists,
1593 bool aForceBackground)
1594 {
1595 // The visibility check belongs here since child elements have the
1596 // opportunity to override the visibility property and display even if
1597 // their parent is hidden.
1598 if (!IsVisibleForPainting(aBuilder))
1599 return;
1600
1601 nsCSSShadowArray* shadows = StyleBorder()->mBoxShadow;
1602 if (shadows && shadows->HasShadowWithInset(false)) {
1603 aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
1604 nsDisplayBoxShadowOuter(aBuilder, this));
1605 }
1606
1607 bool bgIsThemed = DisplayBackgroundUnconditional(aBuilder, aLists,
1608 aForceBackground);
1609
1610 if (shadows && shadows->HasShadowWithInset(true)) {
1611 aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
1612 nsDisplayBoxShadowInner(aBuilder, this));
1613 }
1614
1615 // If there's a themed background, we should not create a border item.
1616 // It won't be rendered.
1617 if (!bgIsThemed && StyleBorder()->HasBorder()) {
1618 aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
1619 nsDisplayBorder(aBuilder, this));
1620 }
1621
1622 DisplayOutlineUnconditional(aBuilder, aLists);
1623 }
1624
1625 inline static bool IsSVGContentWithCSSClip(const nsIFrame *aFrame)
1626 {
1627 // The CSS spec says that the 'clip' property only applies to absolutely
1628 // positioned elements, whereas the SVG spec says that it applies to SVG
1629 // elements regardless of the value of the 'position' property. Here we obey
1630 // the CSS spec for outer-<svg> (since that's what we generally do), but
1631 // obey the SVG spec for other SVG elements to which 'clip' applies.
1632 nsIAtom *tag = aFrame->GetContent()->Tag();
1633 return (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) &&
1634 (tag == nsGkAtoms::svg || tag == nsGkAtoms::foreignObject);
1635 }
1636
1637 bool
1638 nsIFrame::GetClipPropClipRect(const nsStyleDisplay* aDisp, nsRect* aRect,
1639 const nsSize& aSize) const
1640 {
1641 NS_PRECONDITION(aRect, "Must have aRect out parameter");
1642
1643 if (!(aDisp->mClipFlags & NS_STYLE_CLIP_RECT) ||
1644 !(aDisp->IsAbsolutelyPositioned(this) || IsSVGContentWithCSSClip(this))) {
1645 return false;
1646 }
1647
1648 *aRect = aDisp->mClip;
1649 if (NS_STYLE_CLIP_RIGHT_AUTO & aDisp->mClipFlags) {
1650 aRect->width = aSize.width - aRect->x;
1651 }
1652 if (NS_STYLE_CLIP_BOTTOM_AUTO & aDisp->mClipFlags) {
1653 aRect->height = aSize.height - aRect->y;
1654 }
1655 return true;
1656 }
1657
1658 /**
1659 * If the CSS 'clip' property applies to this frame, set it up
1660 * in aBuilder->ClipState() to clip all content descendants. Returns true
1661 * if the property applies, and if so also returns the clip rect (relative
1662 * to aFrame) in *aRect.
1663 */
1664 static bool
1665 ApplyClipPropClipping(nsDisplayListBuilder* aBuilder,
1666 const nsIFrame* aFrame,
1667 const nsStyleDisplay* aDisp,
1668 nsRect* aRect,
1669 DisplayListClipState::AutoSaveRestore& aClipState)
1670 {
1671 if (!aFrame->GetClipPropClipRect(aDisp, aRect, aFrame->GetSize()))
1672 return false;
1673
1674 nsRect clipRect = *aRect + aBuilder->ToReferenceFrame(aFrame);
1675 aClipState.ClipContentDescendants(clipRect);
1676 return true;
1677 }
1678
1679 /**
1680 * If the CSS 'overflow' property applies to this frame, and is not
1681 * handled by constructing a dedicated nsHTML/XULScrollFrame, set up clipping
1682 * for that overflow in aBuilder->ClipState() to clip all containing-block
1683 * descendants.
1684 */
1685 static void
1686 ApplyOverflowClipping(nsDisplayListBuilder* aBuilder,
1687 const nsIFrame* aFrame,
1688 const nsStyleDisplay* aDisp,
1689 DisplayListClipState::AutoClipMultiple& aClipState)
1690 {
1691 // Only -moz-hidden-unscrollable is handled here (and 'hidden' for table
1692 // frames, and any non-visible value for blocks in a paginated context).
1693 // We allow -moz-hidden-unscrollable to apply to any kind of frame. This
1694 // is required by comboboxes which make their display text (an inline frame)
1695 // have clipping.
1696 if (!nsFrame::ShouldApplyOverflowClipping(aFrame, aDisp)) {
1697 return;
1698 }
1699 nsRect clipRect;
1700 bool haveRadii = false;
1701 nscoord radii[8];
1702 if (aFrame->StyleDisplay()->mOverflowClipBox ==
1703 NS_STYLE_OVERFLOW_CLIP_BOX_PADDING_BOX) {
1704 clipRect = aFrame->GetPaddingRectRelativeToSelf() +
1705 aBuilder->ToReferenceFrame(aFrame);
1706 haveRadii = aFrame->GetPaddingBoxBorderRadii(radii);
1707 } else {
1708 clipRect = aFrame->GetContentRectRelativeToSelf() +
1709 aBuilder->ToReferenceFrame(aFrame);
1710 // XXX border-radius
1711 }
1712 aClipState.ClipContainingBlockDescendantsExtra(clipRect, haveRadii ? radii : nullptr);
1713 }
1714
1715 #ifdef DEBUG
1716 static void PaintDebugBorder(nsIFrame* aFrame, nsRenderingContext* aCtx,
1717 const nsRect& aDirtyRect, nsPoint aPt) {
1718 nsRect r(aPt, aFrame->GetSize());
1719 if (aFrame->HasView()) {
1720 aCtx->SetColor(NS_RGB(0,0,255));
1721 } else {
1722 aCtx->SetColor(NS_RGB(255,0,0));
1723 }
1724 aCtx->DrawRect(r);
1725 }
1726
1727 static void PaintEventTargetBorder(nsIFrame* aFrame, nsRenderingContext* aCtx,
1728 const nsRect& aDirtyRect, nsPoint aPt) {
1729 nsRect r(aPt, aFrame->GetSize());
1730 aCtx->SetColor(NS_RGB(128,0,128));
1731 aCtx->DrawRect(r);
1732 }
1733
1734 static void
1735 DisplayDebugBorders(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
1736 const nsDisplayListSet& aLists) {
1737 // Draw a border around the child
1738 // REVIEW: From nsContainerFrame::PaintChild
1739 if (nsFrame::GetShowFrameBorders() && !aFrame->GetRect().IsEmpty()) {
1740 aLists.Outlines()->AppendNewToTop(new (aBuilder)
1741 nsDisplayGeneric(aBuilder, aFrame, PaintDebugBorder, "DebugBorder",
1742 nsDisplayItem::TYPE_DEBUG_BORDER));
1743 }
1744 // Draw a border around the current event target
1745 if (nsFrame::GetShowEventTargetFrameBorder() &&
1746 aFrame->PresContext()->PresShell()->GetDrawEventTargetFrame() == aFrame) {
1747 aLists.Outlines()->AppendNewToTop(new (aBuilder)
1748 nsDisplayGeneric(aBuilder, aFrame, PaintEventTargetBorder, "EventTargetBorder",
1749 nsDisplayItem::TYPE_EVENT_TARGET_BORDER));
1750 }
1751 }
1752 #endif
1753
1754 static nsresult
1755 WrapPreserve3DListInternal(nsIFrame* aFrame, nsDisplayListBuilder *aBuilder, nsDisplayList *aList, nsDisplayList *aOutput, uint32_t& aIndex, nsDisplayList* aTemp)
1756 {
1757 if (aIndex > nsDisplayTransform::INDEX_MAX) {
1758 return NS_OK;
1759 }
1760
1761 nsresult rv = NS_OK;
1762 while (nsDisplayItem *item = aList->RemoveBottom()) {
1763 nsIFrame *childFrame = item->Frame();
1764
1765 // We accumulate sequential items that aren't transforms into the 'temp' list
1766 // and then flush this list into aOutput by wrapping the whole lot with a single
1767 // nsDisplayTransform.
1768
1769 if (childFrame->GetParent() &&
1770 (childFrame->GetParent()->Preserves3DChildren() || childFrame == aFrame)) {
1771 switch (item->GetType()) {
1772 case nsDisplayItem::TYPE_TRANSFORM: {
1773 if (!aTemp->IsEmpty()) {
1774 aOutput->AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, aFrame, aTemp, aIndex++));
1775 }
1776 // Override item's clipping with our current clip state (if any). Since we're
1777 // bubbling up a preserve-3d transformed child to a preserve-3d parent,
1778 // we can be sure the child doesn't have clip state of its own.
1779 NS_ASSERTION(!item->GetClip().HasClip(), "Unexpected clip on item");
1780 const DisplayItemClip* clip = aBuilder->ClipState().GetCurrentCombinedClip(aBuilder);
1781 if (clip) {
1782 item->SetClip(aBuilder, *clip);
1783 }
1784 aOutput->AppendToTop(item);
1785 break;
1786 }
1787 case nsDisplayItem::TYPE_WRAP_LIST: {
1788 nsDisplayWrapList *list = static_cast<nsDisplayWrapList*>(item);
1789 rv = WrapPreserve3DListInternal(aFrame, aBuilder,
1790 list->GetChildren(), aOutput, aIndex, aTemp);
1791 list->~nsDisplayWrapList();
1792 break;
1793 }
1794 case nsDisplayItem::TYPE_OPACITY: {
1795 if (!aTemp->IsEmpty()) {
1796 aOutput->AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, aFrame, aTemp, aIndex++));
1797 }
1798 nsDisplayOpacity *opacity = static_cast<nsDisplayOpacity*>(item);
1799 nsDisplayList output;
1800 // Call GetChildren, not GetSameCoordinateSystemChildren, because
1801 // the preserve-3d children of 'opacity' are temporarily not in the
1802 // same coordinate system as the opacity --- until this wrapping is done.
1803 rv = WrapPreserve3DListInternal(aFrame, aBuilder,
1804 opacity->GetChildren(), &output, aIndex, aTemp);
1805 if (!aTemp->IsEmpty()) {
1806 output.AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, aFrame, aTemp, aIndex++));
1807 }
1808 opacity->GetChildren()->AppendToTop(&output);
1809 opacity->UpdateBounds(aBuilder);
1810 aOutput->AppendToTop(item);
1811 break;
1812 }
1813 default: {
1814 if (childFrame->StyleDisplay()->BackfaceIsHidden()) {
1815 if (!aTemp->IsEmpty()) {
1816 aOutput->AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, aFrame, aTemp, aIndex++));
1817 }
1818
1819 aOutput->AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, childFrame, item, aIndex++));
1820 } else {
1821 aTemp->AppendToTop(item);
1822 }
1823 break;
1824 }
1825 }
1826 } else {
1827 aTemp->AppendToTop(item);
1828 }
1829
1830 if (NS_FAILED(rv) || !item || aIndex > nsDisplayTransform::INDEX_MAX)
1831 return rv;
1832 }
1833
1834 return NS_OK;
1835 }
1836
1837 static bool
1838 IsScrollFrameActive(nsIScrollableFrame* aScrollableFrame)
1839 {
1840 return aScrollableFrame && aScrollableFrame->IsScrollingActive();
1841 }
1842
1843 static nsresult
1844 WrapPreserve3DList(nsIFrame* aFrame, nsDisplayListBuilder* aBuilder, nsDisplayList *aList)
1845 {
1846 uint32_t index = 0;
1847 nsDisplayList temp;
1848 nsDisplayList output;
1849 nsresult rv = WrapPreserve3DListInternal(aFrame, aBuilder, aList, &output, index, &temp);
1850
1851 if (!temp.IsEmpty()) {
1852 output.AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, aFrame, &temp, index++));
1853 }
1854
1855 aList->AppendToTop(&output);
1856 return rv;
1857 }
1858
1859 class AutoSaveRestoreBlendMode
1860 {
1861 nsDisplayListBuilder& mBuilder;
1862 bool AutoResetContainsBlendMode;
1863 public:
1864 AutoSaveRestoreBlendMode(nsDisplayListBuilder& aBuilder)
1865 : mBuilder(aBuilder),
1866 AutoResetContainsBlendMode(aBuilder.ContainsBlendMode()) {
1867 }
1868
1869 ~AutoSaveRestoreBlendMode() {
1870 mBuilder.SetContainsBlendMode(AutoResetContainsBlendMode);
1871 }
1872 };
1873
1874 static void
1875 CheckForTouchEventHandler(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
1876 {
1877 nsIContent* content = aFrame->GetContent();
1878 if (!content) {
1879 return;
1880 }
1881 EventListenerManager* elm = nsContentUtils::GetExistingListenerManagerForNode(content);
1882 if (!elm) {
1883 return;
1884 }
1885 if (elm->HasListenersFor(nsGkAtoms::ontouchstart) ||
1886 elm->HasListenersFor(nsGkAtoms::ontouchmove)) {
1887 aBuilder->SetAncestorHasTouchEventHandler(true);
1888 }
1889 }
1890
1891 void
1892 nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
1893 const nsRect& aDirtyRect,
1894 nsDisplayList* aList) {
1895 if (GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE)
1896 return;
1897
1898 // Replaced elements have their visibility handled here, because
1899 // they're visually atomic
1900 if (IsFrameOfType(eReplaced) && !IsVisibleForPainting(aBuilder))
1901 return;
1902
1903 const nsStyleDisplay* disp = StyleDisplay();
1904 // We can stop right away if this is a zero-opacity stacking context and
1905 // we're painting, and we're not animating opacity. Don't do this
1906 // if we're going to compute plugin geometry, since opacity-0 plugins
1907 // need to have display items built for them.
1908 if (disp->mOpacity == 0.0 && aBuilder->IsForPainting() &&
1909 !aBuilder->WillComputePluginGeometry() &&
1910 !(disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) &&
1911 !nsLayoutUtils::HasAnimations(mContent, eCSSProperty_opacity)) {
1912 return;
1913 }
1914
1915 nsRect dirtyRect = aDirtyRect;
1916
1917 bool inTransform = aBuilder->IsInTransform();
1918 bool isTransformed = IsTransformed();
1919 // reset blend mode so we can keep track if this stacking context needs have
1920 // a nsDisplayBlendContainer. Set the blend mode back when the routine exits
1921 // so we keep track if the parent stacking context needs a container too.
1922 AutoSaveRestoreBlendMode autoRestoreBlendMode(*aBuilder);
1923 aBuilder->SetContainsBlendMode(false);
1924
1925 if (isTransformed) {
1926 const nsRect overflow = GetVisualOverflowRectRelativeToSelf();
1927 if (aBuilder->IsForPainting() &&
1928 nsDisplayTransform::ShouldPrerenderTransformedContent(aBuilder, this)) {
1929 dirtyRect = overflow;
1930 } else {
1931 if (overflow.IsEmpty() && !Preserves3DChildren()) {
1932 return;
1933 }
1934
1935 nsPoint offset = aBuilder->ToReferenceFrame(this);
1936 dirtyRect += offset;
1937
1938 nsRect untransformedDirtyRect;
1939 if (nsDisplayTransform::UntransformRect(dirtyRect, overflow, this, offset, &untransformedDirtyRect)) {
1940 dirtyRect = untransformedDirtyRect;
1941 } else {
1942 NS_WARNING("Unable to untransform dirty rect!");
1943 // This should only happen if the transform is singular, in which case nothing is visible anyway
1944 dirtyRect.SetEmpty();
1945 }
1946 }
1947 inTransform = true;
1948 }
1949
1950 bool useOpacity = HasVisualOpacity() && !nsSVGUtils::CanOptimizeOpacity(this);
1951 bool useBlendMode = disp->mMixBlendMode != NS_STYLE_BLEND_NORMAL;
1952 bool usingSVGEffects = nsSVGIntegrationUtils::UsingEffectsForFrame(this);
1953 bool useStickyPosition = disp->mPosition == NS_STYLE_POSITION_STICKY &&
1954 IsScrollFrameActive(nsLayoutUtils::GetNearestScrollableFrame(GetParent(),
1955 nsLayoutUtils::SCROLLABLE_SAME_DOC |
1956 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN));
1957
1958 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
1959
1960 if (isTransformed || useOpacity || useBlendMode || usingSVGEffects || useStickyPosition) {
1961 // We don't need to pass ancestor clipping down to our children;
1962 // everything goes inside a display item's child list, and the display
1963 // item itself will be clipped.
1964 // For transforms we also need to clear ancestor clipping because it's
1965 // relative to the wrong display item reference frame anyway.
1966 clipState.Clear();
1967 }
1968
1969 nsDisplayListCollection set;
1970 {
1971 nsDisplayListBuilder::AutoBuildingDisplayList rootSetter(aBuilder, true);
1972 DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
1973 nsDisplayListBuilder::AutoInTransformSetter
1974 inTransformSetter(aBuilder, inTransform);
1975 CheckForTouchEventHandler(aBuilder, this);
1976
1977 if (usingSVGEffects) {
1978 dirtyRect =
1979 nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect);
1980 }
1981
1982 nsRect clipPropClip;
1983 if (ApplyClipPropClipping(aBuilder, this, disp, &clipPropClip,
1984 nestedClipState)) {
1985 dirtyRect.IntersectRect(dirtyRect, clipPropClip);
1986 }
1987
1988 MarkAbsoluteFramesForDisplayList(aBuilder, dirtyRect);
1989
1990 // Preserve3DChildren() also guarantees that applyAbsPosClipping and usingSVGEffects are false
1991 // We only modify the preserve-3d rect if we are the top of a preserve-3d heirarchy
1992 if (Preserves3DChildren()) {
1993 aBuilder->MarkPreserve3DFramesForDisplayList(this, aDirtyRect);
1994 }
1995
1996 if (aBuilder->IsBuildingLayerEventRegions()) {
1997 nsDisplayLayerEventRegions* eventRegions =
1998 new (aBuilder) nsDisplayLayerEventRegions(aBuilder, this);
1999 aBuilder->SetLayerEventRegions(eventRegions);
2000 set.BorderBackground()->AppendNewToTop(eventRegions);
2001 }
2002 BuildDisplayList(aBuilder, dirtyRect, set);
2003 }
2004
2005 if (aBuilder->IsBackgroundOnly()) {
2006 set.BlockBorderBackgrounds()->DeleteAll();
2007 set.Floats()->DeleteAll();
2008 set.Content()->DeleteAll();
2009 set.PositionedDescendants()->DeleteAll();
2010 set.Outlines()->DeleteAll();
2011 }
2012
2013 // This z-order sort also sorts secondarily by content order. We need to do
2014 // this so that boxes produced by the same element are placed together
2015 // in the sort. Consider a position:relative inline element that breaks
2016 // across lines and has absolutely positioned children; all the abs-pos
2017 // children should be z-ordered after all the boxes for the position:relative
2018 // element itself.
2019 set.PositionedDescendants()->SortByZOrder(aBuilder, GetContent());
2020
2021 nsDisplayList resultList;
2022 // Now follow the rules of http://www.w3.org/TR/CSS21/zindex.html
2023 // 1,2: backgrounds and borders
2024 resultList.AppendToTop(set.BorderBackground());
2025 // 3: negative z-index children.
2026 for (;;) {
2027 nsDisplayItem* item = set.PositionedDescendants()->GetBottom();
2028 if (item && item->ZIndex() < 0) {
2029 set.PositionedDescendants()->RemoveBottom();
2030 resultList.AppendToTop(item);
2031 continue;
2032 }
2033 break;
2034 }
2035 // 4: block backgrounds
2036 resultList.AppendToTop(set.BlockBorderBackgrounds());
2037 // 5: floats
2038 resultList.AppendToTop(set.Floats());
2039 // 7: general content
2040 resultList.AppendToTop(set.Content());
2041 // 7.5: outlines, in content tree order. We need to sort by content order
2042 // because an element with outline that breaks and has children with outline
2043 // might have placed child outline items between its own outline items.
2044 // The element's outline items need to all come before any child outline
2045 // items.
2046 nsIContent* content = GetContent();
2047 if (!content) {
2048 content = PresContext()->Document()->GetRootElement();
2049 }
2050 if (content) {
2051 set.Outlines()->SortByContentOrder(aBuilder, content);
2052 }
2053 #ifdef DEBUG
2054 DisplayDebugBorders(aBuilder, this, set);
2055 #endif
2056 resultList.AppendToTop(set.Outlines());
2057 // 8, 9: non-negative z-index children
2058 resultList.AppendToTop(set.PositionedDescendants());
2059
2060 if (!isTransformed) {
2061 // Restore saved clip state now so that any display items we create below
2062 // are clipped properly.
2063 clipState.Restore();
2064 }
2065
2066 /* If there are any SVG effects, wrap the list up in an SVG effects item
2067 * (which also handles CSS group opacity). Note that we create an SVG effects
2068 * item even if resultList is empty, since a filter can produce graphical
2069 * output even if the element being filtered wouldn't otherwise do so.
2070 */
2071 if (usingSVGEffects) {
2072 /* List now emptied, so add the new list to the top. */
2073 resultList.AppendNewToTop(
2074 new (aBuilder) nsDisplaySVGEffects(aBuilder, this, &resultList));
2075 }
2076 /* Else, if the list is non-empty and there is CSS group opacity without SVG
2077 * effects, wrap it up in an opacity item.
2078 */
2079 else if (useOpacity && !resultList.IsEmpty()) {
2080 resultList.AppendNewToTop(
2081 new (aBuilder) nsDisplayOpacity(aBuilder, this, &resultList));
2082 }
2083 /* If we have sticky positioning, wrap it in a sticky position item.
2084 */
2085 if (useStickyPosition) {
2086 resultList.AppendNewToTop(
2087 new (aBuilder) nsDisplayStickyPosition(aBuilder, this, &resultList));
2088 }
2089
2090 /* If we're going to apply a transformation and don't have preserve-3d set, wrap
2091 * everything in an nsDisplayTransform. If there's nothing in the list, don't add
2092 * anything.
2093 *
2094 * For the preserve-3d case we want to individually wrap every child in the list with
2095 * a separate nsDisplayTransform instead. When the child is already an nsDisplayTransform,
2096 * we can skip this step, as the computed transform will already include our own.
2097 *
2098 * We also traverse into sublists created by nsDisplayWrapList or nsDisplayOpacity, so that
2099 * we find all the correct children.
2100 */
2101 if (isTransformed && !resultList.IsEmpty()) {
2102 // Restore clip state now so nsDisplayTransform is clipped properly.
2103 clipState.Restore();
2104
2105 if (Preserves3DChildren()) {
2106 WrapPreserve3DList(this, aBuilder, &resultList);
2107 } else {
2108 resultList.AppendNewToTop(
2109 new (aBuilder) nsDisplayTransform(aBuilder, this, &resultList));
2110 }
2111 }
2112
2113 /* If adding both a nsDisplayBlendContainer and a nsDisplayMixBlendMode to the
2114 * same list, the nsDisplayBlendContainer should be added first. This only
2115 * happens when the element creating this stacking context has mix-blend-mode
2116 * and also contains a child which has mix-blend-mode.
2117 * The nsDisplayBlendContainer must be added to the list first, so it does not
2118 * isolate the containing element blending as well.
2119 */
2120
2121 if (aBuilder->ContainsBlendMode()) {
2122 resultList.AppendNewToTop(
2123 new (aBuilder) nsDisplayBlendContainer(aBuilder, this, &resultList));
2124 }
2125
2126 /* If there's blending, wrap up the list in a blend-mode item. Note
2127 * that opacity can be applied before blending as the blend color is
2128 * not affected by foreground opacity (only background alpha).
2129 */
2130
2131 if (useBlendMode && !resultList.IsEmpty()) {
2132 resultList.AppendNewToTop(
2133 new (aBuilder) nsDisplayMixBlendMode(aBuilder, this, &resultList));
2134 }
2135
2136 CreateOwnLayerIfNeeded(aBuilder, &resultList);
2137
2138 aList->AppendToTop(&resultList);
2139 }
2140
2141 static nsDisplayItem*
2142 WrapInWrapList(nsDisplayListBuilder* aBuilder,
2143 nsIFrame* aFrame, nsDisplayList* aList)
2144 {
2145 nsDisplayItem* item = aList->GetBottom();
2146 if (!item || item->GetAbove() || item->Frame() != aFrame) {
2147 return new (aBuilder) nsDisplayWrapList(aBuilder, aFrame, aList);
2148 }
2149 aList->RemoveBottom();
2150 return item;
2151 }
2152
2153 void
2154 nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
2155 nsIFrame* aChild,
2156 const nsRect& aDirtyRect,
2157 const nsDisplayListSet& aLists,
2158 uint32_t aFlags) {
2159 // If painting is restricted to just the background of the top level frame,
2160 // then we have nothing to do here.
2161 if (aBuilder->IsBackgroundOnly())
2162 return;
2163
2164 nsIFrame* child = aChild;
2165 if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE)
2166 return;
2167
2168 bool isSVG = (child->GetStateBits() & NS_FRAME_SVG_LAYOUT);
2169
2170 // true if this is a real or pseudo stacking context
2171 bool pseudoStackingContext =
2172 (aFlags & DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT) != 0;
2173 if (!isSVG &&
2174 (aFlags & DISPLAY_CHILD_INLINE) &&
2175 !child->IsFrameOfType(eLineParticipant)) {
2176 // child is a non-inline frame in an inline context, i.e.,
2177 // it acts like inline-block or inline-table. Therefore it is a
2178 // pseudo-stacking-context.
2179 pseudoStackingContext = true;
2180 }
2181
2182 // dirty rect in child-relative coordinates
2183 nsRect dirty = aDirtyRect - child->GetOffsetTo(this);
2184
2185 nsIAtom* childType = child->GetType();
2186 nsDisplayListBuilder::OutOfFlowDisplayData* savedOutOfFlowData = nullptr;
2187 if (childType == nsGkAtoms::placeholderFrame) {
2188 nsPlaceholderFrame* placeholder = static_cast<nsPlaceholderFrame*>(child);
2189 child = placeholder->GetOutOfFlowFrame();
2190 NS_ASSERTION(child, "No out of flow frame?");
2191 // If 'child' is a pushed float then it's owned by a block that's not an
2192 // ancestor of the placeholder, and it will be painted by that block and
2193 // should not be painted through the placeholder.
2194 if (!child || nsLayoutUtils::IsPopup(child) ||
2195 (child->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT))
2196 return;
2197 // Make sure that any attempt to use childType below is disappointed. We
2198 // could call GetType again but since we don't currently need it, let's
2199 // avoid the virtual call.
2200 childType = nullptr;
2201 // Recheck NS_FRAME_TOO_DEEP_IN_FRAME_TREE
2202 if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE)
2203 return;
2204 savedOutOfFlowData = static_cast<nsDisplayListBuilder::OutOfFlowDisplayData*>
2205 (child->Properties().Get(nsDisplayListBuilder::OutOfFlowDisplayDataProperty()));
2206 if (savedOutOfFlowData) {
2207 dirty = savedOutOfFlowData->mDirtyRect;
2208 } else {
2209 // The out-of-flow frame did not intersect the dirty area. We may still
2210 // need to traverse into it, since it may contain placeholders we need
2211 // to enter to reach other out-of-flow frames that are visible.
2212 dirty.SetEmpty();
2213 }
2214 pseudoStackingContext = true;
2215 }
2216 if (child->Preserves3D()) {
2217 nsRect* savedDirty = static_cast<nsRect*>
2218 (child->Properties().Get(nsDisplayListBuilder::Preserve3DDirtyRectProperty()));
2219 if (savedDirty) {
2220 dirty = *savedDirty;
2221 } else {
2222 dirty.SetEmpty();
2223 }
2224 }
2225
2226 NS_ASSERTION(childType != nsGkAtoms::placeholderFrame,
2227 "Should have dealt with placeholders already");
2228 if (aBuilder->GetSelectedFramesOnly() &&
2229 child->IsLeaf() &&
2230 !aChild->IsSelected()) {
2231 return;
2232 }
2233
2234 if (aBuilder->GetIncludeAllOutOfFlows() &&
2235 (child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
2236 dirty = child->GetVisualOverflowRect();
2237 } else if (!(child->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
2238 // No need to descend into child to catch placeholders for visible
2239 // positioned stuff. So see if we can short-circuit frame traversal here.
2240
2241 // We can stop if child's frame subtree's intersection with the
2242 // dirty area is empty.
2243 // If the child is a scrollframe that we want to ignore, then we need
2244 // to descend into it because its scrolled child may intersect the dirty
2245 // area even if the scrollframe itself doesn't.
2246 // There are cases where the "ignore scroll frame" on the builder is not set
2247 // correctly, and so we additionally want to catch cases where the child is
2248 // a root scrollframe and we are ignoring scrolling on the viewport.
2249 nsIPresShell* shell = PresContext()->PresShell();
2250 bool keepDescending = child == aBuilder->GetIgnoreScrollFrame() ||
2251 (shell->IgnoringViewportScrolling() && child == shell->GetRootScrollFrame());
2252 if (!keepDescending) {
2253 nsRect childDirty;
2254 if (!childDirty.IntersectRect(dirty, child->GetVisualOverflowRect()))
2255 return;
2256 // Usually we could set dirty to childDirty now but there's no
2257 // benefit, and it can be confusing. It can especially confuse
2258 // situations where we're going to ignore a scrollframe's clipping;
2259 // we wouldn't want to clip the dirty area to the scrollframe's
2260 // bounds in that case.
2261 }
2262 }
2263
2264 // XXX need to have inline-block and inline-table set pseudoStackingContext
2265
2266 const nsStyleDisplay* ourDisp = StyleDisplay();
2267 // REVIEW: Taken from nsBoxFrame::Paint
2268 // Don't paint our children if the theme object is a leaf.
2269 if (IsThemed(ourDisp) &&
2270 !PresContext()->GetTheme()->WidgetIsContainer(ourDisp->mAppearance))
2271 return;
2272
2273 // Child is composited if it's transformed, partially transparent, or has
2274 // SVG effects or a blend mode..
2275 const nsStyleDisplay* disp = child->StyleDisplay();
2276 const nsStylePosition* pos = child->StylePosition();
2277 bool isVisuallyAtomic = child->HasOpacity()
2278 || child->IsTransformed()
2279 // strictly speaking, 'perspective' doesn't require visual atomicity,
2280 // but the spec says it acts like the rest of these
2281 || disp->mChildPerspective.GetUnit() == eStyleUnit_Coord
2282 || disp->mMixBlendMode != NS_STYLE_BLEND_NORMAL
2283 || nsSVGIntegrationUtils::UsingEffectsForFrame(child);
2284
2285 bool isPositioned = disp->IsPositioned(child);
2286 bool isStackingContext =
2287 (isPositioned && (disp->mPosition == NS_STYLE_POSITION_STICKY ||
2288 pos->mZIndex.GetUnit() == eStyleUnit_Integer)) ||
2289 (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT) ||
2290 isVisuallyAtomic || (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT);
2291
2292 if (isVisuallyAtomic || isPositioned || (!isSVG && disp->IsFloating(child)) ||
2293 ((disp->mClipFlags & NS_STYLE_CLIP_RECT) &&
2294 IsSVGContentWithCSSClip(child)) ||
2295 (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT) ||
2296 (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) {
2297 // If you change this, also change IsPseudoStackingContextFromStyle()
2298 pseudoStackingContext = true;
2299 }
2300 NS_ASSERTION(!isStackingContext || pseudoStackingContext,
2301 "Stacking contexts must also be pseudo-stacking-contexts");
2302
2303 bool isInFixedPos = aBuilder->IsInFixedPos() ||
2304 (isPositioned &&
2305 disp->mPosition == NS_STYLE_POSITION_FIXED &&
2306 nsLayoutUtils::IsReallyFixedPos(child));
2307 nsDisplayListBuilder::AutoInFixedPosSetter
2308 buildingInFixedPos(aBuilder, isInFixedPos);
2309
2310 nsDisplayListBuilder::AutoBuildingDisplayList
2311 buildingForChild(aBuilder, child, pseudoStackingContext);
2312 DisplayListClipState::AutoClipMultiple clipState(aBuilder);
2313 CheckForTouchEventHandler(aBuilder, child);
2314
2315 if (savedOutOfFlowData) {
2316 clipState.SetClipForContainingBlockDescendants(
2317 &savedOutOfFlowData->mContainingBlockClip);
2318 }
2319
2320 // Setup clipping for the parent's overflow:-moz-hidden-unscrollable,
2321 // or overflow:hidden on elements that don't support scrolling (and therefore
2322 // don't create nsHTML/XULScrollFrame). This clipping needs to not clip
2323 // anything directly rendered by the parent, only the rendering of its
2324 // children.
2325 // Don't use overflowClip to restrict the dirty rect, since some of the
2326 // descendants may not be clipped by it. Even if we end up with unnecessary
2327 // display items, they'll be pruned during ComputeVisibility.
2328 nsIFrame* parent = child->GetParent();
2329 const nsStyleDisplay* parentDisp =
2330 parent == this ? ourDisp : parent->StyleDisplay();
2331 ApplyOverflowClipping(aBuilder, parent, parentDisp, clipState);
2332
2333 nsDisplayList list;
2334 nsDisplayList extraPositionedDescendants;
2335 if (isStackingContext) {
2336 if (disp->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
2337 aBuilder->SetContainsBlendMode(true);
2338 }
2339 // True stacking context.
2340 // For stacking contexts, BuildDisplayListForStackingContext handles
2341 // clipping and MarkAbsoluteFramesForDisplayList.
2342 child->BuildDisplayListForStackingContext(aBuilder, dirty, &list);
2343 aBuilder->DisplayCaret(child, dirty, &list);
2344 } else {
2345 nsRect clipRect;
2346 if (ApplyClipPropClipping(aBuilder, child, disp, &clipRect, clipState)) {
2347 // clipRect is in builder-reference-frame coordinates,
2348 // dirty/clippedDirtyRect are in child coordinates
2349 dirty.IntersectRect(dirty, clipRect);
2350 }
2351
2352 child->MarkAbsoluteFramesForDisplayList(aBuilder, dirty);
2353
2354 if (!pseudoStackingContext) {
2355 // THIS IS THE COMMON CASE.
2356 // Not a pseudo or real stacking context. Do the simple thing and
2357 // return early.
2358 nsDisplayLayerEventRegions* eventRegions = aBuilder->GetLayerEventRegions();
2359 if (eventRegions) {
2360 eventRegions->AddFrame(aBuilder, child);
2361 }
2362 child->BuildDisplayList(aBuilder, dirty, aLists);
2363 aBuilder->DisplayCaret(child, dirty, aLists.Content());
2364 #ifdef DEBUG
2365 DisplayDebugBorders(aBuilder, child, aLists);
2366 #endif
2367 return;
2368 }
2369
2370 // A pseudo-stacking context (e.g., a positioned element with z-index auto).
2371 // We allow positioned descendants of the child to escape to our parent
2372 // stacking context's positioned descendant list, because they might be
2373 // z-index:non-auto
2374 nsDisplayListCollection pseudoStack;
2375 if (aBuilder->IsBuildingLayerEventRegions()) {
2376 nsDisplayLayerEventRegions* eventRegions =
2377 new (aBuilder) nsDisplayLayerEventRegions(aBuilder, this);
2378 aBuilder->SetLayerEventRegions(eventRegions);
2379 pseudoStack.BorderBackground()->AppendNewToTop(eventRegions);
2380 }
2381 child->BuildDisplayList(aBuilder, dirty, pseudoStack);
2382 aBuilder->DisplayCaret(child, dirty, pseudoStack.Content());
2383
2384 list.AppendToTop(pseudoStack.BorderBackground());
2385 list.AppendToTop(pseudoStack.BlockBorderBackgrounds());
2386 list.AppendToTop(pseudoStack.Floats());
2387 list.AppendToTop(pseudoStack.Content());
2388 list.AppendToTop(pseudoStack.Outlines());
2389 extraPositionedDescendants.AppendToTop(pseudoStack.PositionedDescendants());
2390 #ifdef DEBUG
2391 DisplayDebugBorders(aBuilder, child, aLists);
2392 #endif
2393 }
2394
2395 // Clear clip rect for the construction of the items below. Since we're
2396 // clipping all their contents, they themselves don't need to be clipped.
2397 clipState.Clear();
2398
2399 if (isPositioned || isVisuallyAtomic ||
2400 (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) {
2401 // Genuine stacking contexts, and positioned pseudo-stacking-contexts,
2402 // go in this level.
2403 if (!list.IsEmpty()) {
2404 nsDisplayItem* item = WrapInWrapList(aBuilder, child, &list);
2405 if (isSVG) {
2406 aLists.Content()->AppendNewToTop(item);
2407 } else {
2408 aLists.PositionedDescendants()->AppendNewToTop(item);
2409 }
2410 }
2411 } else if (!isSVG && disp->IsFloating(child)) {
2412 if (!list.IsEmpty()) {
2413 aLists.Floats()->AppendNewToTop(WrapInWrapList(aBuilder, child, &list));
2414 }
2415 } else {
2416 aLists.Content()->AppendToTop(&list);
2417 }
2418 // We delay placing the positioned descendants of positioned frames to here,
2419 // because in the absence of z-index this is the correct order for them.
2420 // This doesn't affect correctness because the positioned descendants list
2421 // is sorted by z-order and content in BuildDisplayListForStackingContext,
2422 // but it means that sort routine needs to do less work.
2423 aLists.PositionedDescendants()->AppendToTop(&extraPositionedDescendants);
2424 }
2425
2426 void
2427 nsIFrame::MarkAbsoluteFramesForDisplayList(nsDisplayListBuilder* aBuilder,
2428 const nsRect& aDirtyRect)
2429 {
2430 if (IsAbsoluteContainer()) {
2431 aBuilder->MarkFramesForDisplayList(this, GetAbsoluteContainingBlock()->GetChildList(), aDirtyRect);
2432 }
2433 }
2434
2435 nsresult
2436 nsFrame::GetContentForEvent(WidgetEvent* aEvent,
2437 nsIContent** aContent)
2438 {
2439 nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(this);
2440 *aContent = f->GetContent();
2441 NS_IF_ADDREF(*aContent);
2442 return NS_OK;
2443 }
2444
2445 void
2446 nsFrame::FireDOMEvent(const nsAString& aDOMEventName, nsIContent *aContent)
2447 {
2448 nsIContent* target = aContent ? aContent : mContent;
2449
2450 if (target) {
2451 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
2452 new AsyncEventDispatcher(target, aDOMEventName, true, false);
2453 DebugOnly<nsresult> rv = asyncDispatcher->PostDOMEvent();
2454 NS_ASSERTION(NS_SUCCEEDED(rv), "AsyncEventDispatcher failed to dispatch");
2455 }
2456 }
2457
2458 nsresult
2459 nsFrame::HandleEvent(nsPresContext* aPresContext,
2460 WidgetGUIEvent* aEvent,
2461 nsEventStatus* aEventStatus)
2462 {
2463
2464 if (aEvent->message == NS_MOUSE_MOVE) {
2465 // XXX If the second argument of HandleDrag() is WidgetMouseEvent,
2466 // the implementation becomes simpler.
2467 return HandleDrag(aPresContext, aEvent, aEventStatus);
2468 }
2469
2470 if ((aEvent->eventStructType == NS_MOUSE_EVENT &&
2471 aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) ||
2472 aEvent->eventStructType == NS_TOUCH_EVENT) {
2473 if (aEvent->message == NS_MOUSE_BUTTON_DOWN || aEvent->message == NS_TOUCH_START) {
2474 HandlePress(aPresContext, aEvent, aEventStatus);
2475 } else if (aEvent->message == NS_MOUSE_BUTTON_UP || aEvent->message == NS_TOUCH_END) {
2476 HandleRelease(aPresContext, aEvent, aEventStatus);
2477 }
2478 }
2479 return NS_OK;
2480 }
2481
2482 NS_IMETHODIMP
2483 nsFrame::GetDataForTableSelection(const nsFrameSelection* aFrameSelection,
2484 nsIPresShell* aPresShell,
2485 WidgetMouseEvent* aMouseEvent,
2486 nsIContent** aParentContent,
2487 int32_t* aContentOffset,
2488 int32_t* aTarget)
2489 {
2490 if (!aFrameSelection || !aPresShell || !aMouseEvent || !aParentContent || !aContentOffset || !aTarget)
2491 return NS_ERROR_NULL_POINTER;
2492
2493 *aParentContent = nullptr;
2494 *aContentOffset = 0;
2495 *aTarget = 0;
2496
2497 int16_t displaySelection = aPresShell->GetSelectionFlags();
2498
2499 bool selectingTableCells = aFrameSelection->GetTableCellSelection();
2500
2501 // DISPLAY_ALL means we're in an editor.
2502 // If already in cell selection mode,
2503 // continue selecting with mouse drag or end on mouse up,
2504 // or when using shift key to extend block of cells
2505 // (Mouse down does normal selection unless Ctrl/Cmd is pressed)
2506 bool doTableSelection =
2507 displaySelection == nsISelectionDisplay::DISPLAY_ALL && selectingTableCells &&
2508 (aMouseEvent->message == NS_MOUSE_MOVE ||
2509 (aMouseEvent->message == NS_MOUSE_BUTTON_UP &&
2510 aMouseEvent->button == WidgetMouseEvent::eLeftButton) ||
2511 aMouseEvent->IsShift());
2512
2513 if (!doTableSelection)
2514 {
2515 // In Browser, special 'table selection' key must be pressed for table selection
2516 // or when just Shift is pressed and we're already in table/cell selection mode
2517 #ifdef XP_MACOSX
2518 doTableSelection = aMouseEvent->IsMeta() || (aMouseEvent->IsShift() && selectingTableCells);
2519 #else
2520 doTableSelection = aMouseEvent->IsControl() || (aMouseEvent->IsShift() && selectingTableCells);
2521 #endif
2522 }
2523 if (!doTableSelection)
2524 return NS_OK;
2525
2526 // Get the cell frame or table frame (or parent) of the current content node
2527 nsIFrame *frame = this;
2528 bool foundCell = false;
2529 bool foundTable = false;
2530
2531 // Get the limiting node to stop parent frame search
2532 nsIContent* limiter = aFrameSelection->GetLimiter();
2533
2534 // If our content node is an ancestor of the limiting node,
2535 // we should stop the search right now.
2536 if (limiter && nsContentUtils::ContentIsDescendantOf(limiter, GetContent()))
2537 return NS_OK;
2538
2539 //We don't initiate row/col selection from here now,
2540 // but we may in future
2541 //bool selectColumn = false;
2542 //bool selectRow = false;
2543
2544 while (frame)
2545 {
2546 // Check for a table cell by querying to a known CellFrame interface
2547 nsITableCellLayout *cellElement = do_QueryFrame(frame);
2548 if (cellElement)
2549 {
2550 foundCell = true;
2551 //TODO: If we want to use proximity to top or left border
2552 // for row and column selection, this is the place to do it
2553 break;
2554 }
2555 else
2556 {
2557 // If not a cell, check for table
2558 // This will happen when starting frame is the table or child of a table,
2559 // such as a row (we were inbetween cells or in table border)
2560 nsTableOuterFrame *tableFrame = do_QueryFrame(frame);
2561 if (tableFrame)
2562 {
2563 foundTable = true;
2564 //TODO: How can we select row when along left table edge
2565 // or select column when along top edge?
2566 break;
2567 } else {
2568 frame = frame->GetParent();
2569 // Stop if we have hit the selection's limiting content node
2570 if (frame && frame->GetContent() == limiter)
2571 break;
2572 }
2573 }
2574 }
2575 // We aren't in a cell or table
2576 if (!foundCell && !foundTable) return NS_OK;
2577
2578 nsIContent* tableOrCellContent = frame->GetContent();
2579 if (!tableOrCellContent) return NS_ERROR_FAILURE;
2580
2581 nsCOMPtr<nsIContent> parentContent = tableOrCellContent->GetParent();
2582 if (!parentContent) return NS_ERROR_FAILURE;
2583
2584 int32_t offset = parentContent->IndexOf(tableOrCellContent);
2585 // Not likely?
2586 if (offset < 0) return NS_ERROR_FAILURE;
2587
2588 // Everything is OK -- set the return values
2589 *aParentContent = parentContent;
2590 NS_ADDREF(*aParentContent);
2591
2592 *aContentOffset = offset;
2593
2594 #if 0
2595 if (selectRow)
2596 *aTarget = nsISelectionPrivate::TABLESELECTION_ROW;
2597 else if (selectColumn)
2598 *aTarget = nsISelectionPrivate::TABLESELECTION_COLUMN;
2599 else
2600 #endif
2601 if (foundCell)
2602 *aTarget = nsISelectionPrivate::TABLESELECTION_CELL;
2603 else if (foundTable)
2604 *aTarget = nsISelectionPrivate::TABLESELECTION_TABLE;
2605
2606 return NS_OK;
2607 }
2608
2609 nsresult
2610 nsFrame::IsSelectable(bool* aSelectable, uint8_t* aSelectStyle) const
2611 {
2612 if (!aSelectable) //it's ok if aSelectStyle is null
2613 return NS_ERROR_NULL_POINTER;
2614
2615 // Like 'visibility', we must check all the parents: if a parent
2616 // is not selectable, none of its children is selectable.
2617 //
2618 // The -moz-all value acts similarly: if a frame has 'user-select:-moz-all',
2619 // all its children are selectable, even those with 'user-select:none'.
2620 //
2621 // As a result, if 'none' and '-moz-all' are not present in the frame hierarchy,
2622 // aSelectStyle returns the first style that is not AUTO. If these values
2623 // are present in the frame hierarchy, aSelectStyle returns the style of the
2624 // topmost parent that has either 'none' or '-moz-all'.
2625 //
2626 // For instance, if the frame hierarchy is:
2627 // AUTO -> _MOZ_ALL -> NONE -> TEXT, the returned value is _MOZ_ALL
2628 // TEXT -> NONE -> AUTO -> _MOZ_ALL, the returned value is TEXT
2629 // _MOZ_ALL -> TEXT -> AUTO -> AUTO, the returned value is _MOZ_ALL
2630 // AUTO -> CELL -> TEXT -> AUTO, the returned value is TEXT
2631 //
2632 uint8_t selectStyle = NS_STYLE_USER_SELECT_AUTO;
2633 nsIFrame* frame = const_cast<nsFrame*>(this);
2634
2635 while (frame) {
2636 const nsStyleUIReset* userinterface = frame->StyleUIReset();
2637 switch (userinterface->mUserSelect) {
2638 case NS_STYLE_USER_SELECT_ALL:
2639 case NS_STYLE_USER_SELECT_MOZ_ALL:
2640 // override the previous values
2641 selectStyle = userinterface->mUserSelect;
2642 break;
2643 default:
2644 // otherwise return the first value which is not 'auto'
2645 if (selectStyle == NS_STYLE_USER_SELECT_AUTO) {
2646 selectStyle = userinterface->mUserSelect;
2647 }
2648 break;
2649 }
2650 frame = frame->GetParent();
2651 }
2652
2653 // convert internal values to standard values
2654 if (selectStyle == NS_STYLE_USER_SELECT_AUTO)
2655 selectStyle = NS_STYLE_USER_SELECT_TEXT;
2656 else
2657 if (selectStyle == NS_STYLE_USER_SELECT_MOZ_ALL)
2658 selectStyle = NS_STYLE_USER_SELECT_ALL;
2659
2660 // return stuff
2661 if (aSelectStyle)
2662 *aSelectStyle = selectStyle;
2663 if (mState & NS_FRAME_GENERATED_CONTENT)
2664 *aSelectable = false;
2665 else
2666 *aSelectable = (selectStyle != NS_STYLE_USER_SELECT_NONE);
2667 return NS_OK;
2668 }
2669
2670 /**
2671 * Handles the Mouse Press Event for the frame
2672 */
2673 NS_IMETHODIMP
2674 nsFrame::HandlePress(nsPresContext* aPresContext,
2675 WidgetGUIEvent* aEvent,
2676 nsEventStatus* aEventStatus)
2677 {
2678 NS_ENSURE_ARG_POINTER(aEventStatus);
2679 if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
2680 return NS_OK;
2681 }
2682
2683 NS_ENSURE_ARG_POINTER(aEvent);
2684 if (aEvent->eventStructType == NS_TOUCH_EVENT) {
2685 return NS_OK;
2686 }
2687
2688 //We often get out of sync state issues with mousedown events that
2689 //get interrupted by alerts/dialogs.
2690 //Check with the ESM to see if we should process this one
2691 if (!aPresContext->EventStateManager()->EventStatusOK(aEvent))
2692 return NS_OK;
2693
2694 nsresult rv;
2695 nsIPresShell *shell = aPresContext->GetPresShell();
2696 if (!shell)
2697 return NS_ERROR_FAILURE;
2698
2699 // if we are in Navigator and the click is in a draggable node, we don't want
2700 // to start selection because we don't want to interfere with a potential
2701 // drag of said node and steal all its glory.
2702 int16_t isEditor = shell->GetSelectionFlags();
2703 //weaaak. only the editor can display frame selection not just text and images
2704 isEditor = isEditor == nsISelectionDisplay::DISPLAY_ALL;
2705
2706 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
2707
2708 if (!mouseEvent->IsAlt()) {
2709 for (nsIContent* content = mContent; content;
2710 content = content->GetParent()) {
2711 if (nsContentUtils::ContentIsDraggable(content) &&
2712 !content->IsEditable()) {
2713 // coordinate stuff is the fix for bug #55921
2714 if ((mRect - GetPosition()).Contains(
2715 nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, this))) {
2716 return NS_OK;
2717 }
2718 }
2719 }
2720 }
2721
2722 // check whether style allows selection
2723 // if not, don't tell selection the mouse event even occurred.
2724 bool selectable;
2725 uint8_t selectStyle;
2726 rv = IsSelectable(&selectable, &selectStyle);
2727 if (NS_FAILED(rv)) return rv;
2728
2729 // check for select: none
2730 if (!selectable)
2731 return NS_OK;
2732
2733 // When implementing NS_STYLE_USER_SELECT_ELEMENT, NS_STYLE_USER_SELECT_ELEMENTS and
2734 // NS_STYLE_USER_SELECT_TOGGLE, need to change this logic
2735 bool useFrameSelection = (selectStyle == NS_STYLE_USER_SELECT_TEXT);
2736
2737 // If the mouse is dragged outside the nearest enclosing scrollable area
2738 // while making a selection, the area will be scrolled. To do this, capture
2739 // the mouse on the nearest scrollable frame. If there isn't a scrollable
2740 // frame, or something else is already capturing the mouse, there's no
2741 // reason to capture.
2742 bool hasCapturedContent = false;
2743 if (!nsIPresShell::GetCapturingContent()) {
2744 nsIScrollableFrame* scrollFrame =
2745 nsLayoutUtils::GetNearestScrollableFrame(this,
2746 nsLayoutUtils::SCROLLABLE_SAME_DOC |
2747 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
2748 if (scrollFrame) {
2749 nsIFrame* capturingFrame = do_QueryFrame(scrollFrame);
2750 nsIPresShell::SetCapturingContent(capturingFrame->GetContent(),
2751 CAPTURE_IGNOREALLOWED);
2752 hasCapturedContent = true;
2753 }
2754 }
2755
2756 // XXX This is screwy; it really should use the selection frame, not the
2757 // event frame
2758 const nsFrameSelection* frameselection = nullptr;
2759 if (useFrameSelection)
2760 frameselection = GetConstFrameSelection();
2761 else
2762 frameselection = shell->ConstFrameSelection();
2763
2764 if (!frameselection || frameselection->GetDisplaySelection() == nsISelectionController::SELECTION_OFF)
2765 return NS_OK;//nothing to do we cannot affect selection from here
2766
2767 #ifdef XP_MACOSX
2768 if (mouseEvent->IsControl())
2769 return NS_OK;//short circuit. hard coded for mac due to time restraints.
2770 bool control = mouseEvent->IsMeta();
2771 #else
2772 bool control = mouseEvent->IsControl();
2773 #endif
2774
2775 nsRefPtr<nsFrameSelection> fc = const_cast<nsFrameSelection*>(frameselection);
2776 if (mouseEvent->clickCount > 1) {
2777 // These methods aren't const but can't actually delete anything,
2778 // so no need for nsWeakFrame.
2779 fc->SetMouseDownState(true);
2780 fc->SetMouseDoubleDown(true);
2781 return HandleMultiplePress(aPresContext, mouseEvent, aEventStatus, control);
2782 }
2783
2784 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, this);
2785 ContentOffsets offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN);
2786
2787 if (!offsets.content)
2788 return NS_ERROR_FAILURE;
2789
2790 // On touchables devices, touch the screen is usually a pan action,
2791 // so let's reposition the caret if needed but do not select text
2792 // if the touch did not happen over an editable element. Otherwise,
2793 // let the user move the caret by tapping and dragging.
2794 if (!offsets.content->IsEditable() &&
2795 Preferences::GetBool("browser.ignoreNativeFrameTextSelection", false)) {
2796 // On touchables devices, mouse events are generated if the gesture is a tap.
2797 // Such events are never going to generate a drag action, so let's release
2798 // captured content if any.
2799 if (hasCapturedContent) {
2800 nsIPresShell::SetCapturingContent(nullptr, 0);
2801 }
2802
2803 return fc->HandleClick(offsets.content, offsets.StartOffset(),
2804 offsets.EndOffset(), false, false,
2805 offsets.associateWithNext);
2806 }
2807
2808 // Let Ctrl/Cmd+mouse down do table selection instead of drag initiation
2809 nsCOMPtr<nsIContent>parentContent;
2810 int32_t contentOffset;
2811 int32_t target;
2812 rv = GetDataForTableSelection(frameselection, shell, mouseEvent,
2813 getter_AddRefs(parentContent), &contentOffset,
2814 &target);
2815 if (NS_SUCCEEDED(rv) && parentContent)
2816 {
2817 fc->SetMouseDownState(true);
2818 return fc->HandleTableSelection(parentContent, contentOffset, target,
2819 mouseEvent);
2820 }
2821
2822 fc->SetDelayedCaretData(0);
2823
2824 // Check if any part of this frame is selected, and if the
2825 // user clicked inside the selected region. If so, we delay
2826 // starting a new selection since the user may be trying to
2827 // drag the selected region to some other app.
2828
2829 SelectionDetails *details = 0;
2830 if (GetContent()->IsSelectionDescendant())
2831 {
2832 bool inSelection = false;
2833 details = frameselection->LookUpSelection(offsets.content, 0,
2834 offsets.EndOffset(), false);
2835
2836 //
2837 // If there are any details, check to see if the user clicked
2838 // within any selected region of the frame.
2839 //
2840
2841 SelectionDetails *curDetail = details;
2842
2843 while (curDetail)
2844 {
2845 //
2846 // If the user clicked inside a selection, then just
2847 // return without doing anything. We will handle placing
2848 // the caret later on when the mouse is released. We ignore
2849 // the spellcheck, find and url formatting selections.
2850 //
2851 if (curDetail->mType != nsISelectionController::SELECTION_SPELLCHECK &&
2852 curDetail->mType != nsISelectionController::SELECTION_FIND &&
2853 curDetail->mType != nsISelectionController::SELECTION_URLSECONDARY &&
2854 curDetail->mStart <= offsets.StartOffset() &&
2855 offsets.EndOffset() <= curDetail->mEnd)
2856 {
2857 inSelection = true;
2858 }
2859
2860 SelectionDetails *nextDetail = curDetail->mNext;
2861 delete curDetail;
2862 curDetail = nextDetail;
2863 }
2864
2865 if (inSelection) {
2866 fc->SetMouseDownState(false);
2867 fc->SetDelayedCaretData(mouseEvent);
2868 return NS_OK;
2869 }
2870 }
2871
2872 fc->SetMouseDownState(true);
2873
2874 // Do not touch any nsFrame members after this point without adding
2875 // weakFrame checks.
2876 rv = fc->HandleClick(offsets.content, offsets.StartOffset(),
2877 offsets.EndOffset(), mouseEvent->IsShift(), control,
2878 offsets.associateWithNext);
2879
2880 if (NS_FAILED(rv))
2881 return rv;
2882
2883 if (offsets.offset != offsets.secondaryOffset)
2884 fc->MaintainSelection();
2885
2886 if (isEditor && !mouseEvent->IsShift() &&
2887 (offsets.EndOffset() - offsets.StartOffset()) == 1)
2888 {
2889 // A single node is selected and we aren't extending an existing
2890 // selection, which means the user clicked directly on an object (either
2891 // -moz-user-select: all or a non-text node without children).
2892 // Therefore, disable selection extension during mouse moves.
2893 // XXX This is a bit hacky; shouldn't editor be able to deal with this?
2894 fc->SetMouseDownState(false);
2895 }
2896
2897 return rv;
2898 }
2899
2900 /*
2901 * SelectByTypeAtPoint
2902 *
2903 * Search for selectable content at point and attempt to select
2904 * based on the start and end selection behaviours.
2905 *
2906 * @param aPresContext Presentation context
2907 * @param aPoint Point at which selection will occur. Coordinates
2908 * should be relaitve to this frame.
2909 * @param aBeginAmountType, aEndAmountType Selection behavior, see
2910 * nsIFrame for definitions.
2911 * @param aSelectFlags Selection flags defined in nsFame.h.
2912 * @return success or failure at finding suitable content to select.
2913 */
2914 nsresult
2915 nsFrame::SelectByTypeAtPoint(nsPresContext* aPresContext,
2916 const nsPoint& aPoint,
2917 nsSelectionAmount aBeginAmountType,
2918 nsSelectionAmount aEndAmountType,
2919 uint32_t aSelectFlags)
2920 {
2921 NS_ENSURE_ARG_POINTER(aPresContext);
2922
2923 // No point in selecting if selection is turned off
2924 if (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF)
2925 return NS_OK;
2926
2927 ContentOffsets offsets = GetContentOffsetsFromPoint(aPoint, SKIP_HIDDEN);
2928 if (!offsets.content)
2929 return NS_ERROR_FAILURE;
2930
2931 int32_t offset;
2932 const nsFrameSelection* frameSelection =
2933 PresContext()->GetPresShell()->ConstFrameSelection();
2934 nsIFrame* theFrame = frameSelection->
2935 GetFrameForNodeOffset(offsets.content, offsets.offset,
2936 nsFrameSelection::HINT(offsets.associateWithNext),
2937 &offset);
2938 if (!theFrame)
2939 return NS_ERROR_FAILURE;
2940
2941 nsFrame* frame = static_cast<nsFrame*>(theFrame);
2942 return frame->PeekBackwardAndForward(aBeginAmountType, aEndAmountType,
2943 offset, aPresContext,
2944 aBeginAmountType != eSelectWord,
2945 aSelectFlags);
2946 }
2947
2948 /**
2949 * Multiple Mouse Press -- line or paragraph selection -- for the frame.
2950 * Wouldn't it be nice if this didn't have to be hardwired into Frame code?
2951 */
2952 NS_IMETHODIMP
2953 nsFrame::HandleMultiplePress(nsPresContext* aPresContext,
2954 WidgetGUIEvent* aEvent,
2955 nsEventStatus* aEventStatus,
2956 bool aControlHeld)
2957 {
2958 NS_ENSURE_ARG_POINTER(aEvent);
2959 NS_ENSURE_ARG_POINTER(aEventStatus);
2960
2961 if (nsEventStatus_eConsumeNoDefault == *aEventStatus ||
2962 DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF) {
2963 return NS_OK;
2964 }
2965
2966 // Find out whether we're doing line or paragraph selection.
2967 // If browser.triple_click_selects_paragraph is true, triple-click selects paragraph.
2968 // Otherwise, triple-click selects line, and quadruple-click selects paragraph
2969 // (on platforms that support quadruple-click).
2970 nsSelectionAmount beginAmount, endAmount;
2971 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
2972 if (!mouseEvent) {
2973 return NS_OK;
2974 }
2975
2976 if (mouseEvent->clickCount == 4) {
2977 beginAmount = endAmount = eSelectParagraph;
2978 } else if (mouseEvent->clickCount == 3) {
2979 if (Preferences::GetBool("browser.triple_click_selects_paragraph")) {
2980 beginAmount = endAmount = eSelectParagraph;
2981 } else {
2982 beginAmount = eSelectBeginLine;
2983 endAmount = eSelectEndLine;
2984 }
2985 } else if (mouseEvent->clickCount == 2) {
2986 // We only want inline frames; PeekBackwardAndForward dislikes blocks
2987 beginAmount = endAmount = eSelectWord;
2988 } else {
2989 return NS_OK;
2990 }
2991
2992 nsPoint relPoint =
2993 nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, this);
2994 return SelectByTypeAtPoint(aPresContext, relPoint, beginAmount, endAmount,
2995 (aControlHeld ? SELECT_ACCUMULATE : 0));
2996 }
2997
2998 nsresult
2999 nsFrame::PeekBackwardAndForward(nsSelectionAmount aAmountBack,
3000 nsSelectionAmount aAmountForward,
3001 int32_t aStartPos,
3002 nsPresContext* aPresContext,
3003 bool aJumpLines,
3004 uint32_t aSelectFlags)
3005 {
3006 nsIFrame* baseFrame = this;
3007 int32_t baseOffset = aStartPos;
3008 nsresult rv;
3009
3010 if (aAmountBack == eSelectWord) {
3011 // To avoid selecting the previous word when at start of word,
3012 // first move one character forward.
3013 nsPeekOffsetStruct pos(eSelectCharacter,
3014 eDirNext,
3015 aStartPos,
3016 0,
3017 aJumpLines,
3018 true, //limit on scrolled views
3019 false,
3020 false);
3021 rv = PeekOffset(&pos);
3022 if (NS_SUCCEEDED(rv)) {
3023 baseFrame = pos.mResultFrame;
3024 baseOffset = pos.mContentOffset;
3025 }
3026 }
3027
3028 // Use peek offset one way then the other:
3029 nsPeekOffsetStruct startpos(aAmountBack,
3030 eDirPrevious,
3031 baseOffset,
3032 0,
3033 aJumpLines,
3034 true, //limit on scrolled views
3035 false,
3036 false);
3037 rv = baseFrame->PeekOffset(&startpos);
3038 if (NS_FAILED(rv))
3039 return rv;
3040
3041 nsPeekOffsetStruct endpos(aAmountForward,
3042 eDirNext,
3043 aStartPos,
3044 0,
3045 aJumpLines,
3046 true, //limit on scrolled views
3047 false,
3048 false);
3049 rv = PeekOffset(&endpos);
3050 if (NS_FAILED(rv))
3051 return rv;
3052
3053 // Keep frameSelection alive.
3054 nsRefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
3055
3056 rv = frameSelection->HandleClick(startpos.mResultContent,
3057 startpos.mContentOffset, startpos.mContentOffset,
3058 false, (aSelectFlags & SELECT_ACCUMULATE),
3059 nsFrameSelection::HINTRIGHT);
3060 if (NS_FAILED(rv))
3061 return rv;
3062
3063 rv = frameSelection->HandleClick(endpos.mResultContent,
3064 endpos.mContentOffset, endpos.mContentOffset,
3065 true, false,
3066 nsFrameSelection::HINTLEFT);
3067 if (NS_FAILED(rv))
3068 return rv;
3069
3070 // maintain selection
3071 return frameSelection->MaintainSelection(aAmountBack);
3072 }
3073
3074 NS_IMETHODIMP nsFrame::HandleDrag(nsPresContext* aPresContext,
3075 WidgetGUIEvent* aEvent,
3076 nsEventStatus* aEventStatus)
3077 {
3078 MOZ_ASSERT(aEvent->eventStructType == NS_MOUSE_EVENT, "HandleDrag can only handle mouse event");
3079
3080 bool selectable;
3081 IsSelectable(&selectable, nullptr);
3082
3083 // XXX Do we really need to exclude non-selectable content here?
3084 // GetContentOffsetsFromPoint can handle it just fine, although some
3085 // other stuff might not like it.
3086 if (!selectable)
3087 return NS_OK;
3088 if (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF) {
3089 return NS_OK;
3090 }
3091 nsIPresShell *presShell = aPresContext->PresShell();
3092
3093 nsRefPtr<nsFrameSelection> frameselection = GetFrameSelection();
3094 bool mouseDown = frameselection->GetMouseDownState();
3095 if (!mouseDown)
3096 return NS_OK;
3097
3098 frameselection->StopAutoScrollTimer();
3099
3100 // Check if we are dragging in a table cell
3101 nsCOMPtr<nsIContent> parentContent;
3102 int32_t contentOffset;
3103 int32_t target;
3104 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
3105 nsresult result;
3106 result = GetDataForTableSelection(frameselection, presShell, mouseEvent,
3107 getter_AddRefs(parentContent),
3108 &contentOffset, &target);
3109
3110 nsWeakFrame weakThis = this;
3111 if (NS_SUCCEEDED(result) && parentContent) {
3112 frameselection->HandleTableSelection(parentContent, contentOffset, target,
3113 mouseEvent);
3114 } else {
3115 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, this);
3116 frameselection->HandleDrag(this, pt);
3117 }
3118
3119 // The frameselection object notifies selection listeners synchronously above
3120 // which might have killed us.
3121 if (!weakThis.IsAlive()) {
3122 return NS_OK;
3123 }
3124
3125 // get the nearest scrollframe
3126 nsIScrollableFrame* scrollFrame =
3127 nsLayoutUtils::GetNearestScrollableFrame(this,
3128 nsLayoutUtils::SCROLLABLE_SAME_DOC |
3129 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
3130
3131 if (scrollFrame) {
3132 nsIFrame* capturingFrame = scrollFrame->GetScrolledFrame();
3133 if (capturingFrame) {
3134 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent,
3135 capturingFrame);
3136 frameselection->StartAutoScrollTimer(capturingFrame, pt, 30);
3137 }
3138 }
3139
3140 return NS_OK;
3141 }
3142
3143 /**
3144 * This static method handles part of the nsFrame::HandleRelease in a way
3145 * which doesn't rely on the nsFrame object to stay alive.
3146 */
3147 static nsresult
3148 HandleFrameSelection(nsFrameSelection* aFrameSelection,
3149 nsIFrame::ContentOffsets& aOffsets,
3150 bool aHandleTableSel,
3151 int32_t aContentOffsetForTableSel,
3152 int32_t aTargetForTableSel,
3153 nsIContent* aParentContentForTableSel,
3154 WidgetGUIEvent* aEvent,
3155 nsEventStatus* aEventStatus)
3156 {
3157 if (!aFrameSelection) {
3158 return NS_OK;
3159 }
3160
3161 nsresult rv = NS_OK;
3162
3163 if (nsEventStatus_eConsumeNoDefault != *aEventStatus) {
3164 if (!aHandleTableSel) {
3165 if (!aOffsets.content || !aFrameSelection->HasDelayedCaretData()) {
3166 return NS_ERROR_FAILURE;
3167 }
3168
3169 // We are doing this to simulate what we would have done on HandlePress.
3170 // We didn't do it there to give the user an opportunity to drag
3171 // the text, but since they didn't drag, we want to place the
3172 // caret.
3173 // However, we'll use the mouse position from the release, since:
3174 // * it's easier
3175 // * that's the normal click position to use (although really, in
3176 // the normal case, small movements that don't count as a drag
3177 // can do selection)
3178 aFrameSelection->SetMouseDownState(true);
3179
3180 rv = aFrameSelection->HandleClick(aOffsets.content,
3181 aOffsets.StartOffset(),
3182 aOffsets.EndOffset(),
3183 aFrameSelection->IsShiftDownInDelayedCaretData(),
3184 false,
3185 aOffsets.associateWithNext);
3186 if (NS_FAILED(rv)) {
3187 return rv;
3188 }
3189 } else if (aParentContentForTableSel) {
3190 aFrameSelection->SetMouseDownState(false);
3191 rv = aFrameSelection->HandleTableSelection(
3192 aParentContentForTableSel,
3193 aContentOffsetForTableSel,
3194 aTargetForTableSel,
3195 aEvent->AsMouseEvent());
3196 if (NS_FAILED(rv)) {
3197 return rv;
3198 }
3199 }
3200 aFrameSelection->SetDelayedCaretData(0);
3201 }
3202
3203 aFrameSelection->SetMouseDownState(false);
3204 aFrameSelection->StopAutoScrollTimer();
3205
3206 return NS_OK;
3207 }
3208
3209 NS_IMETHODIMP nsFrame::HandleRelease(nsPresContext* aPresContext,
3210 WidgetGUIEvent* aEvent,
3211 nsEventStatus* aEventStatus)
3212 {
3213 if (aEvent->eventStructType != NS_MOUSE_EVENT) {
3214 return NS_OK;
3215 }
3216
3217 nsIFrame* activeFrame = GetActiveSelectionFrame(aPresContext, this);
3218
3219 nsCOMPtr<nsIContent> captureContent = nsIPresShell::GetCapturingContent();
3220
3221 // We can unconditionally stop capturing because
3222 // we should never be capturing when the mouse button is up
3223 nsIPresShell::SetCapturingContent(nullptr, 0);
3224
3225 bool selectionOff =
3226 (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF);
3227
3228 nsRefPtr<nsFrameSelection> frameselection;
3229 ContentOffsets offsets;
3230 nsCOMPtr<nsIContent> parentContent;
3231 int32_t contentOffsetForTableSel = 0;
3232 int32_t targetForTableSel = 0;
3233 bool handleTableSelection = true;
3234
3235 if (!selectionOff) {
3236 frameselection = GetFrameSelection();
3237 if (nsEventStatus_eConsumeNoDefault != *aEventStatus && frameselection) {
3238 // Check if the frameselection recorded the mouse going down.
3239 // If not, the user must have clicked in a part of the selection.
3240 // Place the caret before continuing!
3241
3242 bool mouseDown = frameselection->GetMouseDownState();
3243
3244 if (!mouseDown && frameselection->HasDelayedCaretData() &&
3245 frameselection->GetClickCountInDelayedCaretData() < 2) {
3246 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this);
3247 offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN);
3248 handleTableSelection = false;
3249 } else {
3250 GetDataForTableSelection(frameselection, PresContext()->PresShell(),
3251 aEvent->AsMouseEvent(),
3252 getter_AddRefs(parentContent),
3253 &contentOffsetForTableSel,
3254 &targetForTableSel);
3255 }
3256 }
3257 }
3258
3259 // We might be capturing in some other document and the event just happened to
3260 // trickle down here. Make sure that document's frame selection is notified.
3261 // Note, this may cause the current nsFrame object to be deleted, bug 336592.
3262 nsRefPtr<nsFrameSelection> frameSelection;
3263 if (activeFrame != this &&
3264 static_cast<nsFrame*>(activeFrame)->DisplaySelection(activeFrame->PresContext())
3265 != nsISelectionController::SELECTION_OFF) {
3266 frameSelection = activeFrame->GetFrameSelection();
3267 }
3268
3269 // Also check the selection of the capturing content which might be in a
3270 // different document.
3271 if (!frameSelection && captureContent) {
3272 nsIDocument* doc = captureContent->GetCurrentDoc();
3273 if (doc) {
3274 nsIPresShell* capturingShell = doc->GetShell();
3275 if (capturingShell && capturingShell != PresContext()->GetPresShell()) {
3276 frameSelection = capturingShell->FrameSelection();
3277 }
3278 }
3279 }
3280
3281 if (frameSelection) {
3282 frameSelection->SetMouseDownState(false);
3283 frameSelection->StopAutoScrollTimer();
3284 }
3285
3286 // Do not call any methods of the current object after this point!!!
3287 // The object is perhaps dead!
3288
3289 return selectionOff
3290 ? NS_OK
3291 : HandleFrameSelection(frameselection, offsets, handleTableSelection,
3292 contentOffsetForTableSel, targetForTableSel,
3293 parentContent, aEvent, aEventStatus);
3294 }
3295
3296 struct MOZ_STACK_CLASS FrameContentRange {
3297 FrameContentRange(nsIContent* aContent, int32_t aStart, int32_t aEnd) :
3298 content(aContent), start(aStart), end(aEnd) { }
3299 nsCOMPtr<nsIContent> content;
3300 int32_t start;
3301 int32_t end;
3302 };
3303
3304 // Retrieve the content offsets of a frame
3305 static FrameContentRange GetRangeForFrame(nsIFrame* aFrame) {
3306 nsCOMPtr<nsIContent> content, parent;
3307 content = aFrame->GetContent();
3308 if (!content) {
3309 NS_WARNING("Frame has no content");
3310 return FrameContentRange(nullptr, -1, -1);
3311 }
3312 nsIAtom* type = aFrame->GetType();
3313 if (type == nsGkAtoms::textFrame) {
3314 int32_t offset, offsetEnd;
3315 aFrame->GetOffsets(offset, offsetEnd);
3316 return FrameContentRange(content, offset, offsetEnd);
3317 }
3318 if (type == nsGkAtoms::brFrame) {
3319 parent = content->GetParent();
3320 int32_t beginOffset = parent->IndexOf(content);
3321 return FrameContentRange(parent, beginOffset, beginOffset);
3322 }
3323 // Loop to deal with anonymous content, which has no index; this loop
3324 // probably won't run more than twice under normal conditions
3325 do {
3326 parent = content->GetParent();
3327 if (parent) {
3328 int32_t beginOffset = parent->IndexOf(content);
3329 if (beginOffset >= 0)
3330 return FrameContentRange(parent, beginOffset, beginOffset + 1);
3331 content = parent;
3332 }
3333 } while (parent);
3334
3335 // The root content node must act differently
3336 return FrameContentRange(content, 0, content->GetChildCount());
3337 }
3338
3339 // The FrameTarget represents the closest frame to a point that can be selected
3340 // The frame is the frame represented, frameEdge says whether one end of the
3341 // frame is the result (in which case different handling is needed), and
3342 // afterFrame says which end is repersented if frameEdge is true
3343 struct FrameTarget {
3344 FrameTarget(nsIFrame* aFrame, bool aFrameEdge, bool aAfterFrame,
3345 bool aEmptyBlock = false) :
3346 frame(aFrame), frameEdge(aFrameEdge), afterFrame(aAfterFrame),
3347 emptyBlock(aEmptyBlock) { }
3348 static FrameTarget Null() {
3349 return FrameTarget(nullptr, false, false);
3350 }
3351 bool IsNull() {
3352 return !frame;
3353 }
3354 nsIFrame* frame;
3355 bool frameEdge;
3356 bool afterFrame;
3357 bool emptyBlock;
3358 };
3359
3360 // See function implementation for information
3361 static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame, nsPoint aPoint,
3362 uint32_t aFlags);
3363
3364 static bool SelfIsSelectable(nsIFrame* aFrame, uint32_t aFlags)
3365 {
3366 if ((aFlags & nsIFrame::SKIP_HIDDEN) &&
3367 !aFrame->StyleVisibility()->IsVisible()) {
3368 return false;
3369 }
3370 return !aFrame->IsGeneratedContentFrame() &&
3371 aFrame->StyleUIReset()->mUserSelect != NS_STYLE_USER_SELECT_NONE;
3372 }
3373
3374 static bool SelectionDescendToKids(nsIFrame* aFrame) {
3375 uint8_t style = aFrame->StyleUIReset()->mUserSelect;
3376 nsIFrame* parent = aFrame->GetParent();
3377 // If we are only near (not directly over) then don't traverse
3378 // frames with independent selection (e.g. text and list controls)
3379 // unless we're already inside such a frame (see bug 268497). Note that this
3380 // prevents any of the users of this method from entering form controls.
3381 // XXX We might want some way to allow using the up-arrow to go into a form
3382 // control, but the focus didn't work right anyway; it'd probably be enough
3383 // if the left and right arrows could enter textboxes (which I don't believe
3384 // they can at the moment)
3385 return !aFrame->IsGeneratedContentFrame() &&
3386 style != NS_STYLE_USER_SELECT_ALL &&
3387 style != NS_STYLE_USER_SELECT_NONE &&
3388 ((parent->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION) ||
3389 !(aFrame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION));
3390 }
3391
3392 static FrameTarget GetSelectionClosestFrameForChild(nsIFrame* aChild,
3393 nsPoint aPoint,
3394 uint32_t aFlags)
3395 {
3396 nsIFrame* parent = aChild->GetParent();
3397 if (SelectionDescendToKids(aChild)) {
3398 nsPoint pt = aPoint - aChild->GetOffsetTo(parent);
3399 return GetSelectionClosestFrame(aChild, pt, aFlags);
3400 }
3401 return FrameTarget(aChild, false, false);
3402 }
3403
3404 // When the cursor needs to be at the beginning of a block, it shouldn't be
3405 // before the first child. A click on a block whose first child is a block
3406 // should put the cursor in the child. The cursor shouldn't be between the
3407 // blocks, because that's not where it's expected.
3408 // Note that this method is guaranteed to succeed.
3409 static FrameTarget DrillDownToSelectionFrame(nsIFrame* aFrame,
3410 bool aEndFrame, uint32_t aFlags) {
3411 if (SelectionDescendToKids(aFrame)) {
3412 nsIFrame* result = nullptr;
3413 nsIFrame *frame = aFrame->GetFirstPrincipalChild();
3414 if (!aEndFrame) {
3415 while (frame && (!SelfIsSelectable(frame, aFlags) ||
3416 frame->IsEmpty()))
3417 frame = frame->GetNextSibling();
3418 if (frame)
3419 result = frame;
3420 } else {
3421 // Because the frame tree is singly linked, to find the last frame,
3422 // we have to iterate through all the frames
3423 // XXX I have a feeling this could be slow for long blocks, although
3424 // I can't find any slowdowns
3425 while (frame) {
3426 if (!frame->IsEmpty() && SelfIsSelectable(frame, aFlags))
3427 result = frame;
3428 frame = frame->GetNextSibling();
3429 }
3430 }
3431 if (result)
3432 return DrillDownToSelectionFrame(result, aEndFrame, aFlags);
3433 }
3434 // If the current frame has no targetable children, target the current frame
3435 return FrameTarget(aFrame, true, aEndFrame);
3436 }
3437
3438 // This method finds the closest valid FrameTarget on a given line; if there is
3439 // no valid FrameTarget on the line, it returns a null FrameTarget
3440 static FrameTarget GetSelectionClosestFrameForLine(
3441 nsBlockFrame* aParent,
3442 nsBlockFrame::line_iterator aLine,
3443 nsPoint aPoint,
3444 uint32_t aFlags)
3445 {
3446 nsIFrame *frame = aLine->mFirstChild;
3447 // Account for end of lines (any iterator from the block is valid)
3448 if (aLine == aParent->end_lines())
3449 return DrillDownToSelectionFrame(aParent, true, aFlags);
3450 nsIFrame *closestFromIStart = nullptr, *closestFromIEnd = nullptr;
3451 nscoord closestIStart = aLine->IStart(), closestIEnd = aLine->IEnd();
3452 WritingMode wm = aLine->mWritingMode;
3453 LogicalPoint pt(wm, aPoint, aLine->mContainerWidth);
3454 for (int32_t n = aLine->GetChildCount(); n;
3455 --n, frame = frame->GetNextSibling()) {
3456 if (!SelfIsSelectable(frame, aFlags) || frame->IsEmpty())
3457 continue;
3458 LogicalRect frameRect = LogicalRect(wm, frame->GetRect(),
3459 aLine->mContainerWidth);
3460 if (pt.I(wm) >= frameRect.IStart(wm)) {
3461 if (pt.I(wm) < frameRect.IEnd(wm)) {
3462 return GetSelectionClosestFrameForChild(frame, aPoint, aFlags);
3463 }
3464 if (frameRect.IEnd(wm) >= closestIStart) {
3465 closestFromIStart = frame;
3466 closestIStart = frameRect.IEnd(wm);
3467 }
3468 } else {
3469 if (frameRect.IStart(wm) <= closestIEnd) {
3470 closestFromIEnd = frame;
3471 closestIEnd = frameRect.IStart(wm);
3472 }
3473 }
3474 }
3475 if (!closestFromIStart && !closestFromIEnd) {
3476 // We should only get here if there are no selectable frames on a line
3477 // XXX Do we need more elaborate handling here?
3478 return FrameTarget::Null();
3479 }
3480 if (closestFromIStart &&
3481 (!closestFromIEnd ||
3482 (abs(pt.I(wm) - closestIStart) <= abs(pt.I(wm) - closestIEnd)))) {
3483 return GetSelectionClosestFrameForChild(closestFromIStart, aPoint,
3484 aFlags);
3485 }
3486 return GetSelectionClosestFrameForChild(closestFromIEnd, aPoint, aFlags);
3487 }
3488
3489 // This method is for the special handling we do for block frames; they're
3490 // special because they represent paragraphs and because they are organized
3491 // into lines, which have bounds that are not stored elsewhere in the
3492 // frame tree. Returns a null FrameTarget for frames which are not
3493 // blocks or blocks with no lines except editable one.
3494 static FrameTarget GetSelectionClosestFrameForBlock(nsIFrame* aFrame,
3495 nsPoint aPoint,
3496 uint32_t aFlags)
3497 {
3498 nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(aFrame); // used only for QI
3499 if (!bf)
3500 return FrameTarget::Null();
3501
3502 // This code searches for the correct line
3503 nsBlockFrame::line_iterator firstLine = bf->begin_lines();
3504 nsBlockFrame::line_iterator end = bf->end_lines();
3505 if (firstLine == end) {
3506 nsIContent *blockContent = aFrame->GetContent();
3507 if (blockContent) {
3508 // Return with empty flag true.
3509 return FrameTarget(aFrame, false, false, true);
3510 }
3511 return FrameTarget::Null();
3512 }
3513 nsBlockFrame::line_iterator curLine = firstLine;
3514 nsBlockFrame::line_iterator closestLine = end;
3515 // Convert aPoint into a LogicalPoint in the writing-mode of this block
3516 WritingMode wm = curLine->mWritingMode;
3517 LogicalPoint pt(wm, aPoint, curLine->mContainerWidth);
3518 while (curLine != end) {
3519 // Check to see if our point lies within the line's block-direction bounds
3520 nscoord BCoord = pt.B(wm) - curLine->BStart();
3521 nscoord BSize = curLine->BSize();
3522 if (BCoord >= 0 && BCoord < BSize) {
3523 closestLine = curLine;
3524 break; // We found the line; stop looking
3525 }
3526 if (BCoord < 0)
3527 break;
3528 ++curLine;
3529 }
3530
3531 if (closestLine == end) {
3532 nsBlockFrame::line_iterator prevLine = curLine.prev();
3533 nsBlockFrame::line_iterator nextLine = curLine;
3534 // Avoid empty lines
3535 while (nextLine != end && nextLine->IsEmpty())
3536 ++nextLine;
3537 while (prevLine != end && prevLine->IsEmpty())
3538 --prevLine;
3539
3540 // This hidden pref dictates whether a point above or below all lines comes
3541 // up with a line or the beginning or end of the frame; 0 on Windows,
3542 // 1 on other platforms by default at the writing of this code
3543 int32_t dragOutOfFrame =
3544 Preferences::GetInt("browser.drag_out_of_frame_style");
3545
3546 if (prevLine == end) {
3547 if (dragOutOfFrame == 1 || nextLine == end)
3548 return DrillDownToSelectionFrame(aFrame, false, aFlags);
3549 closestLine = nextLine;
3550 } else if (nextLine == end) {
3551 if (dragOutOfFrame == 1)
3552 return DrillDownToSelectionFrame(aFrame, true, aFlags);
3553 closestLine = prevLine;
3554 } else { // Figure out which line is closer
3555 if (pt.B(wm) - prevLine->BEnd() < nextLine->BStart() - pt.B(wm))
3556 closestLine = prevLine;
3557 else
3558 closestLine = nextLine;
3559 }
3560 }
3561
3562 do {
3563 FrameTarget target = GetSelectionClosestFrameForLine(bf, closestLine,
3564 aPoint, aFlags);
3565 if (!target.IsNull())
3566 return target;
3567 ++closestLine;
3568 } while (closestLine != end);
3569 // Fall back to just targeting the last targetable place
3570 return DrillDownToSelectionFrame(aFrame, true, aFlags);
3571 }
3572
3573 // GetSelectionClosestFrame is the helper function that calculates the closest
3574 // frame to the given point.
3575 // It doesn't completely account for offset styles, so needs to be used in
3576 // restricted environments.
3577 // Cannot handle overlapping frames correctly, so it should receive the output
3578 // of GetFrameForPoint
3579 // Guaranteed to return a valid FrameTarget
3580 static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame, nsPoint aPoint,
3581 uint32_t aFlags)
3582 {
3583 {
3584 // Handle blocks; if the frame isn't a block, the method fails
3585 FrameTarget target = GetSelectionClosestFrameForBlock(aFrame, aPoint, aFlags);
3586 if (!target.IsNull())
3587 return target;
3588 }
3589
3590 nsIFrame *kid = aFrame->GetFirstPrincipalChild();
3591
3592 if (kid) {
3593 // Go through all the child frames to find the closest one
3594 nsIFrame::FrameWithDistance closest = { nullptr, nscoord_MAX, nscoord_MAX };
3595 for (; kid; kid = kid->GetNextSibling()) {
3596 if (!SelfIsSelectable(kid, aFlags) || kid->IsEmpty())
3597 continue;
3598
3599 kid->FindCloserFrameForSelection(aPoint, &closest);
3600 }
3601 if (closest.mFrame) {
3602 if (closest.mFrame->IsSVGText())
3603 return FrameTarget(closest.mFrame, false, false);
3604 return GetSelectionClosestFrameForChild(closest.mFrame, aPoint, aFlags);
3605 }
3606 }
3607 return FrameTarget(aFrame, false, false);
3608 }
3609
3610 nsIFrame::ContentOffsets OffsetsForSingleFrame(nsIFrame* aFrame, nsPoint aPoint)
3611 {
3612 nsIFrame::ContentOffsets offsets;
3613 FrameContentRange range = GetRangeForFrame(aFrame);
3614 offsets.content = range.content;
3615 // If there are continuations (meaning it's not one rectangle), this is the
3616 // best this function can do
3617 if (aFrame->GetNextContinuation() || aFrame->GetPrevContinuation()) {
3618 offsets.offset = range.start;
3619 offsets.secondaryOffset = range.end;
3620 offsets.associateWithNext = true;
3621 return offsets;
3622 }
3623
3624 // Figure out whether the offsets should be over, after, or before the frame
3625 nsRect rect(nsPoint(0, 0), aFrame->GetSize());
3626
3627 bool isBlock = aFrame->GetDisplay() != NS_STYLE_DISPLAY_INLINE;
3628 bool isRtl = (aFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL);
3629 if ((isBlock && rect.y < aPoint.y) ||
3630 (!isBlock && ((isRtl && rect.x + rect.width / 2 > aPoint.x) ||
3631 (!isRtl && rect.x + rect.width / 2 < aPoint.x)))) {
3632 offsets.offset = range.end;
3633 if (rect.Contains(aPoint))
3634 offsets.secondaryOffset = range.start;
3635 else
3636 offsets.secondaryOffset = range.end;
3637 } else {
3638 offsets.offset = range.start;
3639 if (rect.Contains(aPoint))
3640 offsets.secondaryOffset = range.end;
3641 else
3642 offsets.secondaryOffset = range.start;
3643 }
3644 offsets.associateWithNext = (offsets.offset == range.start);
3645 return offsets;
3646 }
3647
3648 static nsIFrame* AdjustFrameForSelectionStyles(nsIFrame* aFrame) {
3649 nsIFrame* adjustedFrame = aFrame;
3650 for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent())
3651 {
3652 // These are the conditions that make all children not able to handle
3653 // a cursor.
3654 if (frame->StyleUIReset()->mUserSelect == NS_STYLE_USER_SELECT_ALL ||
3655 frame->IsGeneratedContentFrame()) {
3656 adjustedFrame = frame;
3657 }
3658 }
3659 return adjustedFrame;
3660 }
3661
3662 nsIFrame::ContentOffsets nsIFrame::GetContentOffsetsFromPoint(nsPoint aPoint,
3663 uint32_t aFlags)
3664 {
3665 nsIFrame *adjustedFrame;
3666 if (aFlags & IGNORE_SELECTION_STYLE) {
3667 adjustedFrame = this;
3668 }
3669 else {
3670 // This section of code deals with special selection styles. Note that
3671 // -moz-all exists, even though it doesn't need to be explicitly handled.
3672 //
3673 // The offset is forced not to end up in generated content; content offsets
3674 // cannot represent content outside of the document's content tree.
3675
3676 adjustedFrame = AdjustFrameForSelectionStyles(this);
3677
3678 // -moz-user-select: all needs special handling, because clicking on it
3679 // should lead to the whole frame being selected
3680 if (adjustedFrame && adjustedFrame->StyleUIReset()->mUserSelect ==
3681 NS_STYLE_USER_SELECT_ALL) {
3682 nsPoint adjustedPoint = aPoint + this->GetOffsetTo(adjustedFrame);
3683 return OffsetsForSingleFrame(adjustedFrame, adjustedPoint);
3684 }
3685
3686 // For other cases, try to find a closest frame starting from the parent of
3687 // the unselectable frame
3688 if (adjustedFrame != this)
3689 adjustedFrame = adjustedFrame->GetParent();
3690 }
3691
3692 nsPoint adjustedPoint = aPoint + this->GetOffsetTo(adjustedFrame);
3693
3694 FrameTarget closest =
3695 GetSelectionClosestFrame(adjustedFrame, adjustedPoint, aFlags);
3696
3697 if (closest.emptyBlock) {
3698 ContentOffsets offsets;
3699 NS_ASSERTION(closest.frame,
3700 "closest.frame must not be null when it's empty");
3701 offsets.content = closest.frame->GetContent();
3702 offsets.offset = 0;
3703 offsets.secondaryOffset = 0;
3704 offsets.associateWithNext = true;
3705 return offsets;
3706 }
3707
3708 // If the correct offset is at one end of a frame, use offset-based
3709 // calculation method
3710 if (closest.frameEdge) {
3711 ContentOffsets offsets;
3712 FrameContentRange range = GetRangeForFrame(closest.frame);
3713 offsets.content = range.content;
3714 if (closest.afterFrame)
3715 offsets.offset = range.end;
3716 else
3717 offsets.offset = range.start;
3718 offsets.secondaryOffset = offsets.offset;
3719 offsets.associateWithNext = (offsets.offset == range.start);
3720 return offsets;
3721 }
3722
3723 nsPoint pt;
3724 if (closest.frame != this) {
3725 if (closest.frame->IsSVGText()) {
3726 pt = nsLayoutUtils::TransformAncestorPointToFrame(closest.frame,
3727 aPoint, this);
3728 } else {
3729 pt = aPoint - closest.frame->GetOffsetTo(this);
3730 }
3731 } else {
3732 pt = aPoint;
3733 }
3734 return static_cast<nsFrame*>(closest.frame)->CalcContentOffsetsFromFramePoint(pt);
3735
3736 // XXX should I add some kind of offset standardization?
3737 // consider <b>xxxxx</b><i>zzzzz</i>; should any click between the last
3738 // x and first z put the cursor in the same logical position in addition
3739 // to the same visual position?
3740 }
3741
3742 nsIFrame::ContentOffsets nsFrame::CalcContentOffsetsFromFramePoint(nsPoint aPoint)
3743 {
3744 return OffsetsForSingleFrame(this, aPoint);
3745 }
3746
3747 void
3748 nsIFrame::AssociateImage(const nsStyleImage& aImage, nsPresContext* aPresContext)
3749 {
3750 if (aImage.GetType() != eStyleImageType_Image) {
3751 return;
3752 }
3753
3754 imgIRequest *req = aImage.GetImageData();
3755 mozilla::css::ImageLoader* loader =
3756 aPresContext->Document()->StyleImageLoader();
3757
3758 // If this fails there's not much we can do ...
3759 loader->AssociateRequestToFrame(req, this);
3760 }
3761
3762 nsresult
3763 nsFrame::GetCursor(const nsPoint& aPoint,
3764 nsIFrame::Cursor& aCursor)
3765 {
3766 FillCursorInformationFromStyle(StyleUserInterface(), aCursor);
3767 if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) {
3768 // If this is editable, I-beam cursor is better for most elements.
3769 aCursor.mCursor =
3770 (mContent && mContent->IsEditable()) ? NS_STYLE_CURSOR_TEXT :
3771 NS_STYLE_CURSOR_DEFAULT;
3772 }
3773
3774
3775 return NS_OK;
3776 }
3777
3778 // Resize and incremental reflow
3779
3780 /* virtual */ void
3781 nsFrame::MarkIntrinsicWidthsDirty()
3782 {
3783 // This version is meant only for what used to be box-to-block adaptors.
3784 // It should not be called by other derived classes.
3785 if (IsBoxWrapped()) {
3786 nsBoxLayoutMetrics *metrics = BoxMetrics();
3787
3788 SizeNeedsRecalc(metrics->mPrefSize);
3789 SizeNeedsRecalc(metrics->mMinSize);
3790 SizeNeedsRecalc(metrics->mMaxSize);
3791 SizeNeedsRecalc(metrics->mBlockPrefSize);
3792 SizeNeedsRecalc(metrics->mBlockMinSize);
3793 CoordNeedsRecalc(metrics->mFlex);
3794 CoordNeedsRecalc(metrics->mAscent);
3795 }
3796
3797 if (GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) {
3798 nsFontInflationData::MarkFontInflationDataTextDirty(this);
3799 }
3800 }
3801
3802 /* virtual */ nscoord
3803 nsFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
3804 {
3805 nscoord result = 0;
3806 DISPLAY_MIN_WIDTH(this, result);
3807 return result;
3808 }
3809
3810 /* virtual */ nscoord
3811 nsFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
3812 {
3813 nscoord result = 0;
3814 DISPLAY_PREF_WIDTH(this, result);
3815 return result;
3816 }
3817
3818 /* virtual */ void
3819 nsFrame::AddInlineMinWidth(nsRenderingContext *aRenderingContext,
3820 nsIFrame::InlineMinWidthData *aData)
3821 {
3822 NS_ASSERTION(GetParent(), "Must have a parent if we get here!");
3823 nsIFrame* parent = GetParent();
3824 bool canBreak = !CanContinueTextRun() &&
3825 parent->StyleText()->WhiteSpaceCanWrap(parent);
3826
3827 if (canBreak)
3828 aData->OptionallyBreak(aRenderingContext);
3829 aData->trailingWhitespace = 0;
3830 aData->skipWhitespace = false;
3831 aData->trailingTextFrame = nullptr;
3832 aData->currentLine += nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
3833 this, nsLayoutUtils::MIN_WIDTH);
3834 aData->atStartOfLine = false;
3835 if (canBreak)
3836 aData->OptionallyBreak(aRenderingContext);
3837 }
3838
3839 /* virtual */ void
3840 nsFrame::AddInlinePrefWidth(nsRenderingContext *aRenderingContext,
3841 nsIFrame::InlinePrefWidthData *aData)
3842 {
3843 aData->trailingWhitespace = 0;
3844 aData->skipWhitespace = false;
3845 nscoord myPref = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
3846 this, nsLayoutUtils::PREF_WIDTH);
3847 aData->currentLine = NSCoordSaturatingAdd(aData->currentLine, myPref);
3848 }
3849
3850 void
3851 nsIFrame::InlineMinWidthData::ForceBreak(nsRenderingContext *aRenderingContext)
3852 {
3853 currentLine -= trailingWhitespace;
3854 prevLines = std::max(prevLines, currentLine);
3855 currentLine = trailingWhitespace = 0;
3856
3857 for (uint32_t i = 0, i_end = floats.Length(); i != i_end; ++i) {
3858 nscoord float_min = floats[i].Width();
3859 if (float_min > prevLines)
3860 prevLines = float_min;
3861 }
3862 floats.Clear();
3863 trailingTextFrame = nullptr;
3864 skipWhitespace = true;
3865 }
3866
3867 void
3868 nsIFrame::InlineMinWidthData::OptionallyBreak(nsRenderingContext *aRenderingContext,
3869 nscoord aHyphenWidth)
3870 {
3871 trailingTextFrame = nullptr;
3872
3873 // If we can fit more content into a smaller width by staying on this
3874 // line (because we're still at a negative offset due to negative
3875 // text-indent or negative margin), don't break. Otherwise, do the
3876 // same as ForceBreak. it doesn't really matter when we accumulate
3877 // floats.
3878 if (currentLine + aHyphenWidth < 0 || atStartOfLine)
3879 return;
3880 currentLine += aHyphenWidth;
3881 ForceBreak(aRenderingContext);
3882 }
3883
3884 void
3885 nsIFrame::InlinePrefWidthData::ForceBreak(nsRenderingContext *aRenderingContext)
3886 {
3887 if (floats.Length() != 0) {
3888 // preferred widths accumulated for floats that have already
3889 // been cleared past
3890 nscoord floats_done = 0,
3891 // preferred widths accumulated for floats that have not yet
3892 // been cleared past
3893 floats_cur_left = 0,
3894 floats_cur_right = 0;
3895
3896 for (uint32_t i = 0, i_end = floats.Length(); i != i_end; ++i) {
3897 const FloatInfo& floatInfo = floats[i];
3898 const nsStyleDisplay *floatDisp = floatInfo.Frame()->StyleDisplay();
3899 if (floatDisp->mBreakType == NS_STYLE_CLEAR_LEFT ||
3900 floatDisp->mBreakType == NS_STYLE_CLEAR_RIGHT ||
3901 floatDisp->mBreakType == NS_STYLE_CLEAR_BOTH) {
3902 nscoord floats_cur = NSCoordSaturatingAdd(floats_cur_left,
3903 floats_cur_right);
3904 if (floats_cur > floats_done)
3905 floats_done = floats_cur;
3906 if (floatDisp->mBreakType != NS_STYLE_CLEAR_RIGHT)
3907 floats_cur_left = 0;
3908 if (floatDisp->mBreakType != NS_STYLE_CLEAR_LEFT)
3909 floats_cur_right = 0;
3910 }
3911
3912 nscoord &floats_cur = floatDisp->mFloats == NS_STYLE_FLOAT_LEFT
3913 ? floats_cur_left : floats_cur_right;
3914 nscoord floatWidth = floatInfo.Width();
3915 // Negative-width floats don't change the available space so they
3916 // shouldn't change our intrinsic line width either.
3917 floats_cur =
3918 NSCoordSaturatingAdd(floats_cur, std::max(0, floatWidth));
3919 }
3920
3921 nscoord floats_cur =
3922 NSCoordSaturatingAdd(floats_cur_left, floats_cur_right);
3923 if (floats_cur > floats_done)
3924 floats_done = floats_cur;
3925
3926 currentLine = NSCoordSaturatingAdd(currentLine, floats_done);
3927
3928 floats.Clear();
3929 }
3930
3931 currentLine =
3932 NSCoordSaturatingSubtract(currentLine, trailingWhitespace, nscoord_MAX);
3933 prevLines = std::max(prevLines, currentLine);
3934 currentLine = trailingWhitespace = 0;
3935 skipWhitespace = true;
3936 }
3937
3938 static void
3939 AddCoord(const nsStyleCoord& aStyle,
3940 nsRenderingContext* aRenderingContext,
3941 nsIFrame* aFrame,
3942 nscoord* aCoord, float* aPercent,
3943 bool aClampNegativeToZero)
3944 {
3945 switch (aStyle.GetUnit()) {
3946 case eStyleUnit_Coord: {
3947 NS_ASSERTION(!aClampNegativeToZero || aStyle.GetCoordValue() >= 0,
3948 "unexpected negative value");
3949 *aCoord += aStyle.GetCoordValue();
3950 return;
3951 }
3952 case eStyleUnit_Percent: {
3953 NS_ASSERTION(!aClampNegativeToZero || aStyle.GetPercentValue() >= 0.0f,
3954 "unexpected negative value");
3955 *aPercent += aStyle.GetPercentValue();
3956 return;
3957 }
3958 case eStyleUnit_Calc: {
3959 const nsStyleCoord::Calc *calc = aStyle.GetCalcValue();
3960 if (aClampNegativeToZero) {
3961 // This is far from ideal when one is negative and one is positive.
3962 *aCoord += std::max(calc->mLength, 0);
3963 *aPercent += std::max(calc->mPercent, 0.0f);
3964 } else {
3965 *aCoord += calc->mLength;
3966 *aPercent += calc->mPercent;
3967 }
3968 return;
3969 }
3970 default: {
3971 return;
3972 }
3973 }
3974 }
3975
3976 /* virtual */ nsIFrame::IntrinsicWidthOffsetData
3977 nsFrame::IntrinsicWidthOffsets(nsRenderingContext* aRenderingContext)
3978 {
3979 IntrinsicWidthOffsetData result;
3980
3981 const nsStyleMargin *styleMargin = StyleMargin();
3982 AddCoord(styleMargin->mMargin.GetLeft(), aRenderingContext, this,
3983 &result.hMargin, &result.hPctMargin, false);
3984 AddCoord(styleMargin->mMargin.GetRight(), aRenderingContext, this,
3985 &result.hMargin, &result.hPctMargin, false);
3986
3987 const nsStylePadding *stylePadding = StylePadding();
3988 AddCoord(stylePadding->mPadding.GetLeft(), aRenderingContext, this,
3989 &result.hPadding, &result.hPctPadding, true);
3990 AddCoord(stylePadding->mPadding.GetRight(), aRenderingContext, this,
3991 &result.hPadding, &result.hPctPadding, true);
3992
3993 const nsStyleBorder *styleBorder = StyleBorder();
3994 result.hBorder += styleBorder->GetComputedBorderWidth(NS_SIDE_LEFT);
3995 result.hBorder += styleBorder->GetComputedBorderWidth(NS_SIDE_RIGHT);
3996
3997 const nsStyleDisplay *disp = StyleDisplay();
3998 if (IsThemed(disp)) {
3999 nsPresContext *presContext = PresContext();
4000
4001 nsIntMargin border;
4002 presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(),
4003 this, disp->mAppearance,
4004 &border);
4005 result.hBorder = presContext->DevPixelsToAppUnits(border.LeftRight());
4006
4007 nsIntMargin padding;
4008 if (presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(),
4009 this, disp->mAppearance,
4010 &padding)) {
4011 result.hPadding = presContext->DevPixelsToAppUnits(padding.LeftRight());
4012 result.hPctPadding = 0;
4013 }
4014 }
4015
4016 return result;
4017 }
4018
4019 /* virtual */ IntrinsicSize
4020 nsFrame::GetIntrinsicSize()
4021 {
4022 return IntrinsicSize(); // default is width/height set to eStyleUnit_None
4023 }
4024
4025 /* virtual */ nsSize
4026 nsFrame::GetIntrinsicRatio()
4027 {
4028 return nsSize(0, 0);
4029 }
4030
4031 /* virtual */ nsSize
4032 nsFrame::ComputeSize(nsRenderingContext *aRenderingContext,
4033 nsSize aCBSize, nscoord aAvailableWidth,
4034 nsSize aMargin, nsSize aBorder, nsSize aPadding,
4035 uint32_t aFlags)
4036 {
4037 nsSize result = ComputeAutoSize(aRenderingContext, aCBSize, aAvailableWidth,
4038 aMargin, aBorder, aPadding,
4039 aFlags & eShrinkWrap);
4040 nsSize boxSizingAdjust(0,0);
4041 const nsStylePosition *stylePos = StylePosition();
4042
4043 switch (stylePos->mBoxSizing) {
4044 case NS_STYLE_BOX_SIZING_BORDER:
4045 boxSizingAdjust += aBorder;
4046 // fall through
4047 case NS_STYLE_BOX_SIZING_PADDING:
4048 boxSizingAdjust += aPadding;
4049 }
4050 nscoord boxSizingToMarginEdgeWidth =
4051 aMargin.width + aBorder.width + aPadding.width - boxSizingAdjust.width;
4052 const nsStyleCoord* widthStyleCoord = &(stylePos->mWidth);
4053 const nsStyleCoord* heightStyleCoord = &(stylePos->mHeight);
4054
4055 bool isFlexItem = IsFlexItem();
4056 bool isHorizontalFlexItem = false;
4057
4058 if (isFlexItem) {
4059 // Flex items use their "flex-basis" property in place of their main-size
4060 // property (e.g. "width") for sizing purposes, *unless* they have
4061 // "flex-basis:auto", in which case they use their main-size property after
4062 // all.
4063 uint32_t flexDirection = mParent->StylePosition()->mFlexDirection;
4064 isHorizontalFlexItem =
4065 flexDirection == NS_STYLE_FLEX_DIRECTION_ROW ||
4066 flexDirection == NS_STYLE_FLEX_DIRECTION_ROW_REVERSE;
4067
4068 // NOTE: The logic here should match the similar chunk for determining
4069 // widthStyleCoord and heightStyleCoord in
4070 // nsLayoutUtils::ComputeSizeWithIntrinsicDimensions().
4071 const nsStyleCoord* flexBasis = &(stylePos->mFlexBasis);
4072 if (flexBasis->GetUnit() != eStyleUnit_Auto) {
4073 if (isHorizontalFlexItem) {
4074 widthStyleCoord = flexBasis;
4075 } else {
4076 // One caveat for vertical flex items: We don't support enumerated
4077 // values (e.g. "max-content") for height properties yet. So, if our
4078 // computed flex-basis is an enumerated value, we'll just behave as if
4079 // it were "auto", which means "use the main-size property after all"
4080 // (which is "height", in this case).
4081 // NOTE: Once we support intrinsic sizing keywords for "height",
4082 // we should remove this check.
4083 if (flexBasis->GetUnit() != eStyleUnit_Enumerated) {
4084 heightStyleCoord = flexBasis;
4085 }
4086 }
4087 }
4088 }
4089
4090 // Compute width
4091
4092 if (widthStyleCoord->GetUnit() != eStyleUnit_Auto) {
4093 result.width =
4094 nsLayoutUtils::ComputeWidthValue(aRenderingContext, this,
4095 aCBSize.width, boxSizingAdjust.width, boxSizingToMarginEdgeWidth,
4096 *widthStyleCoord);
4097 }
4098
4099 // Flex items ignore their min & max sizing properties in their
4100 // flex container's main-axis. (Those properties get applied later in
4101 // the flexbox algorithm.)
4102 if (stylePos->mMaxWidth.GetUnit() != eStyleUnit_None &&
4103 !(isFlexItem && isHorizontalFlexItem)) {
4104 nscoord maxWidth =
4105 nsLayoutUtils::ComputeWidthValue(aRenderingContext, this,
4106 aCBSize.width, boxSizingAdjust.width, boxSizingToMarginEdgeWidth,
4107 stylePos->mMaxWidth);
4108 result.width = std::min(maxWidth, result.width);
4109 }
4110
4111 nscoord minWidth;
4112 if (!(isFlexItem && isHorizontalFlexItem)) {
4113 minWidth =
4114 nsLayoutUtils::ComputeWidthValue(aRenderingContext, this,
4115 aCBSize.width, boxSizingAdjust.width, boxSizingToMarginEdgeWidth,
4116 stylePos->mMinWidth);
4117 } else {
4118 minWidth = 0;
4119 }
4120 result.width = std::max(minWidth, result.width);
4121
4122 // Compute height
4123 // (but not if we're auto-height or if we recieved the "eUseAutoHeight"
4124 // flag -- then, we'll just stick with the height that we already calculated
4125 // in the initial ComputeAutoSize() call.)
4126 if (!nsLayoutUtils::IsAutoHeight(*heightStyleCoord, aCBSize.height) &&
4127 !(aFlags & nsIFrame::eUseAutoHeight)) {
4128 result.height =
4129 nsLayoutUtils::ComputeHeightValue(aCBSize.height,
4130 boxSizingAdjust.height,
4131 *heightStyleCoord);
4132 }
4133
4134 if (result.height != NS_UNCONSTRAINEDSIZE) {
4135 if (!nsLayoutUtils::IsAutoHeight(stylePos->mMaxHeight, aCBSize.height) &&
4136 !(isFlexItem && !isHorizontalFlexItem)) {
4137 nscoord maxHeight =
4138 nsLayoutUtils::ComputeHeightValue(aCBSize.height,
4139 boxSizingAdjust.height,
4140 stylePos->mMaxHeight);
4141 result.height = std::min(maxHeight, result.height);
4142 }
4143
4144 if (!nsLayoutUtils::IsAutoHeight(stylePos->mMinHeight, aCBSize.height) &&
4145 !(isFlexItem && !isHorizontalFlexItem)) {
4146 nscoord minHeight =
4147 nsLayoutUtils::ComputeHeightValue(aCBSize.height,
4148 boxSizingAdjust.height,
4149 stylePos->mMinHeight);
4150 result.height = std::max(minHeight, result.height);
4151 }
4152 }
4153
4154 const nsStyleDisplay *disp = StyleDisplay();
4155 if (IsThemed(disp)) {
4156 nsIntSize widget(0, 0);
4157 bool canOverride = true;
4158 nsPresContext *presContext = PresContext();
4159 presContext->GetTheme()->
4160 GetMinimumWidgetSize(aRenderingContext, this, disp->mAppearance,
4161 &widget, &canOverride);
4162
4163 nsSize size;
4164 size.width = presContext->DevPixelsToAppUnits(widget.width);
4165 size.height = presContext->DevPixelsToAppUnits(widget.height);
4166
4167 // GMWS() returns border-box; we need content-box
4168 size.width -= aBorder.width + aPadding.width;
4169 size.height -= aBorder.height + aPadding.height;
4170
4171 if (size.height > result.height || !canOverride)
4172 result.height = size.height;
4173 if (size.width > result.width || !canOverride)
4174 result.width = size.width;
4175 }
4176
4177 result.width = std::max(0, result.width);
4178 result.height = std::max(0, result.height);
4179
4180 return result;
4181 }
4182
4183 nsRect
4184 nsIFrame::ComputeTightBounds(gfxContext* aContext) const
4185 {
4186 return GetVisualOverflowRect();
4187 }
4188
4189 nsRect
4190 nsFrame::ComputeSimpleTightBounds(gfxContext* aContext) const
4191 {
4192 if (StyleOutline()->GetOutlineStyle() != NS_STYLE_BORDER_STYLE_NONE ||
4193 StyleBorder()->HasBorder() || !StyleBackground()->IsTransparent() ||
4194 StyleDisplay()->mAppearance) {
4195 // Not necessarily tight, due to clipping, negative
4196 // outline-offset, and lots of other issues, but that's OK
4197 return GetVisualOverflowRect();
4198 }
4199
4200 nsRect r(0, 0, 0, 0);
4201 ChildListIterator lists(this);
4202 for (; !lists.IsDone(); lists.Next()) {
4203 nsFrameList::Enumerator childFrames(lists.CurrentList());
4204 for (; !childFrames.AtEnd(); childFrames.Next()) {
4205 nsIFrame* child = childFrames.get();
4206 r.UnionRect(r, child->ComputeTightBounds(aContext) + child->GetPosition());
4207 }
4208 }
4209 return r;
4210 }
4211
4212 /* virtual */ nsresult
4213 nsIFrame::GetPrefWidthTightBounds(nsRenderingContext* aContext,
4214 nscoord* aX,
4215 nscoord* aXMost)
4216 {
4217 return NS_ERROR_NOT_IMPLEMENTED;
4218 }
4219
4220 /* virtual */ nsSize
4221 nsFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
4222 nsSize aCBSize, nscoord aAvailableWidth,
4223 nsSize aMargin, nsSize aBorder, nsSize aPadding,
4224 bool aShrinkWrap)
4225 {
4226 // Use basic shrink-wrapping as a default implementation.
4227 nsSize result(0xdeadbeef, NS_UNCONSTRAINEDSIZE);
4228
4229 // don't bother setting it if the result won't be used
4230 if (StylePosition()->mWidth.GetUnit() == eStyleUnit_Auto) {
4231 nscoord availBased = aAvailableWidth - aMargin.width - aBorder.width -
4232 aPadding.width;
4233 result.width = ShrinkWidthToFit(aRenderingContext, availBased);
4234 }
4235 return result;
4236 }
4237
4238 nscoord
4239 nsFrame::ShrinkWidthToFit(nsRenderingContext *aRenderingContext,
4240 nscoord aWidthInCB)
4241 {
4242 // If we're a container for font size inflation, then shrink
4243 // wrapping inside of us should not apply font size inflation.
4244 AutoMaybeDisableFontInflation an(this);
4245
4246 nscoord result;
4247 nscoord minWidth = GetMinWidth(aRenderingContext);
4248 if (minWidth > aWidthInCB) {
4249 result = minWidth;
4250 } else {
4251 nscoord prefWidth = GetPrefWidth(aRenderingContext);
4252 if (prefWidth > aWidthInCB) {
4253 result = aWidthInCB;
4254 } else {
4255 result = prefWidth;
4256 }
4257 }
4258 return result;
4259 }
4260
4261 nsresult
4262 nsFrame::WillReflow(nsPresContext* aPresContext)
4263 {
4264 #ifdef DEBUG_dbaron_off
4265 // bug 81268
4266 NS_ASSERTION(!(mState & NS_FRAME_IN_REFLOW),
4267 "nsFrame::WillReflow: frame is already in reflow");
4268 #endif
4269
4270 NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS,
4271 ("WillReflow: oldState=%x", mState));
4272 mState |= NS_FRAME_IN_REFLOW;
4273 return NS_OK;
4274 }
4275
4276 nsresult
4277 nsFrame::DidReflow(nsPresContext* aPresContext,
4278 const nsHTMLReflowState* aReflowState,
4279 nsDidReflowStatus aStatus)
4280 {
4281 NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS,
4282 ("nsFrame::DidReflow: aStatus=%d", static_cast<uint32_t>(aStatus)));
4283
4284 nsSVGEffects::InvalidateDirectRenderingObservers(this, nsSVGEffects::INVALIDATE_REFLOW);
4285
4286 if (nsDidReflowStatus::FINISHED == aStatus) {
4287 mState &= ~(NS_FRAME_IN_REFLOW | NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
4288 NS_FRAME_HAS_DIRTY_CHILDREN);
4289 }
4290
4291 // Notify the percent height observer if there is a percent height.
4292 // The observer may be able to initiate another reflow with a computed
4293 // height. This happens in the case where a table cell has no computed
4294 // height but can fabricate one when the cell height is known.
4295 if (aReflowState && aReflowState->mPercentHeightObserver &&
4296 !GetPrevInFlow()) {
4297 const nsStyleCoord &height = aReflowState->mStylePosition->mHeight;
4298 if (height.HasPercent()) {
4299 aReflowState->mPercentHeightObserver->NotifyPercentHeight(*aReflowState);
4300 }
4301 }
4302
4303 return NS_OK;
4304 }
4305
4306 void
4307 nsFrame::FinishReflowWithAbsoluteFrames(nsPresContext* aPresContext,
4308 nsHTMLReflowMetrics& aDesiredSize,
4309 const nsHTMLReflowState& aReflowState,
4310 nsReflowStatus& aStatus,
4311 bool aConstrainHeight)
4312 {
4313 ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus, aConstrainHeight);
4314
4315 FinishAndStoreOverflow(&aDesiredSize);
4316 }
4317
4318 void
4319 nsFrame::ReflowAbsoluteFrames(nsPresContext* aPresContext,
4320 nsHTMLReflowMetrics& aDesiredSize,
4321 const nsHTMLReflowState& aReflowState,
4322 nsReflowStatus& aStatus,
4323 bool aConstrainHeight)
4324 {
4325 if (HasAbsolutelyPositionedChildren()) {
4326 nsAbsoluteContainingBlock* absoluteContainer = GetAbsoluteContainingBlock();
4327
4328 // Let the absolutely positioned container reflow any absolutely positioned
4329 // child frames that need to be reflowed
4330
4331 // The containing block for the abs pos kids is formed by our padding edge.
4332 nsMargin computedBorder =
4333 aReflowState.ComputedPhysicalBorderPadding() - aReflowState.ComputedPhysicalPadding();
4334 nscoord containingBlockWidth =
4335 aDesiredSize.Width() - computedBorder.LeftRight();
4336 nscoord containingBlockHeight =
4337 aDesiredSize.Height() - computedBorder.TopBottom();
4338
4339 nsContainerFrame* container = do_QueryFrame(this);
4340 NS_ASSERTION(container, "Abs-pos children only supported on container frames for now");
4341
4342 nsRect containingBlock(0, 0, containingBlockWidth, containingBlockHeight);
4343 absoluteContainer->Reflow(container, aPresContext, aReflowState, aStatus,
4344 containingBlock,
4345 aConstrainHeight, true, true, // XXX could be optimized
4346 &aDesiredSize.mOverflowAreas);
4347 }
4348 }
4349
4350 /* virtual */ bool
4351 nsFrame::CanContinueTextRun() const
4352 {
4353 // By default, a frame will *not* allow a text run to be continued
4354 // through it.
4355 return false;
4356 }
4357
4358 nsresult
4359 nsFrame::Reflow(nsPresContext* aPresContext,
4360 nsHTMLReflowMetrics& aDesiredSize,
4361 const nsHTMLReflowState& aReflowState,
4362 nsReflowStatus& aStatus)
4363 {
4364 DO_GLOBAL_REFLOW_COUNT("nsFrame");
4365 aDesiredSize.Width() = 0;
4366 aDesiredSize.Height() = 0;
4367 aStatus = NS_FRAME_COMPLETE;
4368 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
4369 return NS_OK;
4370 }
4371
4372 nsresult
4373 nsFrame::CharacterDataChanged(CharacterDataChangeInfo* aInfo)
4374 {
4375 NS_NOTREACHED("should only be called for text frames");
4376 return NS_OK;
4377 }
4378
4379 nsresult
4380 nsFrame::AttributeChanged(int32_t aNameSpaceID,
4381 nsIAtom* aAttribute,
4382 int32_t aModType)
4383 {
4384 return NS_OK;
4385 }
4386
4387 // Flow member functions
4388
4389 nsSplittableType
4390 nsFrame::GetSplittableType() const
4391 {
4392 return NS_FRAME_NOT_SPLITTABLE;
4393 }
4394
4395 nsIFrame* nsFrame::GetPrevContinuation() const
4396 {
4397 return nullptr;
4398 }
4399
4400 void
4401 nsFrame::SetPrevContinuation(nsIFrame* aPrevContinuation)
4402 {
4403 MOZ_ASSERT(false, "not splittable");
4404 }
4405
4406 nsIFrame* nsFrame::GetNextContinuation() const
4407 {
4408 return nullptr;
4409 }
4410
4411 void
4412 nsFrame::SetNextContinuation(nsIFrame*)
4413 {
4414 MOZ_ASSERT(false, "not splittable");
4415 }
4416
4417 nsIFrame* nsFrame::GetPrevInFlowVirtual() const
4418 {
4419 return nullptr;
4420 }
4421
4422 void
4423 nsFrame::SetPrevInFlow(nsIFrame* aPrevInFlow)
4424 {
4425 MOZ_ASSERT(false, "not splittable");
4426 }
4427
4428 nsIFrame* nsFrame::GetNextInFlowVirtual() const
4429 {
4430 return nullptr;
4431 }
4432
4433 void
4434 nsFrame::SetNextInFlow(nsIFrame*)
4435 {
4436 MOZ_ASSERT(false, "not splittable");
4437 }
4438
4439 nsIFrame* nsIFrame::GetTailContinuation()
4440 {
4441 nsIFrame* frame = this;
4442 while (frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
4443 frame = frame->GetPrevContinuation();
4444 NS_ASSERTION(frame, "first continuation can't be overflow container");
4445 }
4446 for (nsIFrame* next = frame->GetNextContinuation();
4447 next && !(next->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER);
4448 next = frame->GetNextContinuation()) {
4449 frame = next;
4450 }
4451 NS_POSTCONDITION(frame, "illegal state in continuation chain.");
4452 return frame;
4453 }
4454
4455 NS_DECLARE_FRAME_PROPERTY(ViewProperty, nullptr)
4456
4457 // Associated view object
4458 nsView*
4459 nsIFrame::GetView() const
4460 {
4461 // Check the frame state bit and see if the frame has a view
4462 if (!(GetStateBits() & NS_FRAME_HAS_VIEW))
4463 return nullptr;
4464
4465 // Check for a property on the frame
4466 void* value = Properties().Get(ViewProperty());
4467 NS_ASSERTION(value, "frame state bit was set but frame has no view");
4468 return static_cast<nsView*>(value);
4469 }
4470
4471 /* virtual */ nsView*
4472 nsIFrame::GetViewExternal() const
4473 {
4474 return GetView();
4475 }
4476
4477 nsresult
4478 nsIFrame::SetView(nsView* aView)
4479 {
4480 if (aView) {
4481 aView->SetFrame(this);
4482
4483 #ifdef DEBUG
4484 nsIAtom* frameType = GetType();
4485 NS_ASSERTION(frameType == nsGkAtoms::scrollFrame ||
4486 frameType == nsGkAtoms::subDocumentFrame ||
4487 frameType == nsGkAtoms::listControlFrame ||
4488 frameType == nsGkAtoms::objectFrame ||
4489 frameType == nsGkAtoms::viewportFrame ||
4490 frameType == nsGkAtoms::menuPopupFrame,
4491 "Only specific frame types can have an nsView");
4492 #endif
4493
4494 // Set a property on the frame
4495 Properties().Set(ViewProperty(), aView);
4496
4497 // Set the frame state bit that says the frame has a view
4498 AddStateBits(NS_FRAME_HAS_VIEW);
4499
4500 // Let all of the ancestors know they have a descendant with a view.
4501 for (nsIFrame* f = GetParent();
4502 f && !(f->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW);
4503 f = f->GetParent())
4504 f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
4505 }
4506
4507 return NS_OK;
4508 }
4509
4510 nsIFrame* nsIFrame::GetAncestorWithViewExternal() const
4511 {
4512 return GetAncestorWithView();
4513 }
4514
4515 // Find the first geometric parent that has a view
4516 nsIFrame* nsIFrame::GetAncestorWithView() const
4517 {
4518 for (nsIFrame* f = mParent; nullptr != f; f = f->GetParent()) {
4519 if (f->HasView()) {
4520 return f;
4521 }
4522 }
4523 return nullptr;
4524 }
4525
4526 // virtual
4527 nsPoint nsIFrame::GetOffsetToExternal(const nsIFrame* aOther) const
4528 {
4529 return GetOffsetTo(aOther);
4530 }
4531
4532 nsPoint nsIFrame::GetOffsetTo(const nsIFrame* aOther) const
4533 {
4534 NS_PRECONDITION(aOther,
4535 "Must have frame for destination coordinate system!");
4536
4537 NS_ASSERTION(PresContext() == aOther->PresContext(),
4538 "GetOffsetTo called on frames in different documents");
4539
4540 nsPoint offset(0, 0);
4541 const nsIFrame* f;
4542 for (f = this; f != aOther && f; f = f->GetParent()) {
4543 offset += f->GetPosition();
4544 }
4545
4546 if (f != aOther) {
4547 // Looks like aOther wasn't an ancestor of |this|. So now we have
4548 // the root-frame-relative position of |this| in |offset|. Convert back
4549 // to the coordinates of aOther
4550 while (aOther) {
4551 offset -= aOther->GetPosition();
4552 aOther = aOther->GetParent();
4553 }
4554 }
4555
4556 return offset;
4557 }
4558
4559 nsPoint nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther) const
4560 {
4561 return GetOffsetToCrossDoc(aOther, PresContext()->AppUnitsPerDevPixel());
4562 }
4563
4564 nsPoint
4565 nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther, const int32_t aAPD) const
4566 {
4567 NS_PRECONDITION(aOther,
4568 "Must have frame for destination coordinate system!");
4569 NS_ASSERTION(PresContext()->GetRootPresContext() ==
4570 aOther->PresContext()->GetRootPresContext(),
4571 "trying to get the offset between frames in different document "
4572 "hierarchies?");
4573 if (PresContext()->GetRootPresContext() !=
4574 aOther->PresContext()->GetRootPresContext()) {
4575 // crash right away, we are almost certainly going to crash anyway.
4576 NS_RUNTIMEABORT("trying to get the offset between frames in different "
4577 "document hierarchies?");
4578 }
4579
4580 const nsIFrame* root = nullptr;
4581 // offset will hold the final offset
4582 // docOffset holds the currently accumulated offset at the current APD, it
4583 // will be converted and added to offset when the current APD changes.
4584 nsPoint offset(0, 0), docOffset(0, 0);
4585 const nsIFrame* f = this;
4586 int32_t currAPD = PresContext()->AppUnitsPerDevPixel();
4587 while (f && f != aOther) {
4588 docOffset += f->GetPosition();
4589 nsIFrame* parent = f->GetParent();
4590 if (parent) {
4591 f = parent;
4592 } else {
4593 nsPoint newOffset(0, 0);
4594 root = f;
4595 f = nsLayoutUtils::GetCrossDocParentFrame(f, &newOffset);
4596 int32_t newAPD = f ? f->PresContext()->AppUnitsPerDevPixel() : 0;
4597 if (!f || newAPD != currAPD) {
4598 // Convert docOffset to the right APD and add it to offset.
4599 offset += docOffset.ConvertAppUnits(currAPD, aAPD);
4600 docOffset.x = docOffset.y = 0;
4601 }
4602 currAPD = newAPD;
4603 docOffset += newOffset;
4604 }
4605 }
4606 if (f == aOther) {
4607 offset += docOffset.ConvertAppUnits(currAPD, aAPD);
4608 } else {
4609 // Looks like aOther wasn't an ancestor of |this|. So now we have
4610 // the root-document-relative position of |this| in |offset|. Subtract the
4611 // root-document-relative position of |aOther| from |offset|.
4612 // This call won't try to recurse again because root is an ancestor of
4613 // aOther.
4614 nsPoint negOffset = aOther->GetOffsetToCrossDoc(root, aAPD);
4615 offset -= negOffset;
4616 }
4617
4618 return offset;
4619 }
4620
4621 // virtual
4622 nsIntRect nsIFrame::GetScreenRectExternal() const
4623 {
4624 return GetScreenRect();
4625 }
4626
4627 nsIntRect nsIFrame::GetScreenRect() const
4628 {
4629 return GetScreenRectInAppUnits().ToNearestPixels(PresContext()->AppUnitsPerCSSPixel());
4630 }
4631
4632 // virtual
4633 nsRect nsIFrame::GetScreenRectInAppUnitsExternal() const
4634 {
4635 return GetScreenRectInAppUnits();
4636 }
4637
4638 nsRect nsIFrame::GetScreenRectInAppUnits() const
4639 {
4640 nsPresContext* presContext = PresContext();
4641 nsIFrame* rootFrame =
4642 presContext->PresShell()->FrameManager()->GetRootFrame();
4643 nsPoint rootScreenPos(0, 0);
4644 nsPoint rootFrameOffsetInParent(0, 0);
4645 nsIFrame* rootFrameParent =
4646 nsLayoutUtils::GetCrossDocParentFrame(rootFrame, &rootFrameOffsetInParent);
4647 if (rootFrameParent) {
4648 nsRect parentScreenRectAppUnits = rootFrameParent->GetScreenRectInAppUnits();
4649 nsPresContext* parentPresContext = rootFrameParent->PresContext();
4650 double parentScale = double(presContext->AppUnitsPerDevPixel())/
4651 parentPresContext->AppUnitsPerDevPixel();
4652 nsPoint rootPt = parentScreenRectAppUnits.TopLeft() + rootFrameOffsetInParent;
4653 rootScreenPos.x = NS_round(parentScale*rootPt.x);
4654 rootScreenPos.y = NS_round(parentScale*rootPt.y);
4655 } else {
4656 nsCOMPtr<nsIWidget> rootWidget;
4657 presContext->PresShell()->GetViewManager()->GetRootWidget(getter_AddRefs(rootWidget));
4658 if (rootWidget) {
4659 nsIntPoint rootDevPx = rootWidget->WidgetToScreenOffset();
4660 rootScreenPos.x = presContext->DevPixelsToAppUnits(rootDevPx.x);
4661 rootScreenPos.y = presContext->DevPixelsToAppUnits(rootDevPx.y);
4662 }
4663 }
4664
4665 return nsRect(rootScreenPos + GetOffsetTo(rootFrame), GetSize());
4666 }
4667
4668 // Returns the offset from this frame to the closest geometric parent that
4669 // has a view. Also returns the containing view or null in case of error
4670 void
4671 nsIFrame::GetOffsetFromView(nsPoint& aOffset, nsView** aView) const
4672 {
4673 NS_PRECONDITION(nullptr != aView, "null OUT parameter pointer");
4674 nsIFrame* frame = const_cast<nsIFrame*>(this);
4675
4676 *aView = nullptr;
4677 aOffset.MoveTo(0, 0);
4678 do {
4679 aOffset += frame->GetPosition();
4680 frame = frame->GetParent();
4681 } while (frame && !frame->HasView());
4682
4683 if (frame) {
4684 *aView = frame->GetView();
4685 }
4686 }
4687
4688 nsIWidget*
4689 nsIFrame::GetNearestWidget() const
4690 {
4691 return GetClosestView()->GetNearestWidget(nullptr);
4692 }
4693
4694 nsIWidget*
4695 nsIFrame::GetNearestWidget(nsPoint& aOffset) const
4696 {
4697 nsPoint offsetToView;
4698 nsPoint offsetToWidget;
4699 nsIWidget* widget =
4700 GetClosestView(&offsetToView)->GetNearestWidget(&offsetToWidget);
4701 aOffset = offsetToView + offsetToWidget;
4702 return widget;
4703 }
4704
4705 nsIAtom*
4706 nsFrame::GetType() const
4707 {
4708 return nullptr;
4709 }
4710
4711 bool
4712 nsIFrame::IsLeaf() const
4713 {
4714 return true;
4715 }
4716
4717 gfx3DMatrix
4718 nsIFrame::GetTransformMatrix(const nsIFrame* aStopAtAncestor,
4719 nsIFrame** aOutAncestor)
4720 {
4721 NS_PRECONDITION(aOutAncestor, "Need a place to put the ancestor!");
4722
4723 /* If we're transformed, we want to hand back the combination
4724 * transform/translate matrix that will apply our current transform, then
4725 * shift us to our parent.
4726 */
4727 if (IsTransformed()) {
4728 /* Compute the delta to the parent, which we need because we are converting
4729 * coordinates to our parent.
4730 */
4731 NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrame(this),
4732 "Cannot transform the viewport frame!");
4733 int32_t scaleFactor = PresContext()->AppUnitsPerDevPixel();
4734
4735 gfx3DMatrix result =
4736 nsDisplayTransform::GetResultingTransformMatrix(this, nsPoint(0, 0), scaleFactor, nullptr, aOutAncestor);
4737 // XXXjwatt: seems like this will double count offsets in the face of preserve-3d:
4738 nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
4739 /* Combine the raw transform with a translation to our parent. */
4740 result *= gfx3DMatrix::Translation
4741 (NSAppUnitsToFloatPixels(delta.x, scaleFactor),
4742 NSAppUnitsToFloatPixels(delta.y, scaleFactor),
4743 0.0f);
4744 return result;
4745 }
4746
4747 if (nsLayoutUtils::IsPopup(this) &&
4748 GetType() == nsGkAtoms::listControlFrame) {
4749 nsPresContext* presContext = PresContext();
4750 nsIFrame* docRootFrame = presContext->PresShell()->GetRootFrame();
4751
4752 // Compute a matrix that transforms from the popup widget to the toplevel
4753 // widget. We use the widgets because they're the simplest and most
4754 // accurate approach --- this should work no matter how the widget position
4755 // was chosen.
4756 nsIWidget* widget = GetView()->GetWidget();
4757 nsPresContext* rootPresContext = PresContext()->GetRootPresContext();
4758 // Maybe the widget hasn't been created yet? Popups without widgets are
4759 // treated as regular frames. That should work since they'll be rendered
4760 // as part of the page if they're rendered at all.
4761 if (widget && rootPresContext) {
4762 nsIWidget* toplevel = rootPresContext->GetNearestWidget();
4763 if (toplevel) {
4764 nsIntRect screenBounds;
4765 widget->GetClientBounds(screenBounds);
4766 nsIntRect toplevelScreenBounds;
4767 toplevel->GetClientBounds(toplevelScreenBounds);
4768 nsIntPoint translation = screenBounds.TopLeft() - toplevelScreenBounds.TopLeft();
4769
4770 gfx3DMatrix transformToTop;
4771 transformToTop._41 = translation.x;
4772 transformToTop._42 = translation.y;
4773
4774 *aOutAncestor = docRootFrame;
4775 gfx3DMatrix docRootTransformToTop =
4776 nsLayoutUtils::GetTransformToAncestor(docRootFrame, nullptr);
4777 if (docRootTransformToTop.IsSingular()) {
4778 NS_WARNING("Containing document is invisible, we can't compute a valid transform");
4779 } else {
4780 gfx3DMatrix topToDocRootTransform = docRootTransformToTop.Inverse();
4781 return transformToTop*topToDocRootTransform;
4782 }
4783 }
4784 }
4785 }
4786
4787 *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(this);
4788
4789 /* Otherwise, we're not transformed. In that case, we'll walk up the frame
4790 * tree until we either hit the root frame or something that may be
4791 * transformed. We'll then change coordinates into that frame, since we're
4792 * guaranteed that nothing in-between can be transformed. First, however,
4793 * we have to check to see if we have a parent. If not, we'll set the
4794 * outparam to null (indicating that there's nothing left) and will hand back
4795 * the identity matrix.
4796 */
4797 if (!*aOutAncestor)
4798 return gfx3DMatrix();
4799
4800 /* Keep iterating while the frame can't possibly be transformed. */
4801 while (!(*aOutAncestor)->IsTransformed() &&
4802 !nsLayoutUtils::IsPopup(*aOutAncestor) &&
4803 *aOutAncestor != aStopAtAncestor) {
4804 /* If no parent, stop iterating. Otherwise, update the ancestor. */
4805 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(*aOutAncestor);
4806 if (!parent)
4807 break;
4808
4809 *aOutAncestor = parent;
4810 }
4811
4812 NS_ASSERTION(*aOutAncestor, "Somehow ended up with a null ancestor...?");
4813
4814 /* Translate from this frame to our ancestor, if it exists. That's the
4815 * entire transform, so we're done.
4816 */
4817 nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
4818 int32_t scaleFactor = PresContext()->AppUnitsPerDevPixel();
4819 return gfx3DMatrix().Translation
4820 (NSAppUnitsToFloatPixels(delta.x, scaleFactor),
4821 NSAppUnitsToFloatPixels(delta.y, scaleFactor),
4822 0.0f);
4823 }
4824
4825 static void InvalidateFrameInternal(nsIFrame *aFrame, bool aHasDisplayItem = true)
4826 {
4827 if (aHasDisplayItem) {
4828 aFrame->AddStateBits(NS_FRAME_NEEDS_PAINT);
4829 }
4830 nsSVGEffects::InvalidateDirectRenderingObservers(aFrame);
4831 bool needsSchedulePaint = false;
4832 if (nsLayoutUtils::IsPopup(aFrame)) {
4833 needsSchedulePaint = true;
4834 } else {
4835 nsIFrame *parent = nsLayoutUtils::GetCrossDocParentFrame(aFrame);
4836 while (parent && !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
4837 if (aHasDisplayItem) {
4838 parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
4839 }
4840 nsSVGEffects::InvalidateDirectRenderingObservers(parent);
4841
4842 // If we're inside a popup, then we need to make sure that we
4843 // call schedule paint so that the NS_FRAME_UPDATE_LAYER_TREE
4844 // flag gets added to the popup display root frame.
4845 if (nsLayoutUtils::IsPopup(parent)) {
4846 needsSchedulePaint = true;
4847 break;
4848 }
4849 parent = nsLayoutUtils::GetCrossDocParentFrame(parent);
4850 }
4851 if (!parent) {
4852 needsSchedulePaint = true;
4853 }
4854 }
4855 if (!aHasDisplayItem) {
4856 return;
4857 }
4858 if (needsSchedulePaint) {
4859 aFrame->SchedulePaint();
4860 }
4861 if (aFrame->HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
4862 aFrame->Properties().Delete(nsIFrame::InvalidationRect());
4863 aFrame->RemoveStateBits(NS_FRAME_HAS_INVALID_RECT);
4864 }
4865 }
4866
4867 void
4868 nsIFrame::InvalidateFrameSubtree(uint32_t aDisplayItemKey)
4869 {
4870 bool hasDisplayItem =
4871 !aDisplayItemKey || FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey);
4872 InvalidateFrame(aDisplayItemKey);
4873
4874 if (HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT) || !hasDisplayItem) {
4875 return;
4876 }
4877
4878 AddStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT);
4879
4880 nsAutoTArray<nsIFrame::ChildList,4> childListArray;
4881 GetCrossDocChildLists(&childListArray);
4882
4883 nsIFrame::ChildListArrayIterator lists(childListArray);
4884 for (; !lists.IsDone(); lists.Next()) {
4885 nsFrameList::Enumerator childFrames(lists.CurrentList());
4886 for (; !childFrames.AtEnd(); childFrames.Next()) {
4887 childFrames.get()->InvalidateFrameSubtree();
4888 }
4889 }
4890 }
4891
4892 void
4893 nsIFrame::ClearInvalidationStateBits()
4894 {
4895 if (HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
4896 nsAutoTArray<nsIFrame::ChildList,4> childListArray;
4897 GetCrossDocChildLists(&childListArray);
4898
4899 nsIFrame::ChildListArrayIterator lists(childListArray);
4900 for (; !lists.IsDone(); lists.Next()) {
4901 nsFrameList::Enumerator childFrames(lists.CurrentList());
4902 for (; !childFrames.AtEnd(); childFrames.Next()) {
4903 childFrames.get()->ClearInvalidationStateBits();
4904 }
4905 }
4906 }
4907
4908 RemoveStateBits(NS_FRAME_NEEDS_PAINT |
4909 NS_FRAME_DESCENDANT_NEEDS_PAINT |
4910 NS_FRAME_ALL_DESCENDANTS_NEED_PAINT);
4911 }
4912
4913 void
4914 nsIFrame::InvalidateFrame(uint32_t aDisplayItemKey)
4915 {
4916 bool hasDisplayItem =
4917 !aDisplayItemKey || FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey);
4918 InvalidateFrameInternal(this, hasDisplayItem);
4919 }
4920
4921 void
4922 nsIFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey)
4923 {
4924 bool hasDisplayItem =
4925 !aDisplayItemKey || FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey);
4926 bool alreadyInvalid = false;
4927 if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) {
4928 InvalidateFrameInternal(this, hasDisplayItem);
4929 } else {
4930 alreadyInvalid = true;
4931 }
4932
4933 if (!hasDisplayItem) {
4934 return;
4935 }
4936
4937 nsRect *rect = static_cast<nsRect*>(Properties().Get(InvalidationRect()));
4938 if (!rect) {
4939 if (alreadyInvalid) {
4940 return;
4941 }
4942 rect = new nsRect();
4943 Properties().Set(InvalidationRect(), rect);
4944 AddStateBits(NS_FRAME_HAS_INVALID_RECT);
4945 }
4946
4947 *rect = rect->Union(aRect);
4948 }
4949
4950 /*static*/ uint8_t nsIFrame::sLayerIsPrerenderedDataKey;
4951
4952 bool
4953 nsIFrame::TryUpdateTransformOnly(Layer** aLayerResult)
4954 {
4955 Layer* layer = FrameLayerBuilder::GetDedicatedLayer(
4956 this, nsDisplayItem::TYPE_TRANSFORM);
4957 if (!layer || !layer->HasUserData(LayerIsPrerenderedDataKey())) {
4958 // If this layer isn't prerendered or we clip composites to our OS
4959 // window, then we can't correctly optimize to an empty
4960 // transaction in general.
4961 return false;
4962 }
4963
4964 gfx3DMatrix transform3d;
4965 if (!nsLayoutUtils::GetLayerTransformForFrame(this, &transform3d)) {
4966 // We're not able to compute a layer transform that we know would
4967 // be used at the next layers transaction, so we can't only update
4968 // the transform and will need to schedule an invalidating paint.
4969 return false;
4970 }
4971 gfxMatrix transform;
4972 gfx::Matrix previousTransform;
4973 // FIXME/bug 796690 and 796705: in general, changes to 3D
4974 // transforms, or transform changes to properties other than
4975 // translation, may lead us to choose a different rendering
4976 // resolution for our layer. So if the transform is 3D or has a
4977 // non-translation change, bail and schedule an invalidating paint.
4978 // (We can often do better than this, for example for scale-down
4979 // changes.)
4980 static const gfx::Float kError = 0.0001f;
4981 if (!transform3d.Is2D(&transform) ||
4982 !layer->GetBaseTransform().Is2D(&previousTransform) ||
4983 !gfx::FuzzyEqual(transform.xx, previousTransform._11, kError) ||
4984 !gfx::FuzzyEqual(transform.yy, previousTransform._22, kError) ||
4985 !gfx::FuzzyEqual(transform.xy, previousTransform._21, kError) ||
4986 !gfx::FuzzyEqual(transform.yx, previousTransform._12, kError)) {
4987 return false;
4988 }
4989 gfx::Matrix4x4 matrix;
4990 gfx::ToMatrix4x4(transform3d, matrix);
4991 layer->SetBaseTransformForNextTransaction(matrix);
4992 *aLayerResult = layer;
4993 return true;
4994 }
4995
4996 bool
4997 nsIFrame::IsInvalid(nsRect& aRect)
4998 {
4999 if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) {
5000 return false;
5001 }
5002
5003 if (HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
5004 nsRect *rect = static_cast<nsRect*>(Properties().Get(InvalidationRect()));
5005 NS_ASSERTION(rect, "Must have an invalid rect if NS_FRAME_HAS_INVALID_RECT is set!");
5006 aRect = *rect;
5007 } else {
5008 aRect.SetEmpty();
5009 }
5010 return true;
5011 }
5012
5013 void
5014 nsIFrame::SchedulePaint(PaintType aType)
5015 {
5016 nsIFrame *displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
5017 nsPresContext *pres = displayRoot->PresContext()->GetRootPresContext();
5018
5019 // No need to schedule a paint for an external document since they aren't
5020 // painted directly.
5021 if (!pres || (pres->Document() && pres->Document()->IsResourceDoc())) {
5022 return;
5023 }
5024 if (!pres->GetContainerWeak()) {
5025 NS_WARNING("Shouldn't call SchedulePaint in a detached pres context");
5026 return;
5027 }
5028
5029 pres->PresShell()->ScheduleViewManagerFlush(aType == PAINT_DELAYED_COMPRESS ?
5030 nsIPresShell::PAINT_DELAYED_COMPRESS :
5031 nsIPresShell::PAINT_DEFAULT);
5032
5033 if (aType == PAINT_DELAYED_COMPRESS) {
5034 return;
5035 }
5036
5037 if (aType == PAINT_DEFAULT) {
5038 displayRoot->AddStateBits(NS_FRAME_UPDATE_LAYER_TREE);
5039 }
5040 nsIPresShell* shell = PresContext()->PresShell();
5041 if (shell) {
5042 shell->AddInvalidateHiddenPresShellObserver(pres->RefreshDriver());
5043 }
5044 }
5045
5046 Layer*
5047 nsIFrame::InvalidateLayer(uint32_t aDisplayItemKey,
5048 const nsIntRect* aDamageRect,
5049 uint32_t aFlags /* = 0 */)
5050 {
5051 NS_ASSERTION(aDisplayItemKey > 0, "Need a key");
5052
5053 Layer* layer = FrameLayerBuilder::GetDedicatedLayer(this, aDisplayItemKey);
5054
5055 // If the layer is being updated asynchronously, and it's being forwarded
5056 // to a compositor, then we don't need to invalidate.
5057 if ((aFlags & UPDATE_IS_ASYNC) && layer &&
5058 layer->Manager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) {
5059 return layer;
5060 }
5061
5062 if (aDamageRect && aDamageRect->IsEmpty()) {
5063 return layer;
5064 }
5065
5066 if (!layer) {
5067 // Plugins can transition from not rendering anything to rendering,
5068 // and still only call this. So always invalidate, with specifying
5069 // the display item type just in case.
5070 //
5071 // In the bug 930056, dialer app startup but not shown on the
5072 // screen because sometimes we don't have any retainned data
5073 // for remote type displayitem and thus Repaint event is not
5074 // triggered. So, always invalidate here as well.
5075 if (aDisplayItemKey == nsDisplayItem::TYPE_PLUGIN ||
5076 aDisplayItemKey == nsDisplayItem::TYPE_REMOTE) {
5077 InvalidateFrame();
5078 } else {
5079 InvalidateFrame(aDisplayItemKey);
5080 }
5081 return nullptr;
5082 }
5083
5084 if (aDamageRect) {
5085 layer->AddInvalidRect(*aDamageRect);
5086 } else {
5087 layer->SetInvalidRectToVisibleRegion();
5088 }
5089
5090 SchedulePaint(PAINT_COMPOSITE_ONLY);
5091 return layer;
5092 }
5093
5094 static nsRect
5095 ComputeEffectsRect(nsIFrame* aFrame, const nsRect& aOverflowRect,
5096 const nsSize& aNewSize)
5097 {
5098 nsRect r = aOverflowRect;
5099
5100 if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
5101 // For SVG frames, we only need to account for filters.
5102 // TODO: We could also take account of clipPath and mask to reduce the
5103 // visual overflow, but that's not essential.
5104 if (aFrame->StyleSVGReset()->HasFilters()) {
5105 aFrame->Properties().
5106 Set(nsIFrame::PreEffectsBBoxProperty(), new nsRect(r));
5107 r = nsSVGUtils::GetPostFilterVisualOverflowRect(aFrame, aOverflowRect);
5108 }
5109 return r;
5110 }
5111
5112 // box-shadow
5113 r.UnionRect(r, nsLayoutUtils::GetBoxShadowRectForFrame(aFrame, aNewSize));
5114
5115 // border-image-outset.
5116 // We need to include border-image-outset because it can cause the
5117 // border image to be drawn beyond the border box.
5118
5119 // (1) It's important we not check whether there's a border-image
5120 // since the style hint for a change in border image doesn't cause
5121 // reflow, and that's probably more important than optimizing the
5122 // overflow areas for the silly case of border-image-outset without
5123 // border-image
5124 // (2) It's important that we not check whether the border-image
5125 // is actually loaded, since that would require us to reflow when
5126 // the image loads.
5127 const nsStyleBorder* styleBorder = aFrame->StyleBorder();
5128 nsMargin outsetMargin = styleBorder->GetImageOutset();
5129
5130 if (outsetMargin != nsMargin(0, 0, 0, 0)) {
5131 nsRect outsetRect(nsPoint(0, 0), aNewSize);
5132 outsetRect.Inflate(outsetMargin);
5133 r.UnionRect(r, outsetRect);
5134 }
5135
5136 // Note that we don't remove the outlineInnerRect if a frame loses outline
5137 // style. That would require an extra property lookup for every frame,
5138 // or a new frame state bit to track whether a property had been stored,
5139 // or something like that. It's not worth doing that here. At most it's
5140 // only one heap-allocated rect per frame and it will be cleaned up when
5141 // the frame dies.
5142
5143 if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) {
5144 aFrame->Properties().
5145 Set(nsIFrame::PreEffectsBBoxProperty(), new nsRect(r));
5146 r = nsSVGIntegrationUtils::ComputePostEffectsVisualOverflowRect(aFrame, r);
5147 }
5148
5149 return r;
5150 }
5151
5152 void
5153 nsIFrame::MovePositionBy(const nsPoint& aTranslation)
5154 {
5155 nsPoint position = GetNormalPosition() + aTranslation;
5156
5157 const nsMargin* computedOffsets = nullptr;
5158 if (IsRelativelyPositioned()) {
5159 computedOffsets = static_cast<nsMargin*>
5160 (Properties().Get(nsIFrame::ComputedOffsetProperty()));
5161 }
5162 nsHTMLReflowState::ApplyRelativePositioning(this, computedOffsets ?
5163 *computedOffsets : nsMargin(),
5164 &position);
5165 NS_ASSERTION(StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY ||
5166 GetPosition() + aTranslation == position,
5167 "MovePositionBy should always lead to the movement "
5168 "specified, unless the frame is position:sticky");
5169 SetPosition(position);
5170 }
5171
5172 nsPoint
5173 nsIFrame::GetNormalPosition() const
5174 {
5175 // It might be faster to first check
5176 // StyleDisplay()->IsRelativelyPositionedStyle().
5177 nsPoint* normalPosition = static_cast<nsPoint*>
5178 (Properties().Get(NormalPositionProperty()));
5179 if (normalPosition) {
5180 return *normalPosition;
5181 }
5182 return GetPosition();
5183 }
5184
5185 nsRect
5186 nsIFrame::GetOverflowRect(nsOverflowType aType) const
5187 {
5188 NS_ABORT_IF_FALSE(aType == eVisualOverflow || aType == eScrollableOverflow,
5189 "unexpected type");
5190
5191 // Note that in some cases the overflow area might not have been
5192 // updated (yet) to reflect any outline set on the frame or the area
5193 // of child frames. That's OK because any reflow that updates these
5194 // areas will invalidate the appropriate area, so any (mis)uses of
5195 // this method will be fixed up.
5196
5197 if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
5198 // there is an overflow rect, and it's not stored as deltas but as
5199 // a separately-allocated rect
5200 return static_cast<nsOverflowAreas*>(const_cast<nsIFrame*>(this)->
5201 GetOverflowAreasProperty())->Overflow(aType);
5202 }
5203
5204 if (aType == eVisualOverflow &&
5205 mOverflow.mType != NS_FRAME_OVERFLOW_NONE) {
5206 return GetVisualOverflowFromDeltas();
5207 }
5208
5209 return nsRect(nsPoint(0, 0), GetSize());
5210 }
5211
5212 nsOverflowAreas
5213 nsIFrame::GetOverflowAreas() const
5214 {
5215 if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
5216 // there is an overflow rect, and it's not stored as deltas but as
5217 // a separately-allocated rect
5218 return *const_cast<nsIFrame*>(this)->GetOverflowAreasProperty();
5219 }
5220
5221 return nsOverflowAreas(GetVisualOverflowFromDeltas(),
5222 nsRect(nsPoint(0, 0), GetSize()));
5223 }
5224
5225 nsOverflowAreas
5226 nsIFrame::GetOverflowAreasRelativeToSelf() const
5227 {
5228 if (IsTransformed()) {
5229 nsOverflowAreas* preTransformOverflows = static_cast<nsOverflowAreas*>
5230 (Properties().Get(PreTransformOverflowAreasProperty()));
5231 if (preTransformOverflows) {
5232 return nsOverflowAreas(preTransformOverflows->VisualOverflow(),
5233 preTransformOverflows->ScrollableOverflow());
5234 }
5235 }
5236 return nsOverflowAreas(GetVisualOverflowRect(),
5237 GetScrollableOverflowRect());
5238 }
5239
5240 nsRect
5241 nsIFrame::GetScrollableOverflowRectRelativeToParent() const
5242 {
5243 return GetScrollableOverflowRect() + mRect.TopLeft();
5244 }
5245
5246 nsRect
5247 nsIFrame::GetScrollableOverflowRectRelativeToSelf() const
5248 {
5249 if (IsTransformed()) {
5250 nsOverflowAreas* preTransformOverflows = static_cast<nsOverflowAreas*>
5251 (Properties().Get(PreTransformOverflowAreasProperty()));
5252 if (preTransformOverflows)
5253 return preTransformOverflows->ScrollableOverflow();
5254 }
5255 return GetScrollableOverflowRect();
5256 }
5257
5258 nsRect
5259 nsIFrame::GetVisualOverflowRectRelativeToSelf() const
5260 {
5261 if (IsTransformed()) {
5262 nsOverflowAreas* preTransformOverflows = static_cast<nsOverflowAreas*>
5263 (Properties().Get(PreTransformOverflowAreasProperty()));
5264 if (preTransformOverflows)
5265 return preTransformOverflows->VisualOverflow();
5266 }
5267 return GetVisualOverflowRect();
5268 }
5269
5270 nsRect
5271 nsIFrame::GetPreEffectsVisualOverflowRect() const
5272 {
5273 nsRect* r = static_cast<nsRect*>
5274 (Properties().Get(nsIFrame::PreEffectsBBoxProperty()));
5275 return r ? *r : GetVisualOverflowRectRelativeToSelf();
5276 }
5277
5278 inline static bool
5279 FrameMaintainsOverflow(nsIFrame* aFrame)
5280 {
5281 return (aFrame->GetStateBits() &
5282 (NS_FRAME_SVG_LAYOUT | NS_FRAME_IS_NONDISPLAY)) !=
5283 (NS_FRAME_SVG_LAYOUT | NS_FRAME_IS_NONDISPLAY);
5284 }
5285
5286 /* virtual */ bool
5287 nsFrame::UpdateOverflow()
5288 {
5289 MOZ_ASSERT(FrameMaintainsOverflow(this),
5290 "Non-display SVG do not maintain visual overflow rects");
5291
5292 nsRect rect(nsPoint(0, 0), GetSize());
5293 nsOverflowAreas overflowAreas(rect, rect);
5294
5295 if (!DoesClipChildren() &&
5296 !(IsCollapsed() && (IsBoxFrame() || IsBoxWrapped()))) {
5297 nsLayoutUtils::UnionChildOverflow(this, overflowAreas);
5298 }
5299
5300 if (FinishAndStoreOverflow(overflowAreas, GetSize())) {
5301 nsView* view = GetView();
5302 if (view) {
5303 uint32_t flags = 0;
5304 GetLayoutFlags(flags);
5305
5306 if ((flags & NS_FRAME_NO_SIZE_VIEW) == 0) {
5307 // Make sure the frame's view is properly sized.
5308 nsViewManager* vm = view->GetViewManager();
5309 vm->ResizeView(view, overflowAreas.VisualOverflow(), true);
5310 }
5311 }
5312
5313 return true;
5314 }
5315
5316 return false;
5317 }
5318
5319 // Define the MAX_FRAME_DEPTH to be the ContentSink's MAX_REFLOW_DEPTH plus
5320 // 4 for the frames above the document's frames:
5321 // the Viewport, GFXScroll, ScrollPort, and Canvas
5322 #define MAX_FRAME_DEPTH (MAX_REFLOW_DEPTH+4)
5323
5324 bool
5325 nsFrame::IsFrameTreeTooDeep(const nsHTMLReflowState& aReflowState,
5326 nsHTMLReflowMetrics& aMetrics,
5327 nsReflowStatus& aStatus)
5328 {
5329 if (aReflowState.mReflowDepth > MAX_FRAME_DEPTH) {
5330 NS_WARNING("frame tree too deep; setting zero size and returning");
5331 mState |= NS_FRAME_TOO_DEEP_IN_FRAME_TREE;
5332 ClearOverflowRects();
5333 aMetrics.Width() = 0;
5334 aMetrics.Height() = 0;
5335 aMetrics.SetTopAscent(0);
5336 aMetrics.mCarriedOutBottomMargin.Zero();
5337 aMetrics.mOverflowAreas.Clear();
5338
5339 if (GetNextInFlow()) {
5340 // Reflow depth might vary between reflows, so we might have
5341 // successfully reflowed and split this frame before. If so, we
5342 // shouldn't delete its continuations.
5343 aStatus = NS_FRAME_NOT_COMPLETE;
5344 } else {
5345 aStatus = NS_FRAME_COMPLETE;
5346 }
5347
5348 return true;
5349 }
5350 mState &= ~NS_FRAME_TOO_DEEP_IN_FRAME_TREE;
5351 return false;
5352 }
5353
5354 bool
5355 nsIFrame::IsBlockWrapper() const
5356 {
5357 nsIAtom *pseudoType = StyleContext()->GetPseudo();
5358 return (pseudoType == nsCSSAnonBoxes::mozAnonymousBlock ||
5359 pseudoType == nsCSSAnonBoxes::mozAnonymousPositionedBlock ||
5360 pseudoType == nsCSSAnonBoxes::buttonContent ||
5361 pseudoType == nsCSSAnonBoxes::cellContent);
5362 }
5363
5364 static nsIFrame*
5365 GetNearestBlockContainer(nsIFrame* frame)
5366 {
5367 // The block wrappers we use to wrap blocks inside inlines aren't
5368 // described in the CSS spec. We need to make them not be containing
5369 // blocks.
5370 // Since the parent of such a block is either a normal block or
5371 // another such pseudo, this shouldn't cause anything bad to happen.
5372 // Also the anonymous blocks inside table cells are not containing blocks.
5373 while (frame->IsFrameOfType(nsIFrame::eLineParticipant) ||
5374 frame->IsBlockWrapper() ||
5375 // Table rows are not containing blocks either
5376 frame->GetType() == nsGkAtoms::tableRowFrame) {
5377 frame = frame->GetParent();
5378 NS_ASSERTION(frame, "How come we got to the root frame without seeing a containing block?");
5379 }
5380 return frame;
5381 }
5382
5383 nsIFrame*
5384 nsIFrame::GetContainingBlock() const
5385 {
5386 // MathML frames might have absolute positioning style, but they would
5387 // still be in-flow. So we have to check to make sure that the frame
5388 // is really out-of-flow too.
5389 if (IsAbsolutelyPositioned() &&
5390 (GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
5391 return GetParent(); // the parent is always the containing block
5392 }
5393 return GetNearestBlockContainer(GetParent());
5394 }
5395
5396 #ifdef DEBUG_FRAME_DUMP
5397
5398 int32_t nsFrame::ContentIndexInContainer(const nsIFrame* aFrame)
5399 {
5400 int32_t result = -1;
5401
5402 nsIContent* content = aFrame->GetContent();
5403 if (content) {
5404 nsIContent* parentContent = content->GetParent();
5405 if (parentContent) {
5406 result = parentContent->IndexOf(content);
5407 }
5408 }
5409
5410 return result;
5411 }
5412
5413 /**
5414 * List a frame tree to stderr. Meant to be called from gdb.
5415 */
5416 void
5417 DebugListFrameTree(nsIFrame* aFrame)
5418 {
5419 ((nsFrame*)aFrame)->List(stderr);
5420 }
5421
5422 void
5423 nsIFrame::ListTag(nsACString& aTo) const
5424 {
5425 ListTag(aTo, this);
5426 }
5427
5428 /* static */
5429 void
5430 nsIFrame::ListTag(nsACString& aTo, const nsIFrame* aFrame) {
5431 nsAutoString tmp;
5432 aFrame->GetFrameName(tmp);
5433 aTo += NS_ConvertUTF16toUTF8(tmp).get();
5434 aTo += nsPrintfCString("@%p", static_cast<const void*>(aFrame));
5435 }
5436
5437 // Debugging
5438 void
5439 nsIFrame::ListGeneric(nsACString& aTo, const char* aPrefix, uint32_t aFlags) const
5440 {
5441 aTo =+ aPrefix;
5442 ListTag(aTo);
5443 if (HasView()) {
5444 aTo += nsPrintfCString(" [view=%p]", static_cast<void*>(GetView()));
5445 }
5446 if (GetNextSibling()) {
5447 aTo += nsPrintfCString(" next=%p", static_cast<void*>(GetNextSibling()));
5448 }
5449 if (GetPrevContinuation()) {
5450 bool fluid = GetPrevInFlow() == GetPrevContinuation();
5451 aTo += nsPrintfCString(" prev-%s=%p", fluid?"in-flow":"continuation",
5452 static_cast<void*>(GetPrevContinuation()));
5453 }
5454 if (GetNextContinuation()) {
5455 bool fluid = GetNextInFlow() == GetNextContinuation();
5456 aTo += nsPrintfCString(" next-%s=%p", fluid?"in-flow":"continuation",
5457 static_cast<void*>(GetNextContinuation()));
5458 }
5459 void* IBsibling = Properties().Get(IBSplitSibling());
5460 if (IBsibling) {
5461 aTo += nsPrintfCString(" IBSplitSibling=%p", IBsibling);
5462 }
5463 void* IBprevsibling = Properties().Get(IBSplitPrevSibling());
5464 if (IBprevsibling) {
5465 aTo += nsPrintfCString(" IBSplitPrevSibling=%p", IBprevsibling);
5466 }
5467 aTo += nsPrintfCString(" {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height);
5468 nsIFrame* f = const_cast<nsIFrame*>(this);
5469 if (f->HasOverflowAreas()) {
5470 nsRect vo = f->GetVisualOverflowRect();
5471 if (!vo.IsEqualEdges(mRect)) {
5472 aTo += nsPrintfCString(" vis-overflow=%d,%d,%d,%d", vo.x, vo.y, vo.width, vo.height);
5473 }
5474 nsRect so = f->GetScrollableOverflowRect();
5475 if (!so.IsEqualEdges(mRect)) {
5476 aTo += nsPrintfCString(" scr-overflow=%d,%d,%d,%d", so.x, so.y, so.width, so.height);
5477 }
5478 }
5479 if (0 != mState) {
5480 aTo += nsPrintfCString(" [state=%016llx]", (unsigned long long)mState);
5481 }
5482 if (IsTransformed()) {
5483 aTo += nsPrintfCString(" transformed");
5484 }
5485 if (ChildrenHavePerspective()) {
5486 aTo += nsPrintfCString(" perspective");
5487 }
5488 if (Preserves3DChildren()) {
5489 aTo += nsPrintfCString(" preserves-3d-children");
5490 }
5491 if (Preserves3D()) {
5492 aTo += nsPrintfCString(" preserves-3d");
5493 }
5494 if (mContent) {
5495 aTo += nsPrintfCString(" [content=%p]", static_cast<void*>(mContent));
5496 }
5497 aTo += nsPrintfCString(" [sc=%p", static_cast<void*>(mStyleContext));
5498 if (mStyleContext) {
5499 nsIAtom* pseudoTag = mStyleContext->GetPseudo();
5500 if (pseudoTag) {
5501 nsAutoString atomString;
5502 pseudoTag->ToString(atomString);
5503 aTo += nsPrintfCString("%s", NS_LossyConvertUTF16toASCII(atomString).get());
5504 }
5505 if (mParent && mStyleContext->GetParent() != mParent->StyleContext()) {
5506 aTo += nsPrintfCString(",parent=%p", mStyleContext->GetParent());
5507 }
5508 }
5509 aTo += "]";
5510 }
5511
5512 void
5513 nsIFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
5514 {
5515 nsCString str;
5516 ListGeneric(str, aPrefix, aFlags);
5517 fprintf_stderr(out, "%s\n", str.get());
5518 }
5519
5520 nsresult
5521 nsFrame::GetFrameName(nsAString& aResult) const
5522 {
5523 return MakeFrameName(NS_LITERAL_STRING("Frame"), aResult);
5524 }
5525
5526 nsresult
5527 nsFrame::MakeFrameName(const nsAString& aType, nsAString& aResult) const
5528 {
5529 aResult = aType;
5530 if (mContent && !mContent->IsNodeOfType(nsINode::eTEXT)) {
5531 nsAutoString buf;
5532 mContent->Tag()->ToString(buf);
5533 if (GetType() == nsGkAtoms::subDocumentFrame) {
5534 nsAutoString src;
5535 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
5536 buf.Append(NS_LITERAL_STRING(" src=") + src);
5537 }
5538 aResult.Append(NS_LITERAL_STRING("(") + buf + NS_LITERAL_STRING(")"));
5539 }
5540 char buf[40];
5541 PR_snprintf(buf, sizeof(buf), "(%d)", ContentIndexInContainer(this));
5542 AppendASCIItoUTF16(buf, aResult);
5543 return NS_OK;
5544 }
5545
5546 void
5547 nsIFrame::DumpFrameTree()
5548 {
5549 RootFrameList(PresContext(), stderr);
5550 }
5551
5552 void
5553 nsIFrame::DumpFrameTreeLimited()
5554 {
5555 List(stderr);
5556 }
5557
5558 void
5559 nsIFrame::RootFrameList(nsPresContext* aPresContext, FILE* out, const char* aPrefix)
5560 {
5561 if (!aPresContext || !out)
5562 return;
5563
5564 nsIPresShell *shell = aPresContext->GetPresShell();
5565 if (shell) {
5566 nsIFrame* frame = shell->FrameManager()->GetRootFrame();
5567 if(frame) {
5568 frame->List(out, aPrefix);
5569 }
5570 }
5571 }
5572 #endif
5573
5574 #ifdef DEBUG
5575 nsFrameState
5576 nsFrame::GetDebugStateBits() const
5577 {
5578 // We'll ignore these flags for the purposes of comparing frame state:
5579 //
5580 // NS_FRAME_EXTERNAL_REFERENCE
5581 // because this is set by the event state manager or the
5582 // caret code when a frame is focused. Depending on whether
5583 // or not the regression tests are run as the focused window
5584 // will make this value vary randomly.
5585 #define IRRELEVANT_FRAME_STATE_FLAGS NS_FRAME_EXTERNAL_REFERENCE
5586
5587 #define FRAME_STATE_MASK (~(IRRELEVANT_FRAME_STATE_FLAGS))
5588
5589 return GetStateBits() & FRAME_STATE_MASK;
5590 }
5591
5592 void
5593 nsFrame::XMLQuote(nsString& aString)
5594 {
5595 int32_t i, len = aString.Length();
5596 for (i = 0; i < len; i++) {
5597 char16_t ch = aString.CharAt(i);
5598 if (ch == '<') {
5599 nsAutoString tmp(NS_LITERAL_STRING("&lt;"));
5600 aString.Cut(i, 1);
5601 aString.Insert(tmp, i);
5602 len += 3;
5603 i += 3;
5604 }
5605 else if (ch == '>') {
5606 nsAutoString tmp(NS_LITERAL_STRING("&gt;"));
5607 aString.Cut(i, 1);
5608 aString.Insert(tmp, i);
5609 len += 3;
5610 i += 3;
5611 }
5612 else if (ch == '\"') {
5613 nsAutoString tmp(NS_LITERAL_STRING("&quot;"));
5614 aString.Cut(i, 1);
5615 aString.Insert(tmp, i);
5616 len += 5;
5617 i += 5;
5618 }
5619 }
5620 }
5621 #endif
5622
5623 bool
5624 nsIFrame::IsVisibleForPainting(nsDisplayListBuilder* aBuilder) {
5625 if (!StyleVisibility()->IsVisible())
5626 return false;
5627 nsISelection* sel = aBuilder->GetBoundingSelection();
5628 return !sel || IsVisibleInSelection(sel);
5629 }
5630
5631 bool
5632 nsIFrame::IsVisibleForPainting() {
5633 if (!StyleVisibility()->IsVisible())
5634 return false;
5635
5636 nsPresContext* pc = PresContext();
5637 if (!pc->IsRenderingOnlySelection())
5638 return true;
5639
5640 nsCOMPtr<nsISelectionController> selcon(do_QueryInterface(pc->PresShell()));
5641 if (selcon) {
5642 nsCOMPtr<nsISelection> sel;
5643 selcon->GetSelection(nsISelectionController::SELECTION_NORMAL,
5644 getter_AddRefs(sel));
5645 if (sel)
5646 return IsVisibleInSelection(sel);
5647 }
5648 return true;
5649 }
5650
5651 bool
5652 nsIFrame::IsVisibleInSelection(nsDisplayListBuilder* aBuilder) {
5653 nsISelection* sel = aBuilder->GetBoundingSelection();
5654 return !sel || IsVisibleInSelection(sel);
5655 }
5656
5657 bool
5658 nsIFrame::IsVisibleOrCollapsedForPainting(nsDisplayListBuilder* aBuilder) {
5659 if (!StyleVisibility()->IsVisibleOrCollapsed())
5660 return false;
5661 nsISelection* sel = aBuilder->GetBoundingSelection();
5662 return !sel || IsVisibleInSelection(sel);
5663 }
5664
5665 bool
5666 nsIFrame::IsVisibleInSelection(nsISelection* aSelection)
5667 {
5668 if (!GetContent() || !GetContent()->IsSelectionDescendant()) {
5669 return false;
5670 }
5671
5672 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mContent));
5673 bool vis;
5674 nsresult rv = aSelection->ContainsNode(node, true, &vis);
5675 return NS_FAILED(rv) || vis;
5676 }
5677
5678 /* virtual */ bool
5679 nsFrame::IsEmpty()
5680 {
5681 return false;
5682 }
5683
5684 bool
5685 nsIFrame::CachedIsEmpty()
5686 {
5687 NS_PRECONDITION(!(GetStateBits() & NS_FRAME_IS_DIRTY),
5688 "Must only be called on reflowed lines");
5689 return IsEmpty();
5690 }
5691
5692 /* virtual */ bool
5693 nsFrame::IsSelfEmpty()
5694 {
5695 return false;
5696 }
5697
5698 nsresult
5699 nsFrame::GetSelectionController(nsPresContext *aPresContext, nsISelectionController **aSelCon)
5700 {
5701 if (!aPresContext || !aSelCon)
5702 return NS_ERROR_INVALID_ARG;
5703
5704 nsIFrame *frame = this;
5705 while (frame && (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION)) {
5706 nsITextControlFrame *tcf = do_QueryFrame(frame);
5707 if (tcf) {
5708 return tcf->GetOwnedSelectionController(aSelCon);
5709 }
5710 frame = frame->GetParent();
5711 }
5712
5713 return CallQueryInterface(aPresContext->GetPresShell(), aSelCon);
5714 }
5715
5716 already_AddRefed<nsFrameSelection>
5717 nsIFrame::GetFrameSelection()
5718 {
5719 nsRefPtr<nsFrameSelection> fs =
5720 const_cast<nsFrameSelection*>(GetConstFrameSelection());
5721 return fs.forget();
5722 }
5723
5724 const nsFrameSelection*
5725 nsIFrame::GetConstFrameSelection() const
5726 {
5727 nsIFrame* frame = const_cast<nsIFrame*>(this);
5728 while (frame && (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION)) {
5729 nsITextControlFrame* tcf = do_QueryFrame(frame);
5730 if (tcf) {
5731 return tcf->GetOwnedFrameSelection();
5732 }
5733 frame = frame->GetParent();
5734 }
5735
5736 return PresContext()->PresShell()->ConstFrameSelection();
5737 }
5738
5739 #ifdef DEBUG
5740 nsresult
5741 nsFrame::DumpRegressionData(nsPresContext* aPresContext, FILE* out, int32_t aIndent)
5742 {
5743 IndentBy(out, aIndent);
5744 fprintf(out, "<frame va=\"%p\" type=\"", (void*)this);
5745 nsAutoString name;
5746 GetFrameName(name);
5747 XMLQuote(name);
5748 fputs(NS_LossyConvertUTF16toASCII(name).get(), out);
5749 fprintf(out, "\" state=\"%016llx\" parent=\"%p\">\n",
5750 (unsigned long long)GetDebugStateBits(), (void*)mParent);
5751
5752 aIndent++;
5753 DumpBaseRegressionData(aPresContext, out, aIndent);
5754 aIndent--;
5755
5756 IndentBy(out, aIndent);
5757 fprintf(out, "</frame>\n");
5758
5759 return NS_OK;
5760 }
5761
5762 void
5763 nsFrame::DumpBaseRegressionData(nsPresContext* aPresContext, FILE* out, int32_t aIndent)
5764 {
5765 if (GetNextSibling()) {
5766 IndentBy(out, aIndent);
5767 fprintf(out, "<next-sibling va=\"%p\"/>\n", (void*)GetNextSibling());
5768 }
5769
5770 if (HasView()) {
5771 IndentBy(out, aIndent);
5772 fprintf(out, "<view va=\"%p\">\n", (void*)GetView());
5773 aIndent++;
5774 // XXX add in code to dump out view state too...
5775 aIndent--;
5776 IndentBy(out, aIndent);
5777 fprintf(out, "</view>\n");
5778 }
5779
5780 IndentBy(out, aIndent);
5781 fprintf(out, "<bbox x=\"%d\" y=\"%d\" w=\"%d\" h=\"%d\"/>\n",
5782 mRect.x, mRect.y, mRect.width, mRect.height);
5783
5784 // Now dump all of the children on all of the child lists
5785 ChildListIterator lists(this);
5786 for (; !lists.IsDone(); lists.Next()) {
5787 IndentBy(out, aIndent);
5788 if (lists.CurrentID() != kPrincipalList) {
5789 fprintf(out, "<child-list name=\"%s\">\n", mozilla::layout::ChildListName(lists.CurrentID()));
5790 }
5791 else {
5792 fprintf(out, "<child-list>\n");
5793 }
5794 aIndent++;
5795 nsFrameList::Enumerator childFrames(lists.CurrentList());
5796 for (; !childFrames.AtEnd(); childFrames.Next()) {
5797 nsIFrame* kid = childFrames.get();
5798 kid->DumpRegressionData(aPresContext, out, aIndent);
5799 }
5800 aIndent--;
5801 IndentBy(out, aIndent);
5802 fprintf(out, "</child-list>\n");
5803 }
5804 }
5805 #endif
5806
5807 bool
5808 nsIFrame::IsFrameSelected() const
5809 {
5810 NS_ASSERTION(!GetContent() || GetContent()->IsSelectionDescendant(),
5811 "use the public IsSelected() instead");
5812 return nsRange::IsNodeSelected(GetContent(), 0,
5813 GetContent()->GetChildCount());
5814 }
5815
5816 nsresult
5817 nsFrame::GetPointFromOffset(int32_t inOffset, nsPoint* outPoint)
5818 {
5819 NS_PRECONDITION(outPoint != nullptr, "Null parameter");
5820 nsRect contentRect = GetContentRect() - GetPosition();
5821 nsPoint pt = contentRect.TopLeft();
5822 if (mContent)
5823 {
5824 nsIContent* newContent = mContent->GetParent();
5825 if (newContent){
5826 int32_t newOffset = newContent->IndexOf(mContent);
5827
5828 bool isRTL = (NS_GET_EMBEDDING_LEVEL(this) & 1) == 1;
5829 if ((!isRTL && inOffset > newOffset) ||
5830 (isRTL && inOffset <= newOffset)) {
5831 pt = contentRect.TopRight();
5832 }
5833 }
5834 }
5835 *outPoint = pt;
5836 return NS_OK;
5837 }
5838
5839 nsresult
5840 nsFrame::GetChildFrameContainingOffset(int32_t inContentOffset, bool inHint, int32_t* outFrameContentOffset, nsIFrame **outChildFrame)
5841 {
5842 NS_PRECONDITION(outChildFrame && outFrameContentOffset, "Null parameter");
5843 *outFrameContentOffset = (int32_t)inHint;
5844 //the best frame to reflect any given offset would be a visible frame if possible
5845 //i.e. we are looking for a valid frame to place the blinking caret
5846 nsRect rect = GetRect();
5847 if (!rect.width || !rect.height)
5848 {
5849 //if we have a 0 width or height then lets look for another frame that possibly has
5850 //the same content. If we have no frames in flow then just let us return 'this' frame
5851 nsIFrame* nextFlow = GetNextInFlow();
5852 if (nextFlow)
5853 return nextFlow->GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame);
5854 }
5855 *outChildFrame = this;
5856 return NS_OK;
5857 }
5858
5859 //
5860 // What I've pieced together about this routine:
5861 // Starting with a block frame (from which a line frame can be gotten)
5862 // and a line number, drill down and get the first/last selectable
5863 // frame on that line, depending on aPos->mDirection.
5864 // aOutSideLimit != 0 means ignore aLineStart, instead work from
5865 // the end (if > 0) or beginning (if < 0).
5866 //
5867 nsresult
5868 nsFrame::GetNextPrevLineFromeBlockFrame(nsPresContext* aPresContext,
5869 nsPeekOffsetStruct *aPos,
5870 nsIFrame *aBlockFrame,
5871 int32_t aLineStart,
5872 int8_t aOutSideLimit
5873 )
5874 {
5875 //magic numbers aLineStart will be -1 for end of block 0 will be start of block
5876 if (!aBlockFrame || !aPos)
5877 return NS_ERROR_NULL_POINTER;
5878
5879 aPos->mResultFrame = nullptr;
5880 aPos->mResultContent = nullptr;
5881 aPos->mAttachForward = (aPos->mDirection == eDirNext);
5882
5883 nsAutoLineIterator it = aBlockFrame->GetLineIterator();
5884 if (!it)
5885 return NS_ERROR_FAILURE;
5886 int32_t searchingLine = aLineStart;
5887 int32_t countLines = it->GetNumLines();
5888 if (aOutSideLimit > 0) //start at end
5889 searchingLine = countLines;
5890 else if (aOutSideLimit <0)//start at beginning
5891 searchingLine = -1;//"next" will be 0
5892 else
5893 if ((aPos->mDirection == eDirPrevious && searchingLine == 0) ||
5894 (aPos->mDirection == eDirNext && searchingLine >= (countLines -1) )){
5895 //we need to jump to new block frame.
5896 return NS_ERROR_FAILURE;
5897 }
5898 int32_t lineFrameCount;
5899 nsIFrame *resultFrame = nullptr;
5900 nsIFrame *farStoppingFrame = nullptr; //we keep searching until we find a "this" frame then we go to next line
5901 nsIFrame *nearStoppingFrame = nullptr; //if we are backing up from edge, stop here
5902 nsIFrame *firstFrame;
5903 nsIFrame *lastFrame;
5904 nsRect rect;
5905 bool isBeforeFirstFrame, isAfterLastFrame;
5906 bool found = false;
5907
5908 nsresult result = NS_OK;
5909 while (!found)
5910 {
5911 if (aPos->mDirection == eDirPrevious)
5912 searchingLine --;
5913 else
5914 searchingLine ++;
5915 if ((aPos->mDirection == eDirPrevious && searchingLine < 0) ||
5916 (aPos->mDirection == eDirNext && searchingLine >= countLines ))
5917 {
5918 //we need to jump to new block frame.
5919 return NS_ERROR_FAILURE;
5920 }
5921 uint32_t lineFlags;
5922 result = it->GetLine(searchingLine, &firstFrame, &lineFrameCount,
5923 rect, &lineFlags);
5924 if (!lineFrameCount)
5925 continue;
5926 if (NS_SUCCEEDED(result)){
5927 lastFrame = firstFrame;
5928 for (;lineFrameCount > 1;lineFrameCount --){
5929 //result = lastFrame->GetNextSibling(&lastFrame, searchingLine);
5930 result = it->GetNextSiblingOnLine(lastFrame, searchingLine);
5931 if (NS_FAILED(result) || !lastFrame){
5932 NS_ERROR("GetLine promised more frames than could be found");
5933 return NS_ERROR_FAILURE;
5934 }
5935 }
5936 GetLastLeaf(aPresContext, &lastFrame);
5937
5938 if (aPos->mDirection == eDirNext){
5939 nearStoppingFrame = firstFrame;
5940 farStoppingFrame = lastFrame;
5941 }
5942 else{
5943 nearStoppingFrame = lastFrame;
5944 farStoppingFrame = firstFrame;
5945 }
5946 nsPoint offset;
5947 nsView * view; //used for call of get offset from view
5948 aBlockFrame->GetOffsetFromView(offset,&view);
5949 nscoord newDesiredX = aPos->mDesiredX - offset.x;//get desired x into blockframe coordinates!
5950 result = it->FindFrameAt(searchingLine, newDesiredX, &resultFrame, &isBeforeFirstFrame, &isAfterLastFrame);
5951 if(NS_FAILED(result))
5952 continue;
5953 }
5954
5955 if (NS_SUCCEEDED(result) && resultFrame)
5956 {
5957 //check to see if this is ANOTHER blockframe inside the other one if so then call into its lines
5958 nsAutoLineIterator newIt = resultFrame->GetLineIterator();
5959 if (newIt)
5960 {
5961 aPos->mResultFrame = resultFrame;
5962 return NS_OK;
5963 }
5964 //resultFrame is not a block frame
5965 result = NS_ERROR_FAILURE;
5966
5967 nsCOMPtr<nsIFrameEnumerator> frameTraversal;
5968 result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
5969 aPresContext, resultFrame,
5970 ePostOrder,
5971 false, // aVisual
5972 aPos->mScrollViewStop,
5973 false // aFollowOOFs
5974 );
5975 if (NS_FAILED(result))
5976 return result;
5977
5978 nsIFrame *storeOldResultFrame = resultFrame;
5979 while ( !found ){
5980 nsPoint point;
5981 point.x = aPos->mDesiredX;
5982
5983 nsRect tempRect = resultFrame->GetRect();
5984 nsPoint offset;
5985 nsView * view; //used for call of get offset from view
5986 resultFrame->GetOffsetFromView(offset, &view);
5987 if (!view) {
5988 return NS_ERROR_FAILURE;
5989 }
5990 point.y = tempRect.height + offset.y;
5991
5992 //special check. if we allow non-text selection then we can allow a hit location to fall before a table.
5993 //otherwise there is no way to get and click signal to fall before a table (it being a line iterator itself)
5994 nsIPresShell *shell = aPresContext->GetPresShell();
5995 if (!shell)
5996 return NS_ERROR_FAILURE;
5997 int16_t isEditor = shell->GetSelectionFlags();
5998 isEditor = isEditor == nsISelectionDisplay::DISPLAY_ALL;
5999 if ( isEditor )
6000 {
6001 if (resultFrame->GetType() == nsGkAtoms::tableOuterFrame)
6002 {
6003 if (((point.x - offset.x + tempRect.x)<0) || ((point.x - offset.x+ tempRect.x)>tempRect.width))//off left/right side
6004 {
6005 nsIContent* content = resultFrame->GetContent();
6006 if (content)
6007 {
6008 nsIContent* parent = content->GetParent();
6009 if (parent)
6010 {
6011 aPos->mResultContent = parent;
6012 aPos->mContentOffset = parent->IndexOf(content);
6013 aPos->mAttachForward = false;
6014 if ((point.x - offset.x+ tempRect.x)>tempRect.width)
6015 {
6016 aPos->mContentOffset++;//go to end of this frame
6017 aPos->mAttachForward = true;
6018 }
6019 //result frame is the result frames parent.
6020 aPos->mResultFrame = resultFrame->GetParent();
6021 return NS_POSITION_BEFORE_TABLE;
6022 }
6023 }
6024 }
6025 }
6026 }
6027
6028 if (!resultFrame->HasView())
6029 {
6030 nsView* view;
6031 nsPoint offset;
6032 resultFrame->GetOffsetFromView(offset, &view);
6033 ContentOffsets offsets =
6034 resultFrame->GetContentOffsetsFromPoint(point - offset);
6035 aPos->mResultContent = offsets.content;
6036 aPos->mContentOffset = offsets.offset;
6037 aPos->mAttachForward = offsets.associateWithNext;
6038 if (offsets.content)
6039 {
6040 bool selectable;
6041 resultFrame->IsSelectable(&selectable, nullptr);
6042 if (selectable)
6043 {
6044 found = true;
6045 break;
6046 }
6047 }
6048 }
6049
6050 if (aPos->mDirection == eDirPrevious && (resultFrame == farStoppingFrame))
6051 break;
6052 if (aPos->mDirection == eDirNext && (resultFrame == nearStoppingFrame))
6053 break;
6054 //always try previous on THAT line if that fails go the other way
6055 frameTraversal->Prev();
6056 resultFrame = frameTraversal->CurrentItem();
6057 if (!resultFrame)
6058 return NS_ERROR_FAILURE;
6059 }
6060
6061 if (!found){
6062 resultFrame = storeOldResultFrame;
6063
6064 result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
6065 aPresContext, resultFrame,
6066 eLeaf,
6067 false, // aVisual
6068 aPos->mScrollViewStop,
6069 false // aFollowOOFs
6070 );
6071 }
6072 while ( !found ){
6073 nsPoint point(aPos->mDesiredX, 0);
6074 nsView* view;
6075 nsPoint offset;
6076 resultFrame->GetOffsetFromView(offset, &view);
6077 ContentOffsets offsets =
6078 resultFrame->GetContentOffsetsFromPoint(point - offset);
6079 aPos->mResultContent = offsets.content;
6080 aPos->mContentOffset = offsets.offset;
6081 aPos->mAttachForward = offsets.associateWithNext;
6082 if (offsets.content)
6083 {
6084 bool selectable;
6085 resultFrame->IsSelectable(&selectable, nullptr);
6086 if (selectable)
6087 {
6088 found = true;
6089 if (resultFrame == farStoppingFrame)
6090 aPos->mAttachForward = false;
6091 else
6092 aPos->mAttachForward = true;
6093 break;
6094 }
6095 }
6096 if (aPos->mDirection == eDirPrevious && (resultFrame == nearStoppingFrame))
6097 break;
6098 if (aPos->mDirection == eDirNext && (resultFrame == farStoppingFrame))
6099 break;
6100 //previous didnt work now we try "next"
6101 frameTraversal->Next();
6102 nsIFrame *tempFrame = frameTraversal->CurrentItem();
6103 if (!tempFrame)
6104 break;
6105 resultFrame = tempFrame;
6106 }
6107 aPos->mResultFrame = resultFrame;
6108 }
6109 else {
6110 //we need to jump to new block frame.
6111 aPos->mAmount = eSelectLine;
6112 aPos->mStartOffset = 0;
6113 aPos->mAttachForward = !(aPos->mDirection == eDirNext);
6114 if (aPos->mDirection == eDirPrevious)
6115 aPos->mStartOffset = -1;//start from end
6116 return aBlockFrame->PeekOffset(aPos);
6117 }
6118 }
6119 return NS_OK;
6120 }
6121
6122 nsIFrame::CaretPosition
6123 nsIFrame::GetExtremeCaretPosition(bool aStart)
6124 {
6125 CaretPosition result;
6126
6127 FrameTarget targetFrame = DrillDownToSelectionFrame(this, !aStart, 0);
6128 FrameContentRange range = GetRangeForFrame(targetFrame.frame);
6129 result.mResultContent = range.content;
6130 result.mContentOffset = aStart ? range.start : range.end;
6131 return result;
6132 }
6133
6134 // Find the first (or last) descendant of the given frame
6135 // which is either a block frame or a BRFrame.
6136 static nsContentAndOffset
6137 FindBlockFrameOrBR(nsIFrame* aFrame, nsDirection aDirection)
6138 {
6139 nsContentAndOffset result;
6140 result.mContent = nullptr;
6141 result.mOffset = 0;
6142
6143 if (aFrame->IsGeneratedContentFrame())
6144 return result;
6145
6146 // Treat form controls as inline leaves
6147 // XXX we really need a way to determine whether a frame is inline-level
6148 nsIFormControlFrame* fcf = do_QueryFrame(aFrame);
6149 if (fcf)
6150 return result;
6151
6152 // Check the frame itself
6153 // Fall through block-in-inline split frames because their mContent is
6154 // the content of the inline frames they were created from. The
6155 // first/last child of such frames is the real block frame we're
6156 // looking for.
6157 if ((nsLayoutUtils::GetAsBlock(aFrame) &&
6158 !(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) ||
6159 aFrame->GetType() == nsGkAtoms::brFrame) {
6160 nsIContent* content = aFrame->GetContent();
6161 result.mContent = content->GetParent();
6162 // In some cases (bug 310589, bug 370174) we end up here with a null content.
6163 // This probably shouldn't ever happen, but since it sometimes does, we want
6164 // to avoid crashing here.
6165 NS_ASSERTION(result.mContent, "Unexpected orphan content");
6166 if (result.mContent)
6167 result.mOffset = result.mContent->IndexOf(content) +
6168 (aDirection == eDirPrevious ? 1 : 0);
6169 return result;
6170 }
6171
6172 // If this is a preformatted text frame, see if it ends with a newline
6173 if (aFrame->HasSignificantTerminalNewline()) {
6174 int32_t startOffset, endOffset;
6175 aFrame->GetOffsets(startOffset, endOffset);
6176 result.mContent = aFrame->GetContent();
6177 result.mOffset = endOffset - (aDirection == eDirPrevious ? 0 : 1);
6178 return result;
6179 }
6180
6181 // Iterate over children and call ourselves recursively
6182 if (aDirection == eDirPrevious) {
6183 nsIFrame* child = aFrame->GetLastChild(nsIFrame::kPrincipalList);
6184 while(child && !result.mContent) {
6185 result = FindBlockFrameOrBR(child, aDirection);
6186 child = child->GetPrevSibling();
6187 }
6188 } else { // eDirNext
6189 nsIFrame* child = aFrame->GetFirstPrincipalChild();
6190 while(child && !result.mContent) {
6191 result = FindBlockFrameOrBR(child, aDirection);
6192 child = child->GetNextSibling();
6193 }
6194 }
6195 return result;
6196 }
6197
6198 nsresult
6199 nsIFrame::PeekOffsetParagraph(nsPeekOffsetStruct *aPos)
6200 {
6201 nsIFrame* frame = this;
6202 nsContentAndOffset blockFrameOrBR;
6203 blockFrameOrBR.mContent = nullptr;
6204 bool reachedBlockAncestor = false;
6205
6206 // Go through containing frames until reaching a block frame.
6207 // In each step, search the previous (or next) siblings for the closest
6208 // "stop frame" (a block frame or a BRFrame).
6209 // If found, set it to be the selection boundray and abort.
6210
6211 if (aPos->mDirection == eDirPrevious) {
6212 while (!reachedBlockAncestor) {
6213 nsIFrame* parent = frame->GetParent();
6214 // Treat a frame associated with the root content as if it were a block frame.
6215 if (!frame->mContent || !frame->mContent->GetParent()) {
6216 reachedBlockAncestor = true;
6217 break;
6218 }
6219 nsIFrame* sibling = frame->GetPrevSibling();
6220 while (sibling && !blockFrameOrBR.mContent) {
6221 blockFrameOrBR = FindBlockFrameOrBR(sibling, eDirPrevious);
6222 sibling = sibling->GetPrevSibling();
6223 }
6224 if (blockFrameOrBR.mContent) {
6225 aPos->mResultContent = blockFrameOrBR.mContent;
6226 aPos->mContentOffset = blockFrameOrBR.mOffset;
6227 break;
6228 }
6229 frame = parent;
6230 reachedBlockAncestor = (nsLayoutUtils::GetAsBlock(frame) != nullptr);
6231 }
6232 if (reachedBlockAncestor) { // no "stop frame" found
6233 aPos->mResultContent = frame->GetContent();
6234 aPos->mContentOffset = 0;
6235 }
6236 } else { // eDirNext
6237 while (!reachedBlockAncestor) {
6238 nsIFrame* parent = frame->GetParent();
6239 // Treat a frame associated with the root content as if it were a block frame.
6240 if (!frame->mContent || !frame->mContent->GetParent()) {
6241 reachedBlockAncestor = true;
6242 break;
6243 }
6244 nsIFrame* sibling = frame;
6245 while (sibling && !blockFrameOrBR.mContent) {
6246 blockFrameOrBR = FindBlockFrameOrBR(sibling, eDirNext);
6247 sibling = sibling->GetNextSibling();
6248 }
6249 if (blockFrameOrBR.mContent) {
6250 aPos->mResultContent = blockFrameOrBR.mContent;
6251 aPos->mContentOffset = blockFrameOrBR.mOffset;
6252 break;
6253 }
6254 frame = parent;
6255 reachedBlockAncestor = (nsLayoutUtils::GetAsBlock(frame) != nullptr);
6256 }
6257 if (reachedBlockAncestor) { // no "stop frame" found
6258 aPos->mResultContent = frame->GetContent();
6259 if (aPos->mResultContent)
6260 aPos->mContentOffset = aPos->mResultContent->GetChildCount();
6261 }
6262 }
6263 return NS_OK;
6264 }
6265
6266 // Determine movement direction relative to frame
6267 static bool IsMovingInFrameDirection(nsIFrame* frame, nsDirection aDirection, bool aVisual)
6268 {
6269 bool isReverseDirection = aVisual ?
6270 (NS_GET_EMBEDDING_LEVEL(frame) & 1) != (NS_GET_BASE_LEVEL(frame) & 1) : false;
6271 return aDirection == (isReverseDirection ? eDirPrevious : eDirNext);
6272 }
6273
6274 nsresult
6275 nsIFrame::PeekOffset(nsPeekOffsetStruct* aPos)
6276 {
6277 if (!aPos)
6278 return NS_ERROR_NULL_POINTER;
6279 nsresult result = NS_ERROR_FAILURE;
6280
6281 if (mState & NS_FRAME_IS_DIRTY)
6282 return NS_ERROR_UNEXPECTED;
6283
6284 // Translate content offset to be relative to frame
6285 FrameContentRange range = GetRangeForFrame(this);
6286 int32_t offset = aPos->mStartOffset - range.start;
6287 nsIFrame* current = this;
6288
6289 switch (aPos->mAmount) {
6290 case eSelectCharacter:
6291 case eSelectCluster:
6292 {
6293 bool eatingNonRenderableWS = false;
6294 nsIFrame::FrameSearchResult peekSearchState = CONTINUE;
6295 bool jumpedLine = false;
6296 bool movedOverNonSelectableText = false;
6297
6298 while (peekSearchState != FOUND) {
6299 bool movingInFrameDirection =
6300 IsMovingInFrameDirection(current, aPos->mDirection, aPos->mVisual);
6301
6302 if (eatingNonRenderableWS)
6303 peekSearchState = current->PeekOffsetNoAmount(movingInFrameDirection, &offset);
6304 else
6305 peekSearchState = current->PeekOffsetCharacter(movingInFrameDirection, &offset,
6306 aPos->mAmount == eSelectCluster);
6307
6308 movedOverNonSelectableText |= (peekSearchState == CONTINUE_UNSELECTABLE);
6309
6310 if (peekSearchState != FOUND) {
6311 result =
6312 current->GetFrameFromDirection(aPos->mDirection, aPos->mVisual,
6313 aPos->mJumpLines, aPos->mScrollViewStop,
6314 &current, &offset, &jumpedLine);
6315 if (NS_FAILED(result))
6316 return result;
6317
6318 // If we jumped lines, it's as if we found a character, but we still need
6319 // to eat non-renderable content on the new line.
6320 if (jumpedLine)
6321 eatingNonRenderableWS = true;
6322 }
6323
6324 // Found frame, but because we moved over non selectable text we want the offset
6325 // to be at the frame edge.
6326 if (peekSearchState == FOUND && movedOverNonSelectableText)
6327 {
6328 int32_t start, end;
6329 current->GetOffsets(start, end);
6330 offset = aPos->mDirection == eDirNext ? 0 : end - start;
6331 }
6332 }
6333
6334 // Set outputs
6335 range = GetRangeForFrame(current);
6336 aPos->mResultFrame = current;
6337 aPos->mResultContent = range.content;
6338 // Output offset is relative to content, not frame
6339 aPos->mContentOffset = offset < 0 ? range.end : range.start + offset;
6340 // If we're dealing with a text frame and moving backward positions us at
6341 // the end of that line, decrease the offset by one to make sure that
6342 // we're placed before the linefeed character on the previous line.
6343 if (offset < 0 && jumpedLine &&
6344 aPos->mDirection == eDirPrevious &&
6345 current->HasSignificantTerminalNewline()) {
6346 --aPos->mContentOffset;
6347 }
6348
6349 break;
6350 }
6351 case eSelectWordNoSpace:
6352 // eSelectWordNoSpace means that we should not be eating any whitespace when
6353 // moving to the adjacent word. This means that we should set aPos->
6354 // mWordMovementType to eEndWord if we're moving forwards, and to eStartWord
6355 // if we're moving backwards.
6356 if (aPos->mDirection == eDirPrevious) {
6357 aPos->mWordMovementType = eStartWord;
6358 } else {
6359 aPos->mWordMovementType = eEndWord;
6360 }
6361 // Intentionally fall through the eSelectWord case.
6362 case eSelectWord:
6363 {
6364 // wordSelectEatSpace means "are we looking for a boundary between whitespace
6365 // and non-whitespace (in the direction we're moving in)".
6366 // It is true when moving forward and looking for a beginning of a word, or
6367 // when moving backwards and looking for an end of a word.
6368 bool wordSelectEatSpace;
6369 if (aPos->mWordMovementType != eDefaultBehavior) {
6370 // aPos->mWordMovementType possible values:
6371 // eEndWord: eat the space if we're moving backwards
6372 // eStartWord: eat the space if we're moving forwards
6373 wordSelectEatSpace = ((aPos->mWordMovementType == eEndWord) == (aPos->mDirection == eDirPrevious));
6374 }
6375 else {
6376 // Use the hidden preference which is based on operating system behavior.
6377 // This pref only affects whether moving forward by word should go to the end of this word or start of the next word.
6378 // When going backwards, the start of the word is always used, on every operating system.
6379 wordSelectEatSpace = aPos->mDirection == eDirNext &&
6380 Preferences::GetBool("layout.word_select.eat_space_to_next_word");
6381 }
6382
6383 // mSawBeforeType means "we already saw characters of the type
6384 // before the boundary we're looking for". Examples:
6385 // 1. If we're moving forward, looking for a word beginning (i.e. a boundary
6386 // between whitespace and non-whitespace), then eatingWS==true means
6387 // "we already saw some whitespace".
6388 // 2. If we're moving backward, looking for a word beginning (i.e. a boundary
6389 // between non-whitespace and whitespace), then eatingWS==true means
6390 // "we already saw some non-whitespace".
6391 PeekWordState state;
6392 int32_t offsetAdjustment = 0;
6393 bool done = false;
6394 while (!done) {
6395 bool movingInFrameDirection =
6396 IsMovingInFrameDirection(current, aPos->mDirection, aPos->mVisual);
6397
6398 done = current->PeekOffsetWord(movingInFrameDirection, wordSelectEatSpace,
6399 aPos->mIsKeyboardSelect, &offset, &state) == FOUND;
6400
6401 if (!done) {
6402 nsIFrame* nextFrame;
6403 int32_t nextFrameOffset;
6404 bool jumpedLine;
6405 result =
6406 current->GetFrameFromDirection(aPos->mDirection, aPos->mVisual,
6407 aPos->mJumpLines, aPos->mScrollViewStop,
6408 &nextFrame, &nextFrameOffset, &jumpedLine);
6409 // We can't jump lines if we're looking for whitespace following
6410 // non-whitespace, and we already encountered non-whitespace.
6411 if (NS_FAILED(result) ||
6412 (jumpedLine && !wordSelectEatSpace && state.mSawBeforeType)) {
6413 done = true;
6414 // If we've crossed the line boundary, check to make sure that we
6415 // have not consumed a trailing newline as whitesapce if it's significant.
6416 if (jumpedLine && wordSelectEatSpace &&
6417 current->HasSignificantTerminalNewline()) {
6418 offsetAdjustment = -1;
6419 }
6420 } else {
6421 if (jumpedLine) {
6422 state.mContext.Truncate();
6423 }
6424 current = nextFrame;
6425 offset = nextFrameOffset;
6426 // Jumping a line is equivalent to encountering whitespace
6427 if (wordSelectEatSpace && jumpedLine)
6428 state.SetSawBeforeType();
6429 }
6430 }
6431 }
6432
6433 // Set outputs
6434 range = GetRangeForFrame(current);
6435 aPos->mResultFrame = current;
6436 aPos->mResultContent = range.content;
6437 // Output offset is relative to content, not frame
6438 aPos->mContentOffset = (offset < 0 ? range.end : range.start + offset) + offsetAdjustment;
6439 break;
6440 }
6441 case eSelectLine :
6442 {
6443 nsAutoLineIterator iter;
6444 nsIFrame *blockFrame = this;
6445
6446 while (NS_FAILED(result)){
6447 int32_t thisLine = nsFrame::GetLineNumber(blockFrame, aPos->mScrollViewStop, &blockFrame);
6448 if (thisLine < 0)
6449 return NS_ERROR_FAILURE;
6450 iter = blockFrame->GetLineIterator();
6451 NS_ASSERTION(iter, "GetLineNumber() succeeded but no block frame?");
6452 result = NS_OK;
6453
6454 int edgeCase = 0;//no edge case. this should look at thisLine
6455
6456 bool doneLooping = false;//tells us when no more block frames hit.
6457 //this part will find a frame or a block frame. if it's a block frame
6458 //it will "drill down" to find a viable frame or it will return an error.
6459 nsIFrame *lastFrame = this;
6460 do {
6461 result = nsFrame::GetNextPrevLineFromeBlockFrame(PresContext(),
6462 aPos,
6463 blockFrame,
6464 thisLine,
6465 edgeCase //start from thisLine
6466 );
6467 if (NS_SUCCEEDED(result) && (!aPos->mResultFrame || aPos->mResultFrame == lastFrame))//we came back to same spot! keep going
6468 {
6469 aPos->mResultFrame = nullptr;
6470 if (aPos->mDirection == eDirPrevious)
6471 thisLine--;
6472 else
6473 thisLine++;
6474 }
6475 else //if failure or success with different frame.
6476 doneLooping = true; //do not continue with while loop
6477
6478 lastFrame = aPos->mResultFrame; //set last frame
6479
6480 if (NS_SUCCEEDED(result) && aPos->mResultFrame
6481 && blockFrame != aPos->mResultFrame)// make sure block element is not the same as the one we had before
6482 {
6483 /* SPECIAL CHECK FOR TABLE NAVIGATION
6484 tables need to navigate also and the frame that supports it is nsTableRowGroupFrame which is INSIDE
6485 nsTableOuterFrame. if we have stumbled onto an nsTableOuter we need to drill into nsTableRowGroup
6486 if we hit a header or footer that's ok just go into them,
6487 */
6488 bool searchTableBool = false;
6489 if (aPos->mResultFrame->GetType() == nsGkAtoms::tableOuterFrame ||
6490 aPos->mResultFrame->GetType() == nsGkAtoms::tableCellFrame)
6491 {
6492 nsIFrame *frame = aPos->mResultFrame->GetFirstPrincipalChild();
6493 //got the table frame now
6494 while(frame) //ok time to drill down to find iterator
6495 {
6496 iter = frame->GetLineIterator();
6497 if (iter)
6498 {
6499 aPos->mResultFrame = frame;
6500 searchTableBool = true;
6501 result = NS_OK;
6502 break; //while(frame)
6503 }
6504 result = NS_ERROR_FAILURE;
6505 frame = frame->GetFirstPrincipalChild();
6506 }
6507 }
6508
6509 if (!searchTableBool) {
6510 iter = aPos->mResultFrame->GetLineIterator();
6511 result = iter ? NS_OK : NS_ERROR_FAILURE;
6512 }
6513 if (NS_SUCCEEDED(result) && iter)//we've struck another block element!
6514 {
6515 doneLooping = false;
6516 if (aPos->mDirection == eDirPrevious)
6517 edgeCase = 1;//far edge, search from end backwards
6518 else
6519 edgeCase = -1;//near edge search from beginning onwards
6520 thisLine=0;//this line means nothing now.
6521 //everything else means something so keep looking "inside" the block
6522 blockFrame = aPos->mResultFrame;
6523
6524 }
6525 else
6526 {
6527 result = NS_OK;//THIS is to mean that everything is ok to the containing while loop
6528 break;
6529 }
6530 }
6531 } while (!doneLooping);
6532 }
6533 return result;
6534 }
6535
6536 case eSelectParagraph:
6537 return PeekOffsetParagraph(aPos);
6538
6539 case eSelectBeginLine:
6540 case eSelectEndLine:
6541 {
6542 // Adjusted so that the caret can't get confused when content changes
6543 nsIFrame* blockFrame = AdjustFrameForSelectionStyles(this);
6544 int32_t thisLine = nsFrame::GetLineNumber(blockFrame, aPos->mScrollViewStop, &blockFrame);
6545 if (thisLine < 0)
6546 return NS_ERROR_FAILURE;
6547 nsAutoLineIterator it = blockFrame->GetLineIterator();
6548 NS_ASSERTION(it, "GetLineNumber() succeeded but no block frame?");
6549
6550 int32_t lineFrameCount;
6551 nsIFrame *firstFrame;
6552 nsRect usedRect;
6553 uint32_t lineFlags;
6554 nsIFrame* baseFrame = nullptr;
6555 bool endOfLine = (eSelectEndLine == aPos->mAmount);
6556
6557 if (aPos->mVisual && PresContext()->BidiEnabled()) {
6558 bool lineIsRTL = it->GetDirection();
6559 bool isReordered;
6560 nsIFrame *lastFrame;
6561 result = it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame);
6562 baseFrame = endOfLine ? lastFrame : firstFrame;
6563 if (baseFrame) {
6564 nsBidiLevel embeddingLevel = nsBidiPresUtils::GetFrameEmbeddingLevel(baseFrame);
6565 // If the direction of the frame on the edge is opposite to that of the line,
6566 // we'll need to drill down to its opposite end, so reverse endOfLine.
6567 if ((embeddingLevel & 1) == !lineIsRTL)
6568 endOfLine = !endOfLine;
6569 }
6570 } else {
6571 it->GetLine(thisLine, &firstFrame, &lineFrameCount, usedRect, &lineFlags);
6572
6573 nsIFrame* frame = firstFrame;
6574 for (int32_t count = lineFrameCount; count;
6575 --count, frame = frame->GetNextSibling()) {
6576 if (!frame->IsGeneratedContentFrame()) {
6577 baseFrame = frame;
6578 if (!endOfLine)
6579 break;
6580 }
6581 }
6582 }
6583 if (!baseFrame)
6584 return NS_ERROR_FAILURE;
6585 FrameTarget targetFrame = DrillDownToSelectionFrame(baseFrame,
6586 endOfLine, 0);
6587 FrameContentRange range = GetRangeForFrame(targetFrame.frame);
6588 aPos->mResultContent = range.content;
6589 aPos->mContentOffset = endOfLine ? range.end : range.start;
6590 if (endOfLine && targetFrame.frame->HasSignificantTerminalNewline()) {
6591 // Do not position the caret after the terminating newline if we're
6592 // trying to move to the end of line (see bug 596506)
6593 --aPos->mContentOffset;
6594 }
6595 aPos->mResultFrame = targetFrame.frame;
6596 aPos->mAttachForward = (aPos->mContentOffset == range.start);
6597 if (!range.content)
6598 return NS_ERROR_FAILURE;
6599 return NS_OK;
6600 }
6601
6602 default:
6603 {
6604 NS_ASSERTION(false, "Invalid amount");
6605 return NS_ERROR_FAILURE;
6606 }
6607 }
6608 return NS_OK;
6609 }
6610
6611 nsIFrame::FrameSearchResult
6612 nsFrame::PeekOffsetNoAmount(bool aForward, int32_t* aOffset)
6613 {
6614 NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
6615 // Sure, we can stop right here.
6616 return FOUND;
6617 }
6618
6619 nsIFrame::FrameSearchResult
6620 nsFrame::PeekOffsetCharacter(bool aForward, int32_t* aOffset,
6621 bool aRespectClusters)
6622 {
6623 NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
6624 int32_t startOffset = *aOffset;
6625 // A negative offset means "end of frame", which in our case means offset 1.
6626 if (startOffset < 0)
6627 startOffset = 1;
6628 if (aForward == (startOffset == 0)) {
6629 // We're before the frame and moving forward, or after it and moving backwards:
6630 // skip to the other side and we're done.
6631 *aOffset = 1 - startOffset;
6632 return FOUND;
6633 }
6634 return CONTINUE;
6635 }
6636
6637 nsIFrame::FrameSearchResult
6638 nsFrame::PeekOffsetWord(bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect,
6639 int32_t* aOffset, PeekWordState* aState)
6640 {
6641 NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
6642 int32_t startOffset = *aOffset;
6643 // This isn't text, so truncate the context
6644 aState->mContext.Truncate();
6645 if (startOffset < 0)
6646 startOffset = 1;
6647 if (aForward == (startOffset == 0)) {
6648 // We're before the frame and moving forward, or after it and moving backwards.
6649 // If we're looking for non-whitespace, we found it (without skipping this frame).
6650 if (!aState->mAtStart) {
6651 if (aState->mLastCharWasPunctuation) {
6652 // We're not punctuation, so this is a punctuation boundary.
6653 if (BreakWordBetweenPunctuation(aState, aForward, false, false, aIsKeyboardSelect))
6654 return FOUND;
6655 } else {
6656 // This is not a punctuation boundary.
6657 if (aWordSelectEatSpace && aState->mSawBeforeType)
6658 return FOUND;
6659 }
6660 }
6661 // Otherwise skip to the other side and note that we encountered non-whitespace.
6662 *aOffset = 1 - startOffset;
6663 aState->Update(false, // not punctuation
6664 false // not whitespace
6665 );
6666 if (!aWordSelectEatSpace)
6667 aState->SetSawBeforeType();
6668 }
6669 return CONTINUE;
6670 }
6671
6672 bool
6673 nsFrame::BreakWordBetweenPunctuation(const PeekWordState* aState,
6674 bool aForward,
6675 bool aPunctAfter, bool aWhitespaceAfter,
6676 bool aIsKeyboardSelect)
6677 {
6678 NS_ASSERTION(aPunctAfter != aState->mLastCharWasPunctuation,
6679 "Call this only at punctuation boundaries");
6680 if (aState->mLastCharWasWhitespace) {
6681 // We always stop between whitespace and punctuation
6682 return true;
6683 }
6684 if (!Preferences::GetBool("layout.word_select.stop_at_punctuation")) {
6685 // When this pref is false, we never stop at a punctuation boundary unless
6686 // it's followed by whitespace (in the relevant direction).
6687 return aWhitespaceAfter;
6688 }
6689 if (!aIsKeyboardSelect) {
6690 // mouse caret movement (e.g. word selection) always stops at every punctuation boundary
6691 return true;
6692 }
6693 bool afterPunct = aForward ? aState->mLastCharWasPunctuation : aPunctAfter;
6694 if (!afterPunct) {
6695 // keyboard caret movement only stops after punctuation (in content order)
6696 return false;
6697 }
6698 // Stop only if we've seen some non-punctuation since the last whitespace;
6699 // don't stop after punctuation that follows whitespace.
6700 return aState->mSeenNonPunctuationSinceWhitespace;
6701 }
6702
6703 nsresult
6704 nsFrame::CheckVisibility(nsPresContext* , int32_t , int32_t , bool , bool *, bool *)
6705 {
6706 return NS_ERROR_NOT_IMPLEMENTED;
6707 }
6708
6709
6710 int32_t
6711 nsFrame::GetLineNumber(nsIFrame *aFrame, bool aLockScroll, nsIFrame** aContainingBlock)
6712 {
6713 NS_ASSERTION(aFrame, "null aFrame");
6714 nsFrameManager* frameManager = aFrame->PresContext()->FrameManager();
6715 nsIFrame *blockFrame = aFrame;
6716 nsIFrame *thisBlock;
6717 nsAutoLineIterator it;
6718 nsresult result = NS_ERROR_FAILURE;
6719 while (NS_FAILED(result) && blockFrame)
6720 {
6721 thisBlock = blockFrame;
6722 if (thisBlock->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
6723 //if we are searching for a frame that is not in flow we will not find it.
6724 //we must instead look for its placeholder
6725 if (thisBlock->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
6726 // abspos continuations don't have placeholders, get the fif
6727 thisBlock = thisBlock->FirstInFlow();
6728 }
6729 thisBlock = frameManager->GetPlaceholderFrameFor(thisBlock);
6730 if (!thisBlock)
6731 return -1;
6732 }
6733 blockFrame = thisBlock->GetParent();
6734 result = NS_OK;
6735 if (blockFrame) {
6736 if (aLockScroll && blockFrame->GetType() == nsGkAtoms::scrollFrame)
6737 return -1;
6738 it = blockFrame->GetLineIterator();
6739 if (!it)
6740 result = NS_ERROR_FAILURE;
6741 }
6742 }
6743 if (!blockFrame || !it)
6744 return -1;
6745
6746 if (aContainingBlock)
6747 *aContainingBlock = blockFrame;
6748 return it->FindLineContaining(thisBlock);
6749 }
6750
6751 nsresult
6752 nsIFrame::GetFrameFromDirection(nsDirection aDirection, bool aVisual,
6753 bool aJumpLines, bool aScrollViewStop,
6754 nsIFrame** aOutFrame, int32_t* aOutOffset, bool* aOutJumpedLine)
6755 {
6756 nsresult result;
6757
6758 if (!aOutFrame || !aOutOffset || !aOutJumpedLine)
6759 return NS_ERROR_NULL_POINTER;
6760
6761 nsPresContext* presContext = PresContext();
6762 *aOutFrame = nullptr;
6763 *aOutOffset = 0;
6764 *aOutJumpedLine = false;
6765
6766 // Find the prev/next selectable frame
6767 bool selectable = false;
6768 nsIFrame *traversedFrame = this;
6769 while (!selectable) {
6770 nsIFrame *blockFrame;
6771
6772 int32_t thisLine = nsFrame::GetLineNumber(traversedFrame, aScrollViewStop, &blockFrame);
6773 if (thisLine < 0)
6774 return NS_ERROR_FAILURE;
6775
6776 nsAutoLineIterator it = blockFrame->GetLineIterator();
6777 NS_ASSERTION(it, "GetLineNumber() succeeded but no block frame?");
6778
6779 bool atLineEdge;
6780 nsIFrame *firstFrame;
6781 nsIFrame *lastFrame;
6782 if (aVisual && presContext->BidiEnabled()) {
6783 bool lineIsRTL = it->GetDirection();
6784 bool isReordered;
6785 result = it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame);
6786 nsIFrame** framePtr = aDirection == eDirPrevious ? &firstFrame : &lastFrame;
6787 if (*framePtr) {
6788 nsBidiLevel embeddingLevel = nsBidiPresUtils::GetFrameEmbeddingLevel(*framePtr);
6789 if ((((embeddingLevel & 1) && lineIsRTL) || (!(embeddingLevel & 1) && !lineIsRTL)) ==
6790 (aDirection == eDirPrevious)) {
6791 nsFrame::GetFirstLeaf(presContext, framePtr);
6792 } else {
6793 nsFrame::GetLastLeaf(presContext, framePtr);
6794 }
6795 atLineEdge = *framePtr == traversedFrame;
6796 } else {
6797 atLineEdge = true;
6798 }
6799 } else {
6800 nsRect nonUsedRect;
6801 int32_t lineFrameCount;
6802 uint32_t lineFlags;
6803 result = it->GetLine(thisLine, &firstFrame, &lineFrameCount,nonUsedRect,
6804 &lineFlags);
6805 if (NS_FAILED(result))
6806 return result;
6807
6808 if (aDirection == eDirPrevious) {
6809 nsFrame::GetFirstLeaf(presContext, &firstFrame);
6810 atLineEdge = firstFrame == traversedFrame;
6811 } else { // eDirNext
6812 lastFrame = firstFrame;
6813 for (;lineFrameCount > 1;lineFrameCount --){
6814 result = it->GetNextSiblingOnLine(lastFrame, thisLine);
6815 if (NS_FAILED(result) || !lastFrame){
6816 NS_ERROR("should not be reached nsFrame");
6817 return NS_ERROR_FAILURE;
6818 }
6819 }
6820 nsFrame::GetLastLeaf(presContext, &lastFrame);
6821 atLineEdge = lastFrame == traversedFrame;
6822 }
6823 }
6824
6825 if (atLineEdge) {
6826 *aOutJumpedLine = true;
6827 if (!aJumpLines)
6828 return NS_ERROR_FAILURE; //we are done. cannot jump lines
6829 }
6830
6831 nsCOMPtr<nsIFrameEnumerator> frameTraversal;
6832 result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
6833 presContext, traversedFrame,
6834 eLeaf,
6835 aVisual && presContext->BidiEnabled(),
6836 aScrollViewStop,
6837 true // aFollowOOFs
6838 );
6839 if (NS_FAILED(result))
6840 return result;
6841
6842 if (aDirection == eDirNext)
6843 frameTraversal->Next();
6844 else
6845 frameTraversal->Prev();
6846
6847 traversedFrame = frameTraversal->CurrentItem();
6848 if (!traversedFrame)
6849 return NS_ERROR_FAILURE;
6850 traversedFrame->IsSelectable(&selectable, nullptr);
6851 } // while (!selectable)
6852
6853 *aOutOffset = (aDirection == eDirNext) ? 0 : -1;
6854
6855 if (aVisual) {
6856 uint8_t newLevel = NS_GET_EMBEDDING_LEVEL(traversedFrame);
6857 uint8_t newBaseLevel = NS_GET_BASE_LEVEL(traversedFrame);
6858 if ((newLevel & 1) != (newBaseLevel & 1)) // The new frame is reverse-direction, go to the other end
6859 *aOutOffset = -1 - *aOutOffset;
6860 }
6861 *aOutFrame = traversedFrame;
6862 return NS_OK;
6863 }
6864
6865 nsView* nsIFrame::GetClosestView(nsPoint* aOffset) const
6866 {
6867 nsPoint offset(0,0);
6868 for (const nsIFrame *f = this; f; f = f->GetParent()) {
6869 if (f->HasView()) {
6870 if (aOffset)
6871 *aOffset = offset;
6872 return f->GetView();
6873 }
6874 offset += f->GetPosition();
6875 }
6876
6877 NS_NOTREACHED("No view on any parent? How did that happen?");
6878 return nullptr;
6879 }
6880
6881
6882 /* virtual */ void
6883 nsFrame::ChildIsDirty(nsIFrame* aChild)
6884 {
6885 NS_NOTREACHED("should never be called on a frame that doesn't inherit from "
6886 "nsContainerFrame");
6887 }
6888
6889
6890 #ifdef ACCESSIBILITY
6891 a11y::AccType
6892 nsFrame::AccessibleType()
6893 {
6894 return a11y::eNoType;
6895 }
6896 #endif
6897
6898 NS_DECLARE_FRAME_PROPERTY(OverflowAreasProperty,
6899 nsIFrame::DestroyOverflowAreas)
6900
6901 bool
6902 nsIFrame::ClearOverflowRects()
6903 {
6904 if (mOverflow.mType == NS_FRAME_OVERFLOW_NONE) {
6905 return false;
6906 }
6907 if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
6908 Properties().Delete(OverflowAreasProperty());
6909 }
6910 mOverflow.mType = NS_FRAME_OVERFLOW_NONE;
6911 return true;
6912 }
6913
6914 /** Create or retrieve the previously stored overflow area, if the frame does
6915 * not overflow and no creation is required return nullptr.
6916 * @return pointer to the overflow area rectangle
6917 */
6918 nsOverflowAreas*
6919 nsIFrame::GetOverflowAreasProperty()
6920 {
6921 FrameProperties props = Properties();
6922 nsOverflowAreas *overflow =
6923 static_cast<nsOverflowAreas*>(props.Get(OverflowAreasProperty()));
6924
6925 if (overflow) {
6926 return overflow; // the property already exists
6927 }
6928
6929 // The property isn't set yet, so allocate a new rect, set the property,
6930 // and return the newly allocated rect
6931 overflow = new nsOverflowAreas;
6932 props.Set(OverflowAreasProperty(), overflow);
6933 return overflow;
6934 }
6935
6936 /** Set the overflowArea rect, storing it as deltas or a separate rect
6937 * depending on its size in relation to the primary frame rect.
6938 */
6939 bool
6940 nsIFrame::SetOverflowAreas(const nsOverflowAreas& aOverflowAreas)
6941 {
6942 if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
6943 nsOverflowAreas *overflow =
6944 static_cast<nsOverflowAreas*>(Properties().Get(OverflowAreasProperty()));
6945 bool changed = *overflow != aOverflowAreas;
6946 *overflow = aOverflowAreas;
6947
6948 // Don't bother with converting to the deltas form if we already
6949 // have a property.
6950 return changed;
6951 }
6952
6953 const nsRect& vis = aOverflowAreas.VisualOverflow();
6954 uint32_t l = -vis.x, // left edge: positive delta is leftwards
6955 t = -vis.y, // top: positive is upwards
6956 r = vis.XMost() - mRect.width, // right: positive is rightwards
6957 b = vis.YMost() - mRect.height; // bottom: positive is downwards
6958 if (aOverflowAreas.ScrollableOverflow().IsEqualEdges(nsRect(nsPoint(0, 0), GetSize())) &&
6959 l <= NS_FRAME_OVERFLOW_DELTA_MAX &&
6960 t <= NS_FRAME_OVERFLOW_DELTA_MAX &&
6961 r <= NS_FRAME_OVERFLOW_DELTA_MAX &&
6962 b <= NS_FRAME_OVERFLOW_DELTA_MAX &&
6963 // we have to check these against zero because we *never* want to
6964 // set a frame as having no overflow in this function. This is
6965 // because FinishAndStoreOverflow calls this function prior to
6966 // SetRect based on whether the overflow areas match aNewSize.
6967 // In the case where the overflow areas exactly match mRect but
6968 // do not match aNewSize, we need to store overflow in a property
6969 // so that our eventual SetRect/SetSize will know that it has to
6970 // reset our overflow areas.
6971 (l | t | r | b) != 0) {
6972 VisualDeltas oldDeltas = mOverflow.mVisualDeltas;
6973 // It's a "small" overflow area so we store the deltas for each edge
6974 // directly in the frame, rather than allocating a separate rect.
6975 // If they're all zero, that's fine; we're setting things to
6976 // no-overflow.
6977 mOverflow.mVisualDeltas.mLeft = l;
6978 mOverflow.mVisualDeltas.mTop = t;
6979 mOverflow.mVisualDeltas.mRight = r;
6980 mOverflow.mVisualDeltas.mBottom = b;
6981 // There was no scrollable overflow before, and there isn't now.
6982 return oldDeltas != mOverflow.mVisualDeltas;
6983 } else {
6984 bool changed = !aOverflowAreas.ScrollableOverflow().IsEqualEdges(nsRect(nsPoint(0, 0), GetSize())) ||
6985 !aOverflowAreas.VisualOverflow().IsEqualEdges(GetVisualOverflowFromDeltas());
6986
6987 // it's a large overflow area that we need to store as a property
6988 mOverflow.mType = NS_FRAME_OVERFLOW_LARGE;
6989 nsOverflowAreas* overflow = GetOverflowAreasProperty();
6990 NS_ASSERTION(overflow, "should have created areas");
6991 *overflow = aOverflowAreas;
6992 return changed;
6993 }
6994 }
6995
6996 inline bool
6997 IsInlineFrame(nsIFrame *aFrame)
6998 {
6999 nsIAtom *type = aFrame->GetType();
7000 return type == nsGkAtoms::inlineFrame;
7001 }
7002
7003 /**
7004 * Compute the union of the border boxes of aFrame and its descendants,
7005 * in aFrame's coordinate space (if aApplyTransform is false) or its
7006 * post-transform coordinate space (if aApplyTransform is true).
7007 */
7008 static nsRect
7009 UnionBorderBoxes(nsIFrame* aFrame, bool aApplyTransform,
7010 const nsSize* aSizeOverride = nullptr,
7011 const nsOverflowAreas* aOverflowOverride = nullptr)
7012 {
7013 const nsRect bounds(nsPoint(0, 0),
7014 aSizeOverride ? *aSizeOverride : aFrame->GetSize());
7015
7016 // Start from our border-box, transformed. See comment below about
7017 // transform of children.
7018 nsRect u;
7019 bool doTransform = aApplyTransform && aFrame->IsTransformed();
7020 if (doTransform) {
7021 u = nsDisplayTransform::TransformRect(bounds, aFrame,
7022 nsPoint(0, 0), &bounds);
7023 } else {
7024 u = bounds;
7025 }
7026
7027 // Only iterate through the children if the overflow areas suggest
7028 // that we might need to, and if the frame doesn't clip its overflow
7029 // anyway.
7030 if (aOverflowOverride) {
7031 if (!doTransform &&
7032 bounds.IsEqualEdges(aOverflowOverride->VisualOverflow()) &&
7033 bounds.IsEqualEdges(aOverflowOverride->ScrollableOverflow())) {
7034 return u;
7035 }
7036 } else {
7037 if (!doTransform &&
7038 bounds.IsEqualEdges(aFrame->GetVisualOverflowRect()) &&
7039 bounds.IsEqualEdges(aFrame->GetScrollableOverflowRect())) {
7040 return u;
7041 }
7042 }
7043 const nsStyleDisplay* disp = aFrame->StyleDisplay();
7044 nsIAtom* fType = aFrame->GetType();
7045 if (nsFrame::ShouldApplyOverflowClipping(aFrame, disp) ||
7046 fType == nsGkAtoms::scrollFrame ||
7047 fType == nsGkAtoms::svgOuterSVGFrame) {
7048 return u;
7049 }
7050
7051 nsRect clipPropClipRect;
7052 bool hasClipPropClip =
7053 aFrame->GetClipPropClipRect(disp, &clipPropClipRect, bounds.Size());
7054
7055 // Iterate over all children except pop-ups.
7056 const nsIFrame::ChildListIDs skip(nsIFrame::kPopupList |
7057 nsIFrame::kSelectPopupList);
7058 for (nsIFrame::ChildListIterator childLists(aFrame);
7059 !childLists.IsDone(); childLists.Next()) {
7060 if (skip.Contains(childLists.CurrentID())) {
7061 continue;
7062 }
7063
7064 nsFrameList children = childLists.CurrentList();
7065 for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
7066 nsIFrame* child = e.get();
7067 // Note that passing |true| for aApplyTransform when
7068 // child->Preserves3D() is incorrect if our aApplyTransform is
7069 // false... but the opposite would be as well. This is because
7070 // elements within a preserve-3d scene are always transformed up
7071 // to the top of the scene. This means we don't have a
7072 // mechanism for getting a transform up to an intermediate point
7073 // within the scene. We choose to over-transform rather than
7074 // under-transform because this is consistent with other
7075 // overflow areas.
7076 nsRect childRect = UnionBorderBoxes(child, true) +
7077 child->GetPosition();
7078
7079 if (hasClipPropClip) {
7080 // Intersect with the clip before transforming.
7081 childRect.IntersectRect(childRect, clipPropClipRect);
7082 }
7083
7084 // Note that we transform each child separately according to
7085 // aFrame's transform, and then union, which gives a different
7086 // (smaller) result from unioning and then transforming the
7087 // union. This doesn't match the way we handle overflow areas
7088 // with 2-D transforms, though it does match the way we handle
7089 // overflow areas in preserve-3d 3-D scenes.
7090 if (doTransform && !child->Preserves3D()) {
7091 childRect = nsDisplayTransform::TransformRect(childRect, aFrame,
7092 nsPoint(0, 0), &bounds);
7093 }
7094 u.UnionRectEdges(u, childRect);
7095 }
7096 }
7097
7098 return u;
7099 }
7100
7101 static void
7102 ComputeAndIncludeOutlineArea(nsIFrame* aFrame, nsOverflowAreas& aOverflowAreas,
7103 const nsSize& aNewSize)
7104 {
7105 const nsStyleOutline* outline = aFrame->StyleOutline();
7106 if (outline->GetOutlineStyle() == NS_STYLE_BORDER_STYLE_NONE) {
7107 return;
7108 }
7109
7110 nscoord width;
7111 DebugOnly<bool> result = outline->GetOutlineWidth(width);
7112 NS_ASSERTION(result, "GetOutlineWidth had no cached outline width");
7113 if (width <= 0) {
7114 return;
7115 }
7116
7117 // When the outline property is set on :-moz-anonymous-block or
7118 // :-moz-anonymous-positioned-block pseudo-elements, it inherited
7119 // that outline from the inline that was broken because it
7120 // contained a block. In that case, we don't want a really wide
7121 // outline if the block inside the inline is narrow, so union the
7122 // actual contents of the anonymous blocks.
7123 nsIFrame *frameForArea = aFrame;
7124 do {
7125 nsIAtom *pseudoType = frameForArea->StyleContext()->GetPseudo();
7126 if (pseudoType != nsCSSAnonBoxes::mozAnonymousBlock &&
7127 pseudoType != nsCSSAnonBoxes::mozAnonymousPositionedBlock)
7128 break;
7129 // If we're done, we really want it and all its later siblings.
7130 frameForArea = frameForArea->GetFirstPrincipalChild();
7131 NS_ASSERTION(frameForArea, "anonymous block with no children?");
7132 } while (frameForArea);
7133
7134 // Find the union of the border boxes of all descendants, or in
7135 // the block-in-inline case, all descendants we care about.
7136 //
7137 // Note that the interesting perspective-related cases are taken
7138 // care of by the code that handles those issues for overflow
7139 // calling FinishAndStoreOverflow again, which in turn calls this
7140 // function again. We still need to deal with preserve-3d a bit.
7141 nsRect innerRect;
7142 if (frameForArea == aFrame) {
7143 innerRect = UnionBorderBoxes(aFrame, false, &aNewSize, &aOverflowAreas);
7144 } else {
7145 for (; frameForArea; frameForArea = frameForArea->GetNextSibling()) {
7146 nsRect r(UnionBorderBoxes(frameForArea, true));
7147
7148 // Adjust for offsets transforms up to aFrame's pre-transform
7149 // (i.e., normal) coordinate space; see comments in
7150 // UnionBorderBoxes for some of the subtlety here.
7151 for (nsIFrame *f = frameForArea, *parent = f->GetParent();
7152 /* see middle of loop */;
7153 f = parent, parent = f->GetParent()) {
7154 r += f->GetPosition();
7155 if (parent == aFrame) {
7156 break;
7157 }
7158 if (parent->IsTransformed() && !f->Preserves3D()) {
7159 r = nsDisplayTransform::TransformRect(r, parent, nsPoint(0, 0));
7160 }
7161 }
7162
7163 innerRect.UnionRect(innerRect, r);
7164 }
7165 }
7166
7167 aFrame->Properties().Set(nsIFrame::OutlineInnerRectProperty(),
7168 new nsRect(innerRect));
7169
7170 nscoord offset = outline->mOutlineOffset;
7171 nscoord inflateBy = std::max(width + offset, 0);
7172
7173 // Keep this code (and the storing of properties just above) in
7174 // sync with GetOutlineInnerRect in nsCSSRendering.cpp.
7175 nsRect outerRect(innerRect);
7176 outerRect.Inflate(inflateBy, inflateBy);
7177
7178 nsRect& vo = aOverflowAreas.VisualOverflow();
7179 vo.UnionRectEdges(vo, outerRect);
7180 }
7181
7182 bool
7183 nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
7184 nsSize aNewSize, nsSize* aOldSize)
7185 {
7186 NS_ASSERTION(FrameMaintainsOverflow(this),
7187 "Don't call - overflow rects not maintained on these SVG frames");
7188
7189 nsRect bounds(nsPoint(0, 0), aNewSize);
7190 // Store the passed in overflow area if we are a preserve-3d frame or we have
7191 // a transform, and it's not just the frame bounds.
7192 if (Preserves3D() || HasPerspective() || IsTransformed()) {
7193 if (!aOverflowAreas.VisualOverflow().IsEqualEdges(bounds) ||
7194 !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) {
7195
7196 nsOverflowAreas* initial =
7197 static_cast<nsOverflowAreas*>(Properties().Get(nsIFrame::InitialOverflowProperty()));
7198 if (!initial) {
7199 Properties().Set(nsIFrame::InitialOverflowProperty(),
7200 new nsOverflowAreas(aOverflowAreas));
7201 } else if (initial != &aOverflowAreas) {
7202 *initial = aOverflowAreas;
7203 }
7204 }
7205 #ifdef DEBUG
7206 Properties().Set(nsIFrame::DebugInitialOverflowPropertyApplied(), nullptr);
7207 #endif
7208 } else {
7209 #ifdef DEBUG
7210 Properties().Delete(nsIFrame::DebugInitialOverflowPropertyApplied());
7211 #endif
7212 }
7213
7214 // This is now called FinishAndStoreOverflow() instead of
7215 // StoreOverflow() because frame-generic ways of adding overflow
7216 // can happen here, e.g. CSS2 outline and native theme.
7217 // If the overflow area width or height is nscoord_MAX, then a
7218 // saturating union may have encounted an overflow, so the overflow may not
7219 // contain the frame border-box. Don't warn in that case.
7220 // Don't warn for SVG either, since SVG doesn't need the overflow area
7221 // to contain the frame bounds.
7222 NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
7223 DebugOnly<nsRect*> r = &aOverflowAreas.Overflow(otype);
7224 NS_ASSERTION(aNewSize.width == 0 || aNewSize.height == 0 ||
7225 r->width == nscoord_MAX || r->height == nscoord_MAX ||
7226 (mState & NS_FRAME_SVG_LAYOUT) ||
7227 r->Contains(nsRect(nsPoint(0,0), aNewSize)),
7228 "Computed overflow area must contain frame bounds");
7229 }
7230
7231 // If we clip our children, clear accumulated overflow area. The
7232 // children are actually clipped to the padding-box, but since the
7233 // overflow area should include the entire border-box, just set it to
7234 // the border-box here.
7235 const nsStyleDisplay* disp = StyleDisplay();
7236 NS_ASSERTION((disp->mOverflowY == NS_STYLE_OVERFLOW_CLIP) ==
7237 (disp->mOverflowX == NS_STYLE_OVERFLOW_CLIP),
7238 "If one overflow is clip, the other should be too");
7239 if (nsFrame::ShouldApplyOverflowClipping(this, disp)) {
7240 // The contents are actually clipped to the padding area
7241 aOverflowAreas.SetAllTo(bounds);
7242 }
7243
7244 // Overflow area must always include the frame's top-left and bottom-right,
7245 // even if the frame rect is empty (so we can scroll to those positions).
7246 // Pending a real fix for bug 426879, don't do this for inline frames
7247 // with zero width.
7248 // Do not do this for SVG either, since it will usually massively increase
7249 // the area unnecessarily.
7250 if ((aNewSize.width != 0 || !IsInlineFrame(this)) &&
7251 !(GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
7252 NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
7253 nsRect& o = aOverflowAreas.Overflow(otype);
7254 o.UnionRectEdges(o, bounds);
7255 }
7256 }
7257
7258 // Note that NS_STYLE_OVERFLOW_CLIP doesn't clip the frame background,
7259 // so we add theme background overflow here so it's not clipped.
7260 if (!IsBoxWrapped() && IsThemed(disp)) {
7261 nsRect r(bounds);
7262 nsPresContext *presContext = PresContext();
7263 if (presContext->GetTheme()->
7264 GetWidgetOverflow(presContext->DeviceContext(), this,
7265 disp->mAppearance, &r)) {
7266 nsRect& vo = aOverflowAreas.VisualOverflow();
7267 vo.UnionRectEdges(vo, r);
7268 }
7269 }
7270
7271 ComputeAndIncludeOutlineArea(this, aOverflowAreas, aNewSize);
7272
7273 // Nothing in here should affect scrollable overflow.
7274 aOverflowAreas.VisualOverflow() =
7275 ComputeEffectsRect(this, aOverflowAreas.VisualOverflow(), aNewSize);
7276
7277 // Absolute position clipping
7278 nsRect clipPropClipRect;
7279 bool hasClipPropClip = GetClipPropClipRect(disp, &clipPropClipRect, aNewSize);
7280 if (hasClipPropClip) {
7281 NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
7282 nsRect& o = aOverflowAreas.Overflow(otype);
7283 o.IntersectRect(o, clipPropClipRect);
7284 }
7285 }
7286
7287 /* If we're transformed, transform the overflow rect by the current transformation. */
7288 bool hasTransform = IsTransformed();
7289 nsSize oldSize = aOldSize ? *aOldSize : mRect.Size();
7290 bool sizeChanged = (oldSize != aNewSize);
7291 if (hasTransform) {
7292 Properties().Set(nsIFrame::PreTransformOverflowAreasProperty(),
7293 new nsOverflowAreas(aOverflowAreas));
7294 /* Since our size might not actually have been computed yet, we need to make sure that we use the
7295 * correct dimensions by overriding the stored bounding rectangle with the value the caller has
7296 * ensured us we'll use.
7297 */
7298 nsRect newBounds(nsPoint(0, 0), aNewSize);
7299 // Transform affects both overflow areas.
7300 NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
7301 nsRect& o = aOverflowAreas.Overflow(otype);
7302 o = nsDisplayTransform::TransformRect(o, this, nsPoint(0, 0), &newBounds);
7303 }
7304 if (Preserves3DChildren()) {
7305 ComputePreserve3DChildrenOverflow(aOverflowAreas, newBounds);
7306 } else if (sizeChanged && ChildrenHavePerspective()) {
7307 RecomputePerspectiveChildrenOverflow(this->StyleContext(), &newBounds);
7308 }
7309 } else {
7310 Properties().Delete(nsIFrame::PreTransformOverflowAreasProperty());
7311 if (ChildrenHavePerspective() && sizeChanged) {
7312 nsRect newBounds(nsPoint(0, 0), aNewSize);
7313 RecomputePerspectiveChildrenOverflow(this->StyleContext(), &newBounds);
7314 }
7315 }
7316
7317 bool anyOverflowChanged;
7318 if (aOverflowAreas != nsOverflowAreas(bounds, bounds)) {
7319 anyOverflowChanged = SetOverflowAreas(aOverflowAreas);
7320 } else {
7321 anyOverflowChanged = ClearOverflowRects();
7322 }
7323
7324 if (anyOverflowChanged) {
7325 nsSVGEffects::InvalidateDirectRenderingObservers(this);
7326 }
7327 return anyOverflowChanged;
7328 }
7329
7330 void
7331 nsIFrame::RecomputePerspectiveChildrenOverflow(const nsStyleContext* aStartStyle, const nsRect* aBounds)
7332 {
7333 // Children may check our size when getting our transform, make sure it's valid.
7334 nsSize oldSize = GetSize();
7335 if (aBounds) {
7336 SetSize(aBounds->Size());
7337 }
7338 nsIFrame::ChildListIterator lists(this);
7339 for (; !lists.IsDone(); lists.Next()) {
7340 nsFrameList::Enumerator childFrames(lists.CurrentList());
7341 for (; !childFrames.AtEnd(); childFrames.Next()) {
7342 nsIFrame* child = childFrames.get();
7343 if (!FrameMaintainsOverflow(child)) {
7344 continue; // frame does not maintain overflow rects
7345 }
7346 if (child->HasPerspective()) {
7347 nsOverflowAreas* overflow =
7348 static_cast<nsOverflowAreas*>(child->Properties().Get(nsIFrame::InitialOverflowProperty()));
7349 nsRect bounds(nsPoint(0, 0), child->GetSize());
7350 if (overflow) {
7351 nsOverflowAreas overflowCopy = *overflow;
7352 child->FinishAndStoreOverflow(overflowCopy, bounds.Size());
7353 } else {
7354 nsOverflowAreas boundsOverflow;
7355 boundsOverflow.SetAllTo(bounds);
7356 child->FinishAndStoreOverflow(boundsOverflow, bounds.Size());
7357 }
7358 } else if (child->StyleContext()->GetParent() == aStartStyle ||
7359 child->StyleContext() == aStartStyle) {
7360 // If a frame is using perspective, then the size used to compute
7361 // perspective-origin is the size of the frame belonging to its parent
7362 // style context. We must find any descendant frames using our size
7363 // (by recurse into frames with the same style context, or a direct
7364 // child style context) to update their overflow rects too.
7365 child->RecomputePerspectiveChildrenOverflow(aStartStyle, nullptr);
7366 }
7367 }
7368 }
7369 // Restore our old size just in case something depends on this elesewhere.
7370 SetSize(oldSize);
7371 }
7372
7373 /* The overflow rects for leaf nodes in a preserve-3d hierarchy depends on
7374 * the mRect value for their parents (since we use their transform, and transform
7375 * depends on this for transform-origin etc). These weren't necessarily correct
7376 * when we reflowed initially, so walk over all preserve-3d children and repeat the
7377 * overflow calculation.
7378 */
7379 static void
7380 RecomputePreserve3DChildrenOverflow(nsIFrame* aFrame, const nsRect* aBounds)
7381 {
7382 // Children may check our size when getting our transform, make sure it's valid.
7383 nsSize oldSize = aFrame->GetSize();
7384 if (aBounds) {
7385 aFrame->SetSize(aBounds->Size());
7386 }
7387 nsIFrame::ChildListIterator lists(aFrame);
7388 for (; !lists.IsDone(); lists.Next()) {
7389 nsFrameList::Enumerator childFrames(lists.CurrentList());
7390 for (; !childFrames.AtEnd(); childFrames.Next()) {
7391 nsIFrame* child = childFrames.get();
7392 if (!FrameMaintainsOverflow(child)) {
7393 continue; // frame does not maintain overflow rects
7394 }
7395 if (child->Preserves3DChildren()) {
7396 RecomputePreserve3DChildrenOverflow(child, nullptr);
7397 } else if (child->Preserves3D()) {
7398 nsOverflowAreas* overflow =
7399 static_cast<nsOverflowAreas*>(child->Properties().Get(nsIFrame::InitialOverflowProperty()));
7400 nsRect bounds(nsPoint(0, 0), child->GetSize());
7401 if (overflow) {
7402 nsOverflowAreas overflowCopy = *overflow;
7403 child->FinishAndStoreOverflow(overflowCopy, bounds.Size());
7404 } else {
7405 nsOverflowAreas boundsOverflow;
7406 boundsOverflow.SetAllTo(bounds);
7407 child->FinishAndStoreOverflow(boundsOverflow, bounds.Size());
7408 }
7409 }
7410 }
7411 }
7412 // Restore our old size just in case something depends on this elesewhere.
7413 aFrame->SetSize(oldSize);
7414
7415 // Only repeat computing our overflow in recursive calls since the initial caller is still
7416 // in the middle of doing this and we don't want an infinite loop.
7417 if (!aBounds) {
7418 nsOverflowAreas* overflow =
7419 static_cast<nsOverflowAreas*>(aFrame->Properties().Get(nsIFrame::InitialOverflowProperty()));
7420 nsRect bounds(nsPoint(0, 0), aFrame->GetSize());
7421 if (overflow) {
7422 nsOverflowAreas overflowCopy = *overflow;
7423 overflowCopy.UnionAllWith(bounds);
7424 aFrame->FinishAndStoreOverflow(overflowCopy, bounds.Size());
7425 } else {
7426 nsOverflowAreas boundsOverflow;
7427 boundsOverflow.SetAllTo(bounds);
7428 aFrame->FinishAndStoreOverflow(boundsOverflow, bounds.Size());
7429 }
7430 }
7431 }
7432
7433 void
7434 nsIFrame::ComputePreserve3DChildrenOverflow(nsOverflowAreas& aOverflowAreas, const nsRect& aBounds)
7435 {
7436 // When we are preserving 3d we need to iterate over all children separately.
7437 // If the child also preserves 3d then their overflow will already been in our
7438 // coordinate space, otherwise we need to transform.
7439
7440 // If we're the top frame in a preserve 3d chain then we need to recalculate the overflow
7441 // areas of all our children since they will have used our size/offset which was invalid at
7442 // the time.
7443 if (!Preserves3D()) {
7444 RecomputePreserve3DChildrenOverflow(this, &aBounds);
7445 }
7446
7447 nsRect childVisual;
7448 nsRect childScrollable;
7449 nsIFrame::ChildListIterator lists(this);
7450 for (; !lists.IsDone(); lists.Next()) {
7451 nsFrameList::Enumerator childFrames(lists.CurrentList());
7452 for (; !childFrames.AtEnd(); childFrames.Next()) {
7453 nsIFrame* child = childFrames.get();
7454 nsPoint offset = child->GetPosition();
7455 nsRect visual = child->GetVisualOverflowRect();
7456 nsRect scrollable = child->GetScrollableOverflowRect();
7457 visual.MoveBy(offset);
7458 scrollable.MoveBy(offset);
7459 if (child->Preserves3D()) {
7460 childVisual = childVisual.Union(visual);
7461 childScrollable = childScrollable.Union(scrollable);
7462 } else {
7463 childVisual =
7464 childVisual.Union(nsDisplayTransform::TransformRect(visual,
7465 this, nsPoint(0,0), &aBounds));
7466 childScrollable =
7467 childScrollable.Union(nsDisplayTransform::TransformRect(scrollable,
7468 this, nsPoint(0,0), &aBounds));
7469 }
7470 }
7471 }
7472
7473 aOverflowAreas.Overflow(eVisualOverflow) = aOverflowAreas.Overflow(eVisualOverflow).Union(childVisual);
7474 aOverflowAreas.Overflow(eScrollableOverflow) = aOverflowAreas.Overflow(eScrollableOverflow).Union(childScrollable);
7475 }
7476
7477 void
7478 nsFrame::ConsiderChildOverflow(nsOverflowAreas& aOverflowAreas,
7479 nsIFrame* aChildFrame)
7480 {
7481 aOverflowAreas.UnionWith(aChildFrame->GetOverflowAreas() +
7482 aChildFrame->GetPosition());
7483 }
7484
7485 /**
7486 * This function takes a frame that is part of a block-in-inline split,
7487 * and _if_ that frame is an anonymous block created by an ib split it
7488 * returns the block's preceding inline. This is needed because the
7489 * split inline's style context is the parent of the anonymous block's
7490 * style context.
7491 *
7492 * If aFrame is not an anonymous block, null is returned.
7493 */
7494 static nsIFrame*
7495 GetIBSplitSiblingForAnonymousBlock(const nsIFrame* aFrame)
7496 {
7497 NS_PRECONDITION(aFrame, "Must have a non-null frame!");
7498 NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT,
7499 "GetIBSplitSibling should only be called on ib-split frames");
7500
7501 nsIAtom* type = aFrame->StyleContext()->GetPseudo();
7502 if (type != nsCSSAnonBoxes::mozAnonymousBlock &&
7503 type != nsCSSAnonBoxes::mozAnonymousPositionedBlock) {
7504 // it's not an anonymous block
7505 return nullptr;
7506 }
7507
7508 // Find the first continuation of the frame. (Ugh. This ends up
7509 // being O(N^2) when it is called O(N) times.)
7510 aFrame = aFrame->FirstContinuation();
7511
7512 /*
7513 * Now look up the nsGkAtoms::IBSplitPrevSibling
7514 * property.
7515 */
7516 nsIFrame *ibSplitSibling = static_cast<nsIFrame*>
7517 (aFrame->Properties().Get(nsIFrame::IBSplitPrevSibling()));
7518 NS_ASSERTION(ibSplitSibling, "Broken frame tree?");
7519 return ibSplitSibling;
7520 }
7521
7522 /**
7523 * Get the parent, corrected for the mangled frame tree resulting from
7524 * having a block within an inline. The result only differs from the
7525 * result of |GetParent| when |GetParent| returns an anonymous block
7526 * that was created for an element that was 'display: inline' because
7527 * that element contained a block.
7528 *
7529 * Also skip anonymous scrolled-content parents; inherit directly from the
7530 * outer scroll frame.
7531 */
7532 static nsIFrame*
7533 GetCorrectedParent(const nsIFrame* aFrame)
7534 {
7535 nsIFrame *parent = aFrame->GetParent();
7536 if (!parent) {
7537 return nullptr;
7538 }
7539
7540 // Outer tables are always anon boxes; if we're in here for an outer
7541 // table, that actually means its the _inner_ table that wants to
7542 // know its parent. So get the pseudo of the inner in that case.
7543 nsIAtom* pseudo = aFrame->StyleContext()->GetPseudo();
7544 if (pseudo == nsCSSAnonBoxes::tableOuter) {
7545 pseudo = aFrame->GetFirstPrincipalChild()->StyleContext()->GetPseudo();
7546 }
7547 return nsFrame::CorrectStyleParentFrame(parent, pseudo);
7548 }
7549
7550 /* static */
7551 nsIFrame*
7552 nsFrame::CorrectStyleParentFrame(nsIFrame* aProspectiveParent,
7553 nsIAtom* aChildPseudo)
7554 {
7555 NS_PRECONDITION(aProspectiveParent, "Must have a prospective parent");
7556
7557 // Anon boxes are parented to their actual parent already, except
7558 // for non-elements. Those should not be treated as an anon box.
7559 if (aChildPseudo && aChildPseudo != nsCSSAnonBoxes::mozNonElement &&
7560 nsCSSAnonBoxes::IsAnonBox(aChildPseudo)) {
7561 NS_ASSERTION(aChildPseudo != nsCSSAnonBoxes::mozAnonymousBlock &&
7562 aChildPseudo != nsCSSAnonBoxes::mozAnonymousPositionedBlock,
7563 "Should have dealt with kids that have "
7564 "NS_FRAME_PART_OF_IBSPLIT elsewhere");
7565 return aProspectiveParent;
7566 }
7567
7568 // Otherwise, walk up out of all anon boxes. For placeholder frames, walk out
7569 // of all pseudo-elements as well. Otherwise ReparentStyleContext could cause
7570 // style data to be out of sync with the frame tree.
7571 nsIFrame* parent = aProspectiveParent;
7572 do {
7573 if (parent->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) {
7574 nsIFrame* sibling = GetIBSplitSiblingForAnonymousBlock(parent);
7575
7576 if (sibling) {
7577 // |parent| was a block in an {ib} split; use the inline as
7578 // |the style parent.
7579 parent = sibling;
7580 }
7581 }
7582
7583 nsIAtom* parentPseudo = parent->StyleContext()->GetPseudo();
7584 if (!parentPseudo ||
7585 (!nsCSSAnonBoxes::IsAnonBox(parentPseudo) &&
7586 // nsPlaceholderFrame pases in nsGkAtoms::placeholderFrame for
7587 // aChildPseudo (even though that's not a valid pseudo-type) just to
7588 // trigger this behavior of walking up to the nearest non-pseudo
7589 // ancestor.
7590 aChildPseudo != nsGkAtoms::placeholderFrame)) {
7591 return parent;
7592 }
7593
7594 parent = parent->GetParent();
7595 } while (parent);
7596
7597 if (aProspectiveParent->StyleContext()->GetPseudo() ==
7598 nsCSSAnonBoxes::viewportScroll) {
7599 // aProspectiveParent is the scrollframe for a viewport
7600 // and the kids are the anonymous scrollbars
7601 return aProspectiveParent;
7602 }
7603
7604 // We can get here if the root element is absolutely positioned.
7605 // We can't test for this very accurately, but it can only happen
7606 // when the prospective parent is a canvas frame.
7607 NS_ASSERTION(aProspectiveParent->GetType() == nsGkAtoms::canvasFrame,
7608 "Should have found a parent before this");
7609 return nullptr;
7610 }
7611
7612 nsIFrame*
7613 nsFrame::DoGetParentStyleContextFrame() const
7614 {
7615 if (mContent && !mContent->GetParent() &&
7616 !StyleContext()->GetPseudo()) {
7617 // we're a frame for the root. We have no style context parent.
7618 return nullptr;
7619 }
7620
7621 if (!(mState & NS_FRAME_OUT_OF_FLOW)) {
7622 /*
7623 * If this frame is an anonymous block created when an inline with a block
7624 * inside it got split, then the parent style context is on its preceding
7625 * inline. We can get to it using GetIBSplitSiblingForAnonymousBlock.
7626 */
7627 if (mState & NS_FRAME_PART_OF_IBSPLIT) {
7628 nsIFrame* ibSplitSibling = GetIBSplitSiblingForAnonymousBlock(this);
7629 if (ibSplitSibling) {
7630 return ibSplitSibling;
7631 }
7632 }
7633
7634 // If this frame is one of the blocks that split an inline, we must
7635 // return the "special" inline parent, i.e., the parent that this
7636 // frame would have if we didn't mangle the frame structure.
7637 return GetCorrectedParent(this);
7638 }
7639
7640 // We're an out-of-flow frame. For out-of-flow frames, we must
7641 // resolve underneath the placeholder's parent. The placeholder is
7642 // reached from the first-in-flow.
7643 nsIFrame* placeholder = PresContext()->FrameManager()->
7644 GetPlaceholderFrameFor(FirstInFlow());
7645 if (!placeholder) {
7646 NS_NOTREACHED("no placeholder frame for out-of-flow frame");
7647 return GetCorrectedParent(this);
7648 }
7649 return placeholder->GetParentStyleContextFrame();
7650 }
7651
7652 void
7653 nsFrame::GetLastLeaf(nsPresContext* aPresContext, nsIFrame **aFrame)
7654 {
7655 if (!aFrame || !*aFrame)
7656 return;
7657 nsIFrame *child = *aFrame;
7658 //if we are a block frame then go for the last line of 'this'
7659 while (1){
7660 child = child->GetFirstPrincipalChild();
7661 if (!child)
7662 return;//nothing to do
7663 nsIFrame* siblingFrame;
7664 nsIContent* content;
7665 //ignore anonymous elements, e.g. mozTableAdd* mozTableRemove*
7666 //see bug 278197 comment #12 #13 for details
7667 while ((siblingFrame = child->GetNextSibling()) &&
7668 (content = siblingFrame->GetContent()) &&
7669 !content->IsRootOfNativeAnonymousSubtree())
7670 child = siblingFrame;
7671 *aFrame = child;
7672 }
7673 }
7674
7675 void
7676 nsFrame::GetFirstLeaf(nsPresContext* aPresContext, nsIFrame **aFrame)
7677 {
7678 if (!aFrame || !*aFrame)
7679 return;
7680 nsIFrame *child = *aFrame;
7681 while (1){
7682 child = child->GetFirstPrincipalChild();
7683 if (!child)
7684 return;//nothing to do
7685 *aFrame = child;
7686 }
7687 }
7688
7689 /* virtual */ bool
7690 nsIFrame::IsFocusable(int32_t *aTabIndex, bool aWithMouse)
7691 {
7692 int32_t tabIndex = -1;
7693 if (aTabIndex) {
7694 *aTabIndex = -1; // Default for early return is not focusable
7695 }
7696 bool isFocusable = false;
7697
7698 if (mContent && mContent->IsElement() && IsVisibleConsideringAncestors()) {
7699 const nsStyleUserInterface* ui = StyleUserInterface();
7700 if (ui->mUserFocus != NS_STYLE_USER_FOCUS_IGNORE &&
7701 ui->mUserFocus != NS_STYLE_USER_FOCUS_NONE) {
7702 // Pass in default tabindex of -1 for nonfocusable and 0 for focusable
7703 tabIndex = 0;
7704 }
7705 isFocusable = mContent->IsFocusable(&tabIndex, aWithMouse);
7706 if (!isFocusable && !aWithMouse &&
7707 GetType() == nsGkAtoms::scrollFrame &&
7708 mContent->IsHTML() &&
7709 !mContent->IsRootOfNativeAnonymousSubtree() &&
7710 mContent->GetParent() &&
7711 !mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) {
7712 // Elements with scrollable view are focusable with script & tabbable
7713 // Otherwise you couldn't scroll them with keyboard, which is
7714 // an accessibility issue (e.g. Section 508 rules)
7715 // However, we don't make them to be focusable with the mouse,
7716 // because the extra focus outlines are considered unnecessarily ugly.
7717 // When clicked on, the selection position within the element
7718 // will be enough to make them keyboard scrollable.
7719 nsIScrollableFrame *scrollFrame = do_QueryFrame(this);
7720 if (scrollFrame &&
7721 scrollFrame->GetScrollbarStyles() != ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, NS_STYLE_OVERFLOW_HIDDEN) &&
7722 !scrollFrame->GetScrollRange().IsEqualEdges(nsRect(0, 0, 0, 0))) {
7723 // Scroll bars will be used for overflow
7724 isFocusable = true;
7725 tabIndex = 0;
7726 }
7727 }
7728 }
7729
7730 if (aTabIndex) {
7731 *aTabIndex = tabIndex;
7732 }
7733 return isFocusable;
7734 }
7735
7736 /**
7737 * @return true if this text frame ends with a newline character which is
7738 * treated as preformatted. It should return false if this is not a text frame.
7739 */
7740 bool
7741 nsIFrame::HasSignificantTerminalNewline() const
7742 {
7743 return false;
7744 }
7745
7746 static uint8_t
7747 ConvertSVGDominantBaselineToVerticalAlign(uint8_t aDominantBaseline)
7748 {
7749 // Most of these are approximate mappings.
7750 switch (aDominantBaseline) {
7751 case NS_STYLE_DOMINANT_BASELINE_HANGING:
7752 case NS_STYLE_DOMINANT_BASELINE_TEXT_BEFORE_EDGE:
7753 return NS_STYLE_VERTICAL_ALIGN_TEXT_TOP;
7754 case NS_STYLE_DOMINANT_BASELINE_TEXT_AFTER_EDGE:
7755 case NS_STYLE_DOMINANT_BASELINE_IDEOGRAPHIC:
7756 return NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM;
7757 case NS_STYLE_DOMINANT_BASELINE_CENTRAL:
7758 case NS_STYLE_DOMINANT_BASELINE_MIDDLE:
7759 case NS_STYLE_DOMINANT_BASELINE_MATHEMATICAL:
7760 return NS_STYLE_VERTICAL_ALIGN_MIDDLE;
7761 case NS_STYLE_DOMINANT_BASELINE_AUTO:
7762 case NS_STYLE_DOMINANT_BASELINE_ALPHABETIC:
7763 return NS_STYLE_VERTICAL_ALIGN_BASELINE;
7764 case NS_STYLE_DOMINANT_BASELINE_USE_SCRIPT:
7765 case NS_STYLE_DOMINANT_BASELINE_NO_CHANGE:
7766 case NS_STYLE_DOMINANT_BASELINE_RESET_SIZE:
7767 // These three should not simply map to 'baseline', but we don't
7768 // support the complex baseline model that SVG 1.1 has and which
7769 // css3-linebox now defines.
7770 return NS_STYLE_VERTICAL_ALIGN_BASELINE;
7771 default:
7772 NS_NOTREACHED("unexpected aDominantBaseline value");
7773 return NS_STYLE_VERTICAL_ALIGN_BASELINE;
7774 }
7775 }
7776
7777 uint8_t
7778 nsIFrame::VerticalAlignEnum() const
7779 {
7780 if (IsSVGText()) {
7781 uint8_t dominantBaseline;
7782 for (const nsIFrame* frame = this; frame; frame = frame->GetParent()) {
7783 dominantBaseline = frame->StyleSVGReset()->mDominantBaseline;
7784 if (dominantBaseline != NS_STYLE_DOMINANT_BASELINE_AUTO ||
7785 frame->GetType() == nsGkAtoms::svgTextFrame) {
7786 break;
7787 }
7788 }
7789 return ConvertSVGDominantBaselineToVerticalAlign(dominantBaseline);
7790 }
7791
7792 const nsStyleCoord& verticalAlign = StyleTextReset()->mVerticalAlign;
7793 if (verticalAlign.GetUnit() == eStyleUnit_Enumerated) {
7794 return verticalAlign.GetIntValue();
7795 }
7796
7797 return eInvalidVerticalAlign;
7798 }
7799
7800 /* static */
7801 void nsFrame::FillCursorInformationFromStyle(const nsStyleUserInterface* ui,
7802 nsIFrame::Cursor& aCursor)
7803 {
7804 aCursor.mCursor = ui->mCursor;
7805 aCursor.mHaveHotspot = false;
7806 aCursor.mHotspotX = aCursor.mHotspotY = 0.0f;
7807
7808 for (nsCursorImage *item = ui->mCursorArray,
7809 *item_end = ui->mCursorArray + ui->mCursorArrayLength;
7810 item < item_end; ++item) {
7811 uint32_t status;
7812 nsresult rv = item->GetImage()->GetImageStatus(&status);
7813 if (NS_SUCCEEDED(rv) && (status & imgIRequest::STATUS_LOAD_COMPLETE)) {
7814 // This is the one we want
7815 item->GetImage()->GetImage(getter_AddRefs(aCursor.mContainer));
7816 aCursor.mHaveHotspot = item->mHaveHotspot;
7817 aCursor.mHotspotX = item->mHotspotX;
7818 aCursor.mHotspotY = item->mHotspotY;
7819 break;
7820 }
7821 }
7822 }
7823
7824 NS_IMETHODIMP
7825 nsFrame::RefreshSizeCache(nsBoxLayoutState& aState)
7826 {
7827 // XXXbz this comment needs some rewriting to make sense in the
7828 // post-reflow-branch world.
7829
7830 // Ok we need to compute our minimum, preferred, and maximum sizes.
7831 // 1) Maximum size. This is easy. Its infinite unless it is overloaded by CSS.
7832 // 2) Preferred size. This is a little harder. This is the size the block would be
7833 // if it were laid out on an infinite canvas. So we can get this by reflowing
7834 // the block with and INTRINSIC width and height. We can also do a nice optimization
7835 // for incremental reflow. If the reflow is incremental then we can pass a flag to
7836 // have the block compute the preferred width for us! Preferred height can just be
7837 // the minimum height;
7838 // 3) Minimum size. This is a toughy. We can pass the block a flag asking for the max element
7839 // size. That would give us the width. Unfortunately you can only ask for a maxElementSize
7840 // during an incremental reflow. So on other reflows we will just have to use 0.
7841 // The min height on the other hand is fairly easy we need to get the largest
7842 // line height. This can be done with the line iterator.
7843
7844 // if we do have a rendering context
7845 nsresult rv = NS_OK;
7846 nsRenderingContext* rendContext = aState.GetRenderingContext();
7847 if (rendContext) {
7848 nsPresContext* presContext = aState.PresContext();
7849
7850 // If we don't have any HTML constraints and it's a resize, then nothing in the block
7851 // could have changed, so no refresh is necessary.
7852 nsBoxLayoutMetrics* metrics = BoxMetrics();
7853 if (!DoesNeedRecalc(metrics->mBlockPrefSize))
7854 return NS_OK;
7855
7856 // the rect we plan to size to.
7857 nsRect rect = GetRect();
7858
7859 nsMargin bp(0,0,0,0);
7860 GetBorderAndPadding(bp);
7861
7862 {
7863 // If we're a container for font size inflation, then shrink
7864 // wrapping inside of us should not apply font size inflation.
7865 AutoMaybeDisableFontInflation an(this);
7866
7867 metrics->mBlockPrefSize.width =
7868 GetPrefWidth(rendContext) + bp.LeftRight();
7869 metrics->mBlockMinSize.width =
7870 GetMinWidth(rendContext) + bp.LeftRight();
7871 }
7872
7873 // do the nasty.
7874 const WritingMode wm = aState.OuterReflowState() ?
7875 aState.OuterReflowState()->GetWritingMode() : GetWritingMode();
7876 nsHTMLReflowMetrics desiredSize(wm);
7877 rv = BoxReflow(aState, presContext, desiredSize, rendContext,
7878 rect.x, rect.y,
7879 metrics->mBlockPrefSize.width, NS_UNCONSTRAINEDSIZE);
7880
7881 metrics->mBlockMinSize.height = 0;
7882 // ok we need the max ascent of the items on the line. So to do this
7883 // ask the block for its line iterator. Get the max ascent.
7884 nsAutoLineIterator lines = GetLineIterator();
7885 if (lines)
7886 {
7887 metrics->mBlockMinSize.height = 0;
7888 int count = 0;
7889 nsIFrame* firstFrame = nullptr;
7890 int32_t framesOnLine;
7891 nsRect lineBounds;
7892 uint32_t lineFlags;
7893
7894 do {
7895 lines->GetLine(count, &firstFrame, &framesOnLine, lineBounds, &lineFlags);
7896
7897 if (lineBounds.height > metrics->mBlockMinSize.height)
7898 metrics->mBlockMinSize.height = lineBounds.height;
7899
7900 count++;
7901 } while(firstFrame);
7902 } else {
7903 metrics->mBlockMinSize.height = desiredSize.Height();
7904 }
7905
7906 metrics->mBlockPrefSize.height = metrics->mBlockMinSize.height;
7907
7908 if (desiredSize.TopAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) {
7909 if (!nsLayoutUtils::GetFirstLineBaseline(this, &metrics->mBlockAscent))
7910 metrics->mBlockAscent = GetBaseline();
7911 } else {
7912 metrics->mBlockAscent = desiredSize.TopAscent();
7913 }
7914
7915 #ifdef DEBUG_adaptor
7916 printf("min=(%d,%d), pref=(%d,%d), ascent=%d\n", metrics->mBlockMinSize.width,
7917 metrics->mBlockMinSize.height,
7918 metrics->mBlockPrefSize.width,
7919 metrics->mBlockPrefSize.height,
7920 metrics->mBlockAscent);
7921 #endif
7922 }
7923
7924 return rv;
7925 }
7926
7927 /* virtual */ nsILineIterator*
7928 nsFrame::GetLineIterator()
7929 {
7930 return nullptr;
7931 }
7932
7933 nsSize
7934 nsFrame::GetPrefSize(nsBoxLayoutState& aState)
7935 {
7936 nsSize size(0,0);
7937 DISPLAY_PREF_SIZE(this, size);
7938 // If the size is cached, and there are no HTML constraints that we might
7939 // be depending on, then we just return the cached size.
7940 nsBoxLayoutMetrics *metrics = BoxMetrics();
7941 if (!DoesNeedRecalc(metrics->mPrefSize)) {
7942 return metrics->mPrefSize;
7943 }
7944
7945 if (IsCollapsed())
7946 return size;
7947
7948 // get our size in CSS.
7949 bool widthSet, heightSet;
7950 bool completelyRedefined = nsIFrame::AddCSSPrefSize(this, size, widthSet, heightSet);
7951
7952 // Refresh our caches with new sizes.
7953 if (!completelyRedefined) {
7954 RefreshSizeCache(aState);
7955 nsSize blockSize = metrics->mBlockPrefSize;
7956
7957 // notice we don't need to add our borders or padding
7958 // in. That's because the block did it for us.
7959 if (!widthSet)
7960 size.width = blockSize.width;
7961 if (!heightSet)
7962 size.height = blockSize.height;
7963 }
7964
7965 metrics->mPrefSize = size;
7966 return size;
7967 }
7968
7969 nsSize
7970 nsFrame::GetMinSize(nsBoxLayoutState& aState)
7971 {
7972 nsSize size(0,0);
7973 DISPLAY_MIN_SIZE(this, size);
7974 // Don't use the cache if we have HTMLReflowState constraints --- they might have changed
7975 nsBoxLayoutMetrics *metrics = BoxMetrics();
7976 if (!DoesNeedRecalc(metrics->mMinSize)) {
7977 size = metrics->mMinSize;
7978 return size;
7979 }
7980
7981 if (IsCollapsed())
7982 return size;
7983
7984 // get our size in CSS.
7985 bool widthSet, heightSet;
7986 bool completelyRedefined =
7987 nsIFrame::AddCSSMinSize(aState, this, size, widthSet, heightSet);
7988
7989 // Refresh our caches with new sizes.
7990 if (!completelyRedefined) {
7991 RefreshSizeCache(aState);
7992 nsSize blockSize = metrics->mBlockMinSize;
7993
7994 if (!widthSet)
7995 size.width = blockSize.width;
7996 if (!heightSet)
7997 size.height = blockSize.height;
7998 }
7999
8000 metrics->mMinSize = size;
8001 return size;
8002 }
8003
8004 nsSize
8005 nsFrame::GetMaxSize(nsBoxLayoutState& aState)
8006 {
8007 nsSize size(NS_INTRINSICSIZE, NS_INTRINSICSIZE);
8008 DISPLAY_MAX_SIZE(this, size);
8009 // Don't use the cache if we have HTMLReflowState constraints --- they might have changed
8010 nsBoxLayoutMetrics *metrics = BoxMetrics();
8011 if (!DoesNeedRecalc(metrics->mMaxSize)) {
8012 size = metrics->mMaxSize;
8013 return size;
8014 }
8015
8016 if (IsCollapsed())
8017 return size;
8018
8019 size = nsBox::GetMaxSize(aState);
8020 metrics->mMaxSize = size;
8021
8022 return size;
8023 }
8024
8025 nscoord
8026 nsFrame::GetFlex(nsBoxLayoutState& aState)
8027 {
8028 nsBoxLayoutMetrics *metrics = BoxMetrics();
8029 if (!DoesNeedRecalc(metrics->mFlex))
8030 return metrics->mFlex;
8031
8032 metrics->mFlex = nsBox::GetFlex(aState);
8033
8034 return metrics->mFlex;
8035 }
8036
8037 nscoord
8038 nsFrame::GetBoxAscent(nsBoxLayoutState& aState)
8039 {
8040 nsBoxLayoutMetrics *metrics = BoxMetrics();
8041 if (!DoesNeedRecalc(metrics->mAscent))
8042 return metrics->mAscent;
8043
8044 if (IsCollapsed()) {
8045 metrics->mAscent = 0;
8046 } else {
8047 // Refresh our caches with new sizes.
8048 RefreshSizeCache(aState);
8049 metrics->mAscent = metrics->mBlockAscent;
8050 }
8051
8052 return metrics->mAscent;
8053 }
8054
8055 nsresult
8056 nsFrame::DoLayout(nsBoxLayoutState& aState)
8057 {
8058 nsRect ourRect(mRect);
8059
8060 nsRenderingContext* rendContext = aState.GetRenderingContext();
8061 nsPresContext* presContext = aState.PresContext();
8062 const WritingMode wm = aState.OuterReflowState() ?
8063 aState.OuterReflowState()->GetWritingMode() : GetWritingMode();
8064 nsHTMLReflowMetrics desiredSize(wm);
8065 nsresult rv = NS_OK;
8066
8067 if (rendContext) {
8068
8069 rv = BoxReflow(aState, presContext, desiredSize, rendContext,
8070 ourRect.x, ourRect.y, ourRect.width, ourRect.height);
8071
8072 if (IsCollapsed()) {
8073 SetSize(nsSize(0, 0));
8074 } else {
8075
8076 // if our child needs to be bigger. This might happend with
8077 // wrapping text. There is no way to predict its height until we
8078 // reflow it. Now that we know the height reshuffle upward.
8079 if (desiredSize.Width() > ourRect.width ||
8080 desiredSize.Height() > ourRect.height) {
8081
8082 #ifdef DEBUG_GROW
8083 DumpBox(stdout);
8084 printf(" GREW from (%d,%d) -> (%d,%d)\n",
8085 ourRect.width, ourRect.height,
8086 desiredSize.Width(), desiredSize.Height());
8087 #endif
8088
8089 if (desiredSize.Width() > ourRect.width)
8090 ourRect.width = desiredSize.Width();
8091
8092 if (desiredSize.Height() > ourRect.height)
8093 ourRect.height = desiredSize.Height();
8094 }
8095
8096 // ensure our size is what we think is should be. Someone could have
8097 // reset the frame to be smaller or something dumb like that.
8098 SetSize(ourRect.Size());
8099 }
8100 }
8101
8102 // Should we do this if IsCollapsed() is true?
8103 nsSize size(GetSize());
8104 desiredSize.Width() = size.width;
8105 desiredSize.Height() = size.height;
8106 desiredSize.UnionOverflowAreasWithDesiredBounds();
8107
8108 if (HasAbsolutelyPositionedChildren()) {
8109 // Set up a |reflowState| to pass into ReflowAbsoluteFrames
8110 nsHTMLReflowState reflowState(aState.PresContext(), this,
8111 aState.GetRenderingContext(),
8112 nsSize(size.width, NS_UNCONSTRAINEDSIZE),
8113 nsHTMLReflowState::DUMMY_PARENT_REFLOW_STATE);
8114
8115 AddStateBits(NS_FRAME_IN_REFLOW);
8116 // Set up a |reflowStatus| to pass into ReflowAbsoluteFrames
8117 // (just a dummy value; hopefully that's OK)
8118 nsReflowStatus reflowStatus = NS_FRAME_COMPLETE;
8119 ReflowAbsoluteFrames(aState.PresContext(), desiredSize,
8120 reflowState, reflowStatus);
8121 RemoveStateBits(NS_FRAME_IN_REFLOW);
8122 }
8123
8124 nsSize oldSize(ourRect.Size());
8125 FinishAndStoreOverflow(desiredSize.mOverflowAreas, size, &oldSize);
8126
8127 SyncLayout(aState);
8128
8129 return rv;
8130 }
8131
8132 nsresult
8133 nsFrame::BoxReflow(nsBoxLayoutState& aState,
8134 nsPresContext* aPresContext,
8135 nsHTMLReflowMetrics& aDesiredSize,
8136 nsRenderingContext* aRenderingContext,
8137 nscoord aX,
8138 nscoord aY,
8139 nscoord aWidth,
8140 nscoord aHeight,
8141 bool aMoveFrame)
8142 {
8143 DO_GLOBAL_REFLOW_COUNT("nsBoxToBlockAdaptor");
8144
8145 #ifdef DEBUG_REFLOW
8146 nsAdaptorAddIndents();
8147 printf("Reflowing: ");
8148 nsFrame::ListTag(stdout, mFrame);
8149 printf("\n");
8150 gIndent2++;
8151 #endif
8152
8153 nsBoxLayoutMetrics *metrics = BoxMetrics();
8154 nsReflowStatus status = NS_FRAME_COMPLETE;
8155
8156 bool needsReflow = NS_SUBTREE_DIRTY(this);
8157
8158 // if we don't need a reflow then
8159 // lets see if we are already that size. Yes? then don't even reflow. We are done.
8160 if (!needsReflow) {
8161
8162 if (aWidth != NS_INTRINSICSIZE && aHeight != NS_INTRINSICSIZE) {
8163
8164 // if the new calculated size has a 0 width or a 0 height
8165 if ((metrics->mLastSize.width == 0 || metrics->mLastSize.height == 0) && (aWidth == 0 || aHeight == 0)) {
8166 needsReflow = false;
8167 aDesiredSize.Width() = aWidth;
8168 aDesiredSize.Height() = aHeight;
8169 SetSize(nsSize(aDesiredSize.Width(), aDesiredSize.Height()));
8170 } else {
8171 aDesiredSize.Width() = metrics->mLastSize.width;
8172 aDesiredSize.Height() = metrics->mLastSize.height;
8173
8174 // remove the margin. The rect of our child does not include it but our calculated size does.
8175 // don't reflow if we are already the right size
8176 if (metrics->mLastSize.width == aWidth && metrics->mLastSize.height == aHeight)
8177 needsReflow = false;
8178 else
8179 needsReflow = true;
8180
8181 }
8182 } else {
8183 // if the width or height are intrinsic alway reflow because
8184 // we don't know what it should be.
8185 needsReflow = true;
8186 }
8187 }
8188
8189 // ok now reflow the child into the spacers calculated space
8190 if (needsReflow) {
8191
8192 aDesiredSize.Width() = 0;
8193 aDesiredSize.Height() = 0;
8194
8195 // create a reflow state to tell our child to flow at the given size.
8196
8197 // Construct a bogus parent reflow state so that there's a usable
8198 // containing block reflow state.
8199 nsMargin margin(0,0,0,0);
8200 GetMargin(margin);
8201
8202 nsSize parentSize(aWidth, aHeight);
8203 if (parentSize.height != NS_INTRINSICSIZE)
8204 parentSize.height += margin.TopBottom();
8205 if (parentSize.width != NS_INTRINSICSIZE)
8206 parentSize.width += margin.LeftRight();
8207
8208 nsIFrame *parentFrame = GetParent();
8209 nsFrameState savedState = parentFrame->GetStateBits();
8210 nsHTMLReflowState parentReflowState(aPresContext, parentFrame,
8211 aRenderingContext,
8212 parentSize,
8213 nsHTMLReflowState::DUMMY_PARENT_REFLOW_STATE);
8214 parentFrame->RemoveStateBits(~nsFrameState(0));
8215 parentFrame->AddStateBits(savedState);
8216
8217 // This may not do very much useful, but it's probably worth trying.
8218 if (parentSize.width != NS_INTRINSICSIZE)
8219 parentReflowState.SetComputedWidth(std::max(parentSize.width, 0));
8220 if (parentSize.height != NS_INTRINSICSIZE)
8221 parentReflowState.SetComputedHeight(std::max(parentSize.height, 0));
8222 parentReflowState.ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
8223 // XXX use box methods
8224 parentFrame->GetPadding(parentReflowState.ComputedPhysicalPadding());
8225 parentFrame->GetBorder(parentReflowState.ComputedPhysicalBorderPadding());
8226 parentReflowState.ComputedPhysicalBorderPadding() +=
8227 parentReflowState.ComputedPhysicalPadding();
8228
8229 // Construct the parent chain manually since constructing it normally
8230 // messes up dimensions.
8231 const nsHTMLReflowState *outerReflowState = aState.OuterReflowState();
8232 NS_ASSERTION(!outerReflowState || outerReflowState->frame != this,
8233 "in and out of XUL on a single frame?");
8234 const nsHTMLReflowState* parentRS;
8235 if (outerReflowState && outerReflowState->frame == parentFrame) {
8236 // We're a frame (such as a text control frame) that jumps into
8237 // box reflow and then straight out of it on the child frame.
8238 // This means we actually have a real parent reflow state.
8239 // nsLayoutUtils::InflationMinFontSizeFor used to need this to be
8240 // linked up correctly for text control frames, so do so here).
8241 parentRS = outerReflowState;
8242 } else {
8243 parentRS = &parentReflowState;
8244 }
8245
8246 // XXX Is it OK that this reflow state has only one ancestor?
8247 // (It used to have a bogus parent, skipping all the boxes).
8248 nsSize availSize(aWidth, NS_INTRINSICSIZE);
8249 nsHTMLReflowState reflowState(aPresContext, *parentRS, this,
8250 availSize, -1, -1,
8251 nsHTMLReflowState::DUMMY_PARENT_REFLOW_STATE);
8252
8253 // XXX_jwir3: This is somewhat fishy. If this is actually changing the value
8254 // here (which it might be), then we should make sure that it's
8255 // correct the first time around, rather than changing it later.
8256 reflowState.mCBReflowState = parentRS;
8257
8258 reflowState.mReflowDepth = aState.GetReflowDepth();
8259
8260 // mComputedWidth and mComputedHeight are content-box, not
8261 // border-box
8262 if (aWidth != NS_INTRINSICSIZE) {
8263 nscoord computedWidth =
8264 aWidth - reflowState.ComputedPhysicalBorderPadding().LeftRight();
8265 computedWidth = std::max(computedWidth, 0);
8266 reflowState.SetComputedWidth(computedWidth);
8267 }
8268
8269 // Most child frames of box frames (e.g. subdocument or scroll frames)
8270 // need to be constrained to the provided size and overflow as necessary.
8271 // The one exception are block frames, because we need to know their
8272 // natural height excluding any overflow area which may be caused by
8273 // various CSS effects such as shadow or outline.
8274 if (!IsFrameOfType(eBlockFrame)) {
8275 if (aHeight != NS_INTRINSICSIZE) {
8276 nscoord computedHeight =
8277 aHeight - reflowState.ComputedPhysicalBorderPadding().TopBottom();
8278 computedHeight = std::max(computedHeight, 0);
8279 reflowState.SetComputedHeight(computedHeight);
8280 } else {
8281 reflowState.SetComputedHeight(
8282 ComputeSize(aRenderingContext, availSize, availSize.width,
8283 nsSize(reflowState.ComputedPhysicalMargin().LeftRight(),
8284 reflowState.ComputedPhysicalMargin().TopBottom()),
8285 nsSize(reflowState.ComputedPhysicalBorderPadding().LeftRight() -
8286 reflowState.ComputedPhysicalPadding().LeftRight(),
8287 reflowState.ComputedPhysicalBorderPadding().TopBottom() -
8288 reflowState.ComputedPhysicalPadding().TopBottom()),
8289 nsSize(reflowState.ComputedPhysicalPadding().LeftRight(),
8290 reflowState.ComputedPhysicalPadding().TopBottom()),
8291 false).height
8292 );
8293 }
8294 }
8295
8296 // Box layout calls SetRect before Layout, whereas non-box layout
8297 // calls SetRect after Reflow.
8298 // XXX Perhaps we should be doing this by twiddling the rect back to
8299 // mLastSize before calling Reflow and then switching it back, but
8300 // However, mLastSize can also be the size passed to BoxReflow by
8301 // RefreshSizeCache, so that doesn't really make sense.
8302 if (metrics->mLastSize.width != aWidth) {
8303 reflowState.mFlags.mHResize = true;
8304
8305 // When font size inflation is enabled, a horizontal resize
8306 // requires a full reflow. See nsHTMLReflowState::InitResizeFlags
8307 // for more details.
8308 if (nsLayoutUtils::FontSizeInflationEnabled(aPresContext)) {
8309 AddStateBits(NS_FRAME_IS_DIRTY);
8310 }
8311 }
8312 if (metrics->mLastSize.height != aHeight)
8313 reflowState.mFlags.mVResize = true;
8314
8315 #ifdef DEBUG_REFLOW
8316 nsAdaptorAddIndents();
8317 printf("Size=(%d,%d)\n",reflowState.ComputedWidth(),
8318 reflowState.ComputedHeight());
8319 nsAdaptorAddIndents();
8320 nsAdaptorPrintReason(reflowState);
8321 printf("\n");
8322 #endif
8323
8324 // place the child and reflow
8325 WillReflow(aPresContext);
8326
8327 Reflow(aPresContext, aDesiredSize, reflowState, status);
8328
8329 NS_ASSERTION(NS_FRAME_IS_COMPLETE(status), "bad status");
8330
8331 uint32_t layoutFlags = aState.LayoutFlags();
8332 nsContainerFrame::FinishReflowChild(this, aPresContext, aDesiredSize,
8333 &reflowState, aX, aY, layoutFlags | NS_FRAME_NO_MOVE_FRAME);
8334
8335 // Save the ascent. (bug 103925)
8336 if (IsCollapsed()) {
8337 metrics->mAscent = 0;
8338 } else {
8339 if (aDesiredSize.TopAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) {
8340 if (!nsLayoutUtils::GetFirstLineBaseline(this, &metrics->mAscent))
8341 metrics->mAscent = GetBaseline();
8342 } else
8343 metrics->mAscent = aDesiredSize.TopAscent();
8344 }
8345
8346 } else {
8347 aDesiredSize.SetTopAscent(metrics->mBlockAscent);
8348 }
8349
8350 #ifdef DEBUG_REFLOW
8351 if (aHeight != NS_INTRINSICSIZE && aDesiredSize.Height() != aHeight)
8352 {
8353 nsAdaptorAddIndents();
8354 printf("*****got taller!*****\n");
8355
8356 }
8357 if (aWidth != NS_INTRINSICSIZE && aDesiredSize.Width() != aWidth)
8358 {
8359 nsAdaptorAddIndents();
8360 printf("*****got wider!******\n");
8361
8362 }
8363 #endif
8364
8365 if (aWidth == NS_INTRINSICSIZE)
8366 aWidth = aDesiredSize.Width();
8367
8368 if (aHeight == NS_INTRINSICSIZE)
8369 aHeight = aDesiredSize.Height();
8370
8371 metrics->mLastSize.width = aDesiredSize.Width();
8372 metrics->mLastSize.height = aDesiredSize.Height();
8373
8374 #ifdef DEBUG_REFLOW
8375 gIndent2--;
8376 #endif
8377
8378 return NS_OK;
8379 }
8380
8381 static void
8382 DestroyBoxMetrics(void* aPropertyValue)
8383 {
8384 delete static_cast<nsBoxLayoutMetrics*>(aPropertyValue);
8385 }
8386
8387 NS_DECLARE_FRAME_PROPERTY(BoxMetricsProperty, DestroyBoxMetrics)
8388
8389 nsBoxLayoutMetrics*
8390 nsFrame::BoxMetrics() const
8391 {
8392 nsBoxLayoutMetrics* metrics =
8393 static_cast<nsBoxLayoutMetrics*>(Properties().Get(BoxMetricsProperty()));
8394 NS_ASSERTION(metrics, "A box layout method was called but InitBoxMetrics was never called");
8395 return metrics;
8396 }
8397
8398 /* static */ void
8399 nsIFrame::AddInPopupStateBitToDescendants(nsIFrame* aFrame)
8400 {
8401 aFrame->AddStateBits(NS_FRAME_IN_POPUP);
8402
8403 nsAutoTArray<nsIFrame::ChildList,4> childListArray;
8404 aFrame->GetCrossDocChildLists(&childListArray);
8405
8406 nsIFrame::ChildListArrayIterator lists(childListArray);
8407 for (; !lists.IsDone(); lists.Next()) {
8408 nsFrameList::Enumerator childFrames(lists.CurrentList());
8409 for (; !childFrames.AtEnd(); childFrames.Next()) {
8410 AddInPopupStateBitToDescendants(childFrames.get());
8411 }
8412 }
8413 }
8414
8415 /* static */ void
8416 nsIFrame::RemoveInPopupStateBitFromDescendants(nsIFrame* aFrame)
8417 {
8418 if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) ||
8419 nsLayoutUtils::IsPopup(aFrame)) {
8420 return;
8421 }
8422
8423 aFrame->RemoveStateBits(NS_FRAME_IN_POPUP);
8424
8425 nsAutoTArray<nsIFrame::ChildList,4> childListArray;
8426 aFrame->GetCrossDocChildLists(&childListArray);
8427
8428 nsIFrame::ChildListArrayIterator lists(childListArray);
8429 for (; !lists.IsDone(); lists.Next()) {
8430 nsFrameList::Enumerator childFrames(lists.CurrentList());
8431 for (; !childFrames.AtEnd(); childFrames.Next()) {
8432 RemoveInPopupStateBitFromDescendants(childFrames.get());
8433 }
8434 }
8435 }
8436
8437 void
8438 nsFrame::SetParent(nsIFrame* aParent)
8439 {
8440 bool wasBoxWrapped = IsBoxWrapped();
8441 mParent = aParent;
8442 if (!wasBoxWrapped && IsBoxWrapped()) {
8443 InitBoxMetrics(true);
8444 } else if (wasBoxWrapped && !IsBoxWrapped()) {
8445 Properties().Delete(BoxMetricsProperty());
8446 }
8447
8448 if (GetStateBits() & (NS_FRAME_HAS_VIEW | NS_FRAME_HAS_CHILD_WITH_VIEW)) {
8449 for (nsIFrame* f = aParent;
8450 f && !(f->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW);
8451 f = f->GetParent()) {
8452 f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
8453 }
8454 }
8455
8456 if (HasInvalidFrameInSubtree()) {
8457 for (nsIFrame* f = aParent;
8458 f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
8459 f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
8460 f->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
8461 }
8462 }
8463
8464 if (aParent->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
8465 AddInPopupStateBitToDescendants(this);
8466 } else {
8467 RemoveInPopupStateBitFromDescendants(this);
8468 }
8469
8470 // If our new parent only has invalid children, then we just invalidate
8471 // ourselves too. This is probably faster than clearing the flag all
8472 // the way up the frame tree.
8473 if (aParent->HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT)) {
8474 InvalidateFrame();
8475 }
8476 }
8477
8478 void
8479 nsFrame::InitBoxMetrics(bool aClear)
8480 {
8481 FrameProperties props = Properties();
8482 if (aClear) {
8483 props.Delete(BoxMetricsProperty());
8484 }
8485
8486 nsBoxLayoutMetrics *metrics = new nsBoxLayoutMetrics();
8487 props.Set(BoxMetricsProperty(), metrics);
8488
8489 nsFrame::MarkIntrinsicWidthsDirty();
8490 metrics->mBlockAscent = 0;
8491 metrics->mLastSize.SizeTo(0, 0);
8492 }
8493
8494 void
8495 nsIFrame::CreateOwnLayerIfNeeded(nsDisplayListBuilder* aBuilder,
8496 nsDisplayList* aList)
8497 {
8498 if (GetContent() &&
8499 GetContent()->IsXUL() &&
8500 GetContent()->HasAttr(kNameSpaceID_None, nsGkAtoms::layer)) {
8501 aList->AppendNewToTop(new (aBuilder)
8502 nsDisplayOwnLayer(aBuilder, this, aList));
8503 }
8504 }
8505
8506 bool
8507 nsIFrame::IsSelected() const
8508 {
8509 return (GetContent() && GetContent()->IsSelectionDescendant()) ?
8510 IsFrameSelected() : false;
8511 }
8512
8513 void
8514 nsIFrame::DestroySurface(void* aPropertyValue)
8515 {
8516 static_cast<gfxASurface*>(aPropertyValue)->Release();
8517 }
8518
8519 void
8520 nsIFrame::DestroyDT(void* aPropertyValue)
8521 {
8522 static_cast<mozilla::gfx::DrawTarget*>(aPropertyValue)->Release();
8523 }
8524
8525 void
8526 nsIFrame::DestroyRegion(void* aPropertyValue)
8527 {
8528 delete static_cast<nsRegion*>(aPropertyValue);
8529 }
8530
8531 bool
8532 nsIFrame::IsPseudoStackingContextFromStyle() {
8533 const nsStyleDisplay* disp = StyleDisplay();
8534 // If you change this, also change the computation of pseudoStackingContext
8535 // in BuildDisplayListForChild()
8536 return disp->mOpacity != 1.0f ||
8537 disp->IsPositioned(this) ||
8538 disp->IsFloating(this) ||
8539 (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT);
8540 }
8541
8542 Element*
8543 nsIFrame::GetPseudoElement(nsCSSPseudoElements::Type aType)
8544 {
8545 nsIFrame* frame = nullptr;
8546
8547 if (aType == nsCSSPseudoElements::ePseudo_before) {
8548 frame = nsLayoutUtils::GetBeforeFrame(this);
8549 } else if (aType == nsCSSPseudoElements::ePseudo_after) {
8550 frame = nsLayoutUtils::GetAfterFrame(this);
8551 }
8552
8553 if (frame) {
8554 nsIContent* content = frame->GetContent();
8555 if (content->IsElement()) {
8556 return content->AsElement();
8557 }
8558 }
8559
8560 return nullptr;
8561 }
8562
8563 nsIFrame::ContentOffsets::ContentOffsets()
8564 {
8565 }
8566
8567 nsIFrame::ContentOffsets::ContentOffsets(const ContentOffsets& rhs)
8568 : content(rhs.content),
8569 offset(rhs.offset),
8570 secondaryOffset(rhs.secondaryOffset),
8571 associateWithNext(rhs.associateWithNext)
8572 {
8573 }
8574
8575 nsIFrame::ContentOffsets::~ContentOffsets()
8576 {
8577 }
8578
8579 nsIFrame::CaretPosition::CaretPosition()
8580 : mContentOffset(0)
8581 {
8582 }
8583
8584 nsIFrame::CaretPosition::~CaretPosition()
8585 {
8586 }
8587
8588 // Box layout debugging
8589 #ifdef DEBUG_REFLOW
8590 int32_t gIndent2 = 0;
8591
8592 void
8593 nsAdaptorAddIndents()
8594 {
8595 for(int32_t i=0; i < gIndent2; i++)
8596 {
8597 printf(" ");
8598 }
8599 }
8600
8601 void
8602 nsAdaptorPrintReason(nsHTMLReflowState& aReflowState)
8603 {
8604 char* reflowReasonString;
8605
8606 switch(aReflowState.reason)
8607 {
8608 case eReflowReason_Initial:
8609 reflowReasonString = "initial";
8610 break;
8611
8612 case eReflowReason_Resize:
8613 reflowReasonString = "resize";
8614 break;
8615 case eReflowReason_Dirty:
8616 reflowReasonString = "dirty";
8617 break;
8618 case eReflowReason_StyleChange:
8619 reflowReasonString = "stylechange";
8620 break;
8621 case eReflowReason_Incremental:
8622 {
8623 switch (aReflowState.reflowCommand->Type()) {
8624 case eReflowType_StyleChanged:
8625 reflowReasonString = "incremental (StyleChanged)";
8626 break;
8627 case eReflowType_ReflowDirty:
8628 reflowReasonString = "incremental (ReflowDirty)";
8629 break;
8630 default:
8631 reflowReasonString = "incremental (Unknown)";
8632 }
8633 }
8634 break;
8635 default:
8636 reflowReasonString = "unknown";
8637 break;
8638 }
8639
8640 printf("%s",reflowReasonString);
8641 }
8642
8643 #endif
8644 #ifdef DEBUG_LAYOUT
8645 void
8646 nsFrame::GetBoxName(nsAutoString& aName)
8647 {
8648 GetFrameName(aName);
8649 }
8650 #endif
8651
8652 #ifdef DEBUG
8653 static void
8654 GetTagName(nsFrame* aFrame, nsIContent* aContent, int aResultSize,
8655 char* aResult)
8656 {
8657 if (aContent) {
8658 PR_snprintf(aResult, aResultSize, "%s@%p",
8659 nsAtomCString(aContent->Tag()).get(), aFrame);
8660 }
8661 else {
8662 PR_snprintf(aResult, aResultSize, "@%p", aFrame);
8663 }
8664 }
8665
8666 void
8667 nsFrame::Trace(const char* aMethod, bool aEnter)
8668 {
8669 if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) {
8670 char tagbuf[40];
8671 GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
8672 PR_LogPrint("%s: %s %s", tagbuf, aEnter ? "enter" : "exit", aMethod);
8673 }
8674 }
8675
8676 void
8677 nsFrame::Trace(const char* aMethod, bool aEnter, nsReflowStatus aStatus)
8678 {
8679 if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) {
8680 char tagbuf[40];
8681 GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
8682 PR_LogPrint("%s: %s %s, status=%scomplete%s",
8683 tagbuf, aEnter ? "enter" : "exit", aMethod,
8684 NS_FRAME_IS_NOT_COMPLETE(aStatus) ? "not" : "",
8685 (NS_FRAME_REFLOW_NEXTINFLOW & aStatus) ? "+reflow" : "");
8686 }
8687 }
8688
8689 void
8690 nsFrame::TraceMsg(const char* aFormatString, ...)
8691 {
8692 if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) {
8693 // Format arguments into a buffer
8694 char argbuf[200];
8695 va_list ap;
8696 va_start(ap, aFormatString);
8697 PR_vsnprintf(argbuf, sizeof(argbuf), aFormatString, ap);
8698 va_end(ap);
8699
8700 char tagbuf[40];
8701 GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
8702 PR_LogPrint("%s: %s", tagbuf, argbuf);
8703 }
8704 }
8705
8706 void
8707 nsFrame::VerifyDirtyBitSet(const nsFrameList& aFrameList)
8708 {
8709 for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
8710 NS_ASSERTION(e.get()->GetStateBits() & NS_FRAME_IS_DIRTY,
8711 "dirty bit not set");
8712 }
8713 }
8714
8715 // Start Display Reflow
8716 #ifdef DEBUG
8717
8718 DR_cookie::DR_cookie(nsPresContext* aPresContext,
8719 nsIFrame* aFrame,
8720 const nsHTMLReflowState& aReflowState,
8721 nsHTMLReflowMetrics& aMetrics,
8722 nsReflowStatus& aStatus)
8723 :mPresContext(aPresContext), mFrame(aFrame), mReflowState(aReflowState), mMetrics(aMetrics), mStatus(aStatus)
8724 {
8725 MOZ_COUNT_CTOR(DR_cookie);
8726 mValue = nsFrame::DisplayReflowEnter(aPresContext, mFrame, mReflowState);
8727 }
8728
8729 DR_cookie::~DR_cookie()
8730 {
8731 MOZ_COUNT_DTOR(DR_cookie);
8732 nsFrame::DisplayReflowExit(mPresContext, mFrame, mMetrics, mStatus, mValue);
8733 }
8734
8735 DR_layout_cookie::DR_layout_cookie(nsIFrame* aFrame)
8736 : mFrame(aFrame)
8737 {
8738 MOZ_COUNT_CTOR(DR_layout_cookie);
8739 mValue = nsFrame::DisplayLayoutEnter(mFrame);
8740 }
8741
8742 DR_layout_cookie::~DR_layout_cookie()
8743 {
8744 MOZ_COUNT_DTOR(DR_layout_cookie);
8745 nsFrame::DisplayLayoutExit(mFrame, mValue);
8746 }
8747
8748 DR_intrinsic_width_cookie::DR_intrinsic_width_cookie(
8749 nsIFrame* aFrame,
8750 const char* aType,
8751 nscoord& aResult)
8752 : mFrame(aFrame)
8753 , mType(aType)
8754 , mResult(aResult)
8755 {
8756 MOZ_COUNT_CTOR(DR_intrinsic_width_cookie);
8757 mValue = nsFrame::DisplayIntrinsicWidthEnter(mFrame, mType);
8758 }
8759
8760 DR_intrinsic_width_cookie::~DR_intrinsic_width_cookie()
8761 {
8762 MOZ_COUNT_DTOR(DR_intrinsic_width_cookie);
8763 nsFrame::DisplayIntrinsicWidthExit(mFrame, mType, mResult, mValue);
8764 }
8765
8766 DR_intrinsic_size_cookie::DR_intrinsic_size_cookie(
8767 nsIFrame* aFrame,
8768 const char* aType,
8769 nsSize& aResult)
8770 : mFrame(aFrame)
8771 , mType(aType)
8772 , mResult(aResult)
8773 {
8774 MOZ_COUNT_CTOR(DR_intrinsic_size_cookie);
8775 mValue = nsFrame::DisplayIntrinsicSizeEnter(mFrame, mType);
8776 }
8777
8778 DR_intrinsic_size_cookie::~DR_intrinsic_size_cookie()
8779 {
8780 MOZ_COUNT_DTOR(DR_intrinsic_size_cookie);
8781 nsFrame::DisplayIntrinsicSizeExit(mFrame, mType, mResult, mValue);
8782 }
8783
8784 DR_init_constraints_cookie::DR_init_constraints_cookie(
8785 nsIFrame* aFrame,
8786 nsHTMLReflowState* aState,
8787 nscoord aCBWidth,
8788 nscoord aCBHeight,
8789 const nsMargin* aMargin,
8790 const nsMargin* aPadding)
8791 : mFrame(aFrame)
8792 , mState(aState)
8793 {
8794 MOZ_COUNT_CTOR(DR_init_constraints_cookie);
8795 mValue = nsHTMLReflowState::DisplayInitConstraintsEnter(mFrame, mState,
8796 aCBWidth, aCBHeight,
8797 aMargin, aPadding);
8798 }
8799
8800 DR_init_constraints_cookie::~DR_init_constraints_cookie()
8801 {
8802 MOZ_COUNT_DTOR(DR_init_constraints_cookie);
8803 nsHTMLReflowState::DisplayInitConstraintsExit(mFrame, mState, mValue);
8804 }
8805
8806 DR_init_offsets_cookie::DR_init_offsets_cookie(
8807 nsIFrame* aFrame,
8808 nsCSSOffsetState* aState,
8809 nscoord aHorizontalPercentBasis,
8810 nscoord aVerticalPercentBasis,
8811 const nsMargin* aMargin,
8812 const nsMargin* aPadding)
8813 : mFrame(aFrame)
8814 , mState(aState)
8815 {
8816 MOZ_COUNT_CTOR(DR_init_offsets_cookie);
8817 mValue = nsCSSOffsetState::DisplayInitOffsetsEnter(mFrame, mState,
8818 aHorizontalPercentBasis,
8819 aVerticalPercentBasis,
8820 aMargin, aPadding);
8821 }
8822
8823 DR_init_offsets_cookie::~DR_init_offsets_cookie()
8824 {
8825 MOZ_COUNT_DTOR(DR_init_offsets_cookie);
8826 nsCSSOffsetState::DisplayInitOffsetsExit(mFrame, mState, mValue);
8827 }
8828
8829 DR_init_type_cookie::DR_init_type_cookie(
8830 nsIFrame* aFrame,
8831 nsHTMLReflowState* aState)
8832 : mFrame(aFrame)
8833 , mState(aState)
8834 {
8835 MOZ_COUNT_CTOR(DR_init_type_cookie);
8836 mValue = nsHTMLReflowState::DisplayInitFrameTypeEnter(mFrame, mState);
8837 }
8838
8839 DR_init_type_cookie::~DR_init_type_cookie()
8840 {
8841 MOZ_COUNT_DTOR(DR_init_type_cookie);
8842 nsHTMLReflowState::DisplayInitFrameTypeExit(mFrame, mState, mValue);
8843 }
8844
8845 struct DR_FrameTypeInfo;
8846 struct DR_FrameTreeNode;
8847 struct DR_Rule;
8848
8849 struct DR_State
8850 {
8851 DR_State();
8852 ~DR_State();
8853 void Init();
8854 void AddFrameTypeInfo(nsIAtom* aFrameType,
8855 const char* aFrameNameAbbrev,
8856 const char* aFrameName);
8857 DR_FrameTypeInfo* GetFrameTypeInfo(nsIAtom* aFrameType);
8858 DR_FrameTypeInfo* GetFrameTypeInfo(char* aFrameName);
8859 void InitFrameTypeTable();
8860 DR_FrameTreeNode* CreateTreeNode(nsIFrame* aFrame,
8861 const nsHTMLReflowState* aReflowState);
8862 void FindMatchingRule(DR_FrameTreeNode& aNode);
8863 bool RuleMatches(DR_Rule& aRule,
8864 DR_FrameTreeNode& aNode);
8865 bool GetToken(FILE* aFile,
8866 char* aBuf,
8867 size_t aBufSize);
8868 DR_Rule* ParseRule(FILE* aFile);
8869 void ParseRulesFile();
8870 void AddRule(nsTArray<DR_Rule*>& aRules,
8871 DR_Rule& aRule);
8872 bool IsWhiteSpace(int c);
8873 bool GetNumber(char* aBuf,
8874 int32_t& aNumber);
8875 void PrettyUC(nscoord aSize,
8876 char* aBuf);
8877 void PrintMargin(const char* tag, const nsMargin* aMargin);
8878 void DisplayFrameTypeInfo(nsIFrame* aFrame,
8879 int32_t aIndent);
8880 void DeleteTreeNode(DR_FrameTreeNode& aNode);
8881
8882 bool mInited;
8883 bool mActive;
8884 int32_t mCount;
8885 int32_t mAssert;
8886 int32_t mIndent;
8887 bool mIndentUndisplayedFrames;
8888 bool mDisplayPixelErrors;
8889 nsTArray<DR_Rule*> mWildRules;
8890 nsTArray<DR_FrameTypeInfo> mFrameTypeTable;
8891 // reflow specific state
8892 nsTArray<DR_FrameTreeNode*> mFrameTreeLeaves;
8893 };
8894
8895 static DR_State *DR_state; // the one and only DR_State
8896
8897 struct DR_RulePart
8898 {
8899 DR_RulePart(nsIAtom* aFrameType) : mFrameType(aFrameType), mNext(0) {}
8900 void Destroy();
8901
8902 nsIAtom* mFrameType;
8903 DR_RulePart* mNext;
8904 };
8905
8906 void DR_RulePart::Destroy()
8907 {
8908 if (mNext) {
8909 mNext->Destroy();
8910 }
8911 delete this;
8912 }
8913
8914 struct DR_Rule
8915 {
8916 DR_Rule() : mLength(0), mTarget(nullptr), mDisplay(false) {
8917 MOZ_COUNT_CTOR(DR_Rule);
8918 }
8919 ~DR_Rule() {
8920 if (mTarget) mTarget->Destroy();
8921 MOZ_COUNT_DTOR(DR_Rule);
8922 }
8923 void AddPart(nsIAtom* aFrameType);
8924
8925 uint32_t mLength;
8926 DR_RulePart* mTarget;
8927 bool mDisplay;
8928 };
8929
8930 void DR_Rule::AddPart(nsIAtom* aFrameType)
8931 {
8932 DR_RulePart* newPart = new DR_RulePart(aFrameType);
8933 newPart->mNext = mTarget;
8934 mTarget = newPart;
8935 mLength++;
8936 }
8937
8938 struct DR_FrameTypeInfo
8939 {
8940 DR_FrameTypeInfo(nsIAtom* aFrmeType, const char* aFrameNameAbbrev, const char* aFrameName);
8941 ~DR_FrameTypeInfo() {
8942 int32_t numElements;
8943 numElements = mRules.Length();
8944 for (int32_t i = numElements - 1; i >= 0; i--) {
8945 delete mRules.ElementAt(i);
8946 }
8947 }
8948
8949 nsIAtom* mType;
8950 char mNameAbbrev[16];
8951 char mName[32];
8952 nsTArray<DR_Rule*> mRules;
8953 private:
8954 DR_FrameTypeInfo& operator=(const DR_FrameTypeInfo&) MOZ_DELETE;
8955 };
8956
8957 DR_FrameTypeInfo::DR_FrameTypeInfo(nsIAtom* aFrameType,
8958 const char* aFrameNameAbbrev,
8959 const char* aFrameName)
8960 {
8961 mType = aFrameType;
8962 PL_strncpyz(mNameAbbrev, aFrameNameAbbrev, sizeof(mNameAbbrev));
8963 PL_strncpyz(mName, aFrameName, sizeof(mName));
8964 }
8965
8966 struct DR_FrameTreeNode
8967 {
8968 DR_FrameTreeNode(nsIFrame* aFrame, DR_FrameTreeNode* aParent) : mFrame(aFrame), mParent(aParent), mDisplay(0), mIndent(0)
8969 {
8970 MOZ_COUNT_CTOR(DR_FrameTreeNode);
8971 }
8972
8973 ~DR_FrameTreeNode()
8974 {
8975 MOZ_COUNT_DTOR(DR_FrameTreeNode);
8976 }
8977
8978 nsIFrame* mFrame;
8979 DR_FrameTreeNode* mParent;
8980 bool mDisplay;
8981 uint32_t mIndent;
8982 };
8983
8984 // DR_State implementation
8985
8986 DR_State::DR_State()
8987 : mInited(false), mActive(false), mCount(0), mAssert(-1), mIndent(0),
8988 mIndentUndisplayedFrames(false), mDisplayPixelErrors(false)
8989 {
8990 MOZ_COUNT_CTOR(DR_State);
8991 }
8992
8993 void DR_State::Init()
8994 {
8995 char* env = PR_GetEnv("GECKO_DISPLAY_REFLOW_ASSERT");
8996 int32_t num;
8997 if (env) {
8998 if (GetNumber(env, num))
8999 mAssert = num;
9000 else
9001 printf("GECKO_DISPLAY_REFLOW_ASSERT - invalid value = %s", env);
9002 }
9003
9004 env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_START");
9005 if (env) {
9006 if (GetNumber(env, num))
9007 mIndent = num;
9008 else
9009 printf("GECKO_DISPLAY_REFLOW_INDENT_START - invalid value = %s", env);
9010 }
9011
9012 env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES");
9013 if (env) {
9014 if (GetNumber(env, num))
9015 mIndentUndisplayedFrames = num;
9016 else
9017 printf("GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES - invalid value = %s", env);
9018 }
9019
9020 env = PR_GetEnv("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS");
9021 if (env) {
9022 if (GetNumber(env, num))
9023 mDisplayPixelErrors = num;
9024 else
9025 printf("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS - invalid value = %s", env);
9026 }
9027
9028 InitFrameTypeTable();
9029 ParseRulesFile();
9030 mInited = true;
9031 }
9032
9033 DR_State::~DR_State()
9034 {
9035 MOZ_COUNT_DTOR(DR_State);
9036 int32_t numElements, i;
9037 numElements = mWildRules.Length();
9038 for (i = numElements - 1; i >= 0; i--) {
9039 delete mWildRules.ElementAt(i);
9040 }
9041 numElements = mFrameTreeLeaves.Length();
9042 for (i = numElements - 1; i >= 0; i--) {
9043 delete mFrameTreeLeaves.ElementAt(i);
9044 }
9045 }
9046
9047 bool DR_State::GetNumber(char* aBuf,
9048 int32_t& aNumber)
9049 {
9050 if (sscanf(aBuf, "%d", &aNumber) > 0)
9051 return true;
9052 else
9053 return false;
9054 }
9055
9056 bool DR_State::IsWhiteSpace(int c) {
9057 return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r');
9058 }
9059
9060 bool DR_State::GetToken(FILE* aFile,
9061 char* aBuf,
9062 size_t aBufSize)
9063 {
9064 bool haveToken = false;
9065 aBuf[0] = 0;
9066 // get the 1st non whitespace char
9067 int c = -1;
9068 for (c = getc(aFile); (c > 0) && IsWhiteSpace(c); c = getc(aFile)) {
9069 }
9070
9071 if (c > 0) {
9072 haveToken = true;
9073 aBuf[0] = c;
9074 // get everything up to the next whitespace char
9075 size_t cX;
9076 for (cX = 1; cX + 1 < aBufSize ; cX++) {
9077 c = getc(aFile);
9078 if (c < 0) { // EOF
9079 ungetc(' ', aFile);
9080 break;
9081 }
9082 else {
9083 if (IsWhiteSpace(c)) {
9084 break;
9085 }
9086 else {
9087 aBuf[cX] = c;
9088 }
9089 }
9090 }
9091 aBuf[cX] = 0;
9092 }
9093 return haveToken;
9094 }
9095
9096 DR_Rule* DR_State::ParseRule(FILE* aFile)
9097 {
9098 char buf[128];
9099 int32_t doDisplay;
9100 DR_Rule* rule = nullptr;
9101 while (GetToken(aFile, buf, sizeof(buf))) {
9102 if (GetNumber(buf, doDisplay)) {
9103 if (rule) {
9104 rule->mDisplay = !!doDisplay;
9105 break;
9106 }
9107 else {
9108 printf("unexpected token - %s \n", buf);
9109 }
9110 }
9111 else {
9112 if (!rule) {
9113 rule = new DR_Rule;
9114 }
9115 if (strcmp(buf, "*") == 0) {
9116 rule->AddPart(nullptr);
9117 }
9118 else {
9119 DR_FrameTypeInfo* info = GetFrameTypeInfo(buf);
9120 if (info) {
9121 rule->AddPart(info->mType);
9122 }
9123 else {
9124 printf("invalid frame type - %s \n", buf);
9125 }
9126 }
9127 }
9128 }
9129 return rule;
9130 }
9131
9132 void DR_State::AddRule(nsTArray<DR_Rule*>& aRules,
9133 DR_Rule& aRule)
9134 {
9135 int32_t numRules = aRules.Length();
9136 for (int32_t ruleX = 0; ruleX < numRules; ruleX++) {
9137 DR_Rule* rule = aRules.ElementAt(ruleX);
9138 NS_ASSERTION(rule, "program error");
9139 if (aRule.mLength > rule->mLength) {
9140 aRules.InsertElementAt(ruleX, &aRule);
9141 return;
9142 }
9143 }
9144 aRules.AppendElement(&aRule);
9145 }
9146
9147 void DR_State::ParseRulesFile()
9148 {
9149 char* path = PR_GetEnv("GECKO_DISPLAY_REFLOW_RULES_FILE");
9150 if (path) {
9151 FILE* inFile = fopen(path, "r");
9152 if (inFile) {
9153 for (DR_Rule* rule = ParseRule(inFile); rule; rule = ParseRule(inFile)) {
9154 if (rule->mTarget) {
9155 nsIAtom* fType = rule->mTarget->mFrameType;
9156 if (fType) {
9157 DR_FrameTypeInfo* info = GetFrameTypeInfo(fType);
9158 if (info) {
9159 AddRule(info->mRules, *rule);
9160 }
9161 }
9162 else {
9163 AddRule(mWildRules, *rule);
9164 }
9165 mActive = true;
9166 }
9167 }
9168 }
9169 }
9170 }
9171
9172
9173 void DR_State::AddFrameTypeInfo(nsIAtom* aFrameType,
9174 const char* aFrameNameAbbrev,
9175 const char* aFrameName)
9176 {
9177 mFrameTypeTable.AppendElement(DR_FrameTypeInfo(aFrameType, aFrameNameAbbrev, aFrameName));
9178 }
9179
9180 DR_FrameTypeInfo* DR_State::GetFrameTypeInfo(nsIAtom* aFrameType)
9181 {
9182 int32_t numEntries = mFrameTypeTable.Length();
9183 NS_ASSERTION(numEntries != 0, "empty FrameTypeTable");
9184 for (int32_t i = 0; i < numEntries; i++) {
9185 DR_FrameTypeInfo& info = mFrameTypeTable.ElementAt(i);
9186 if (info.mType == aFrameType) {
9187 return &info;
9188 }
9189 }
9190 return &mFrameTypeTable.ElementAt(numEntries - 1); // return unknown frame type
9191 }
9192
9193 DR_FrameTypeInfo* DR_State::GetFrameTypeInfo(char* aFrameName)
9194 {
9195 int32_t numEntries = mFrameTypeTable.Length();
9196 NS_ASSERTION(numEntries != 0, "empty FrameTypeTable");
9197 for (int32_t i = 0; i < numEntries; i++) {
9198 DR_FrameTypeInfo& info = mFrameTypeTable.ElementAt(i);
9199 if ((strcmp(aFrameName, info.mName) == 0) || (strcmp(aFrameName, info.mNameAbbrev) == 0)) {
9200 return &info;
9201 }
9202 }
9203 return &mFrameTypeTable.ElementAt(numEntries - 1); // return unknown frame type
9204 }
9205
9206 void DR_State::InitFrameTypeTable()
9207 {
9208 AddFrameTypeInfo(nsGkAtoms::blockFrame, "block", "block");
9209 AddFrameTypeInfo(nsGkAtoms::brFrame, "br", "br");
9210 AddFrameTypeInfo(nsGkAtoms::bulletFrame, "bullet", "bullet");
9211 AddFrameTypeInfo(nsGkAtoms::colorControlFrame, "color", "colorControl");
9212 AddFrameTypeInfo(nsGkAtoms::gfxButtonControlFrame, "button", "gfxButtonControl");
9213 AddFrameTypeInfo(nsGkAtoms::HTMLButtonControlFrame, "HTMLbutton", "HTMLButtonControl");
9214 AddFrameTypeInfo(nsGkAtoms::HTMLCanvasFrame, "HTMLCanvas","HTMLCanvas");
9215 AddFrameTypeInfo(nsGkAtoms::subDocumentFrame, "subdoc", "subDocument");
9216 AddFrameTypeInfo(nsGkAtoms::imageFrame, "img", "image");
9217 AddFrameTypeInfo(nsGkAtoms::inlineFrame, "inline", "inline");
9218 AddFrameTypeInfo(nsGkAtoms::letterFrame, "letter", "letter");
9219 AddFrameTypeInfo(nsGkAtoms::lineFrame, "line", "line");
9220 AddFrameTypeInfo(nsGkAtoms::listControlFrame, "select", "select");
9221 AddFrameTypeInfo(nsGkAtoms::objectFrame, "obj", "object");
9222 AddFrameTypeInfo(nsGkAtoms::pageFrame, "page", "page");
9223 AddFrameTypeInfo(nsGkAtoms::placeholderFrame, "place", "placeholder");
9224 AddFrameTypeInfo(nsGkAtoms::canvasFrame, "canvas", "canvas");
9225 AddFrameTypeInfo(nsGkAtoms::rootFrame, "root", "root");
9226 AddFrameTypeInfo(nsGkAtoms::scrollFrame, "scroll", "scroll");
9227 AddFrameTypeInfo(nsGkAtoms::tableCaptionFrame, "caption", "tableCaption");
9228 AddFrameTypeInfo(nsGkAtoms::tableCellFrame, "cell", "tableCell");
9229 AddFrameTypeInfo(nsGkAtoms::bcTableCellFrame, "bcCell", "bcTableCell");
9230 AddFrameTypeInfo(nsGkAtoms::tableColFrame, "col", "tableCol");
9231 AddFrameTypeInfo(nsGkAtoms::tableColGroupFrame, "colG", "tableColGroup");
9232 AddFrameTypeInfo(nsGkAtoms::tableFrame, "tbl", "table");
9233 AddFrameTypeInfo(nsGkAtoms::tableOuterFrame, "tblO", "tableOuter");
9234 AddFrameTypeInfo(nsGkAtoms::tableRowGroupFrame, "rowG", "tableRowGroup");
9235 AddFrameTypeInfo(nsGkAtoms::tableRowFrame, "row", "tableRow");
9236 AddFrameTypeInfo(nsGkAtoms::textInputFrame, "textCtl", "textInput");
9237 AddFrameTypeInfo(nsGkAtoms::textFrame, "text", "text");
9238 AddFrameTypeInfo(nsGkAtoms::viewportFrame, "VP", "viewport");
9239 #ifdef MOZ_XUL
9240 AddFrameTypeInfo(nsGkAtoms::XULLabelFrame, "XULLabel", "XULLabel");
9241 AddFrameTypeInfo(nsGkAtoms::boxFrame, "Box", "Box");
9242 AddFrameTypeInfo(nsGkAtoms::sliderFrame, "Slider", "Slider");
9243 AddFrameTypeInfo(nsGkAtoms::popupSetFrame, "PopupSet", "PopupSet");
9244 #endif
9245 AddFrameTypeInfo(nullptr, "unknown", "unknown");
9246 }
9247
9248
9249 void DR_State::DisplayFrameTypeInfo(nsIFrame* aFrame,
9250 int32_t aIndent)
9251 {
9252 DR_FrameTypeInfo* frameTypeInfo = GetFrameTypeInfo(aFrame->GetType());
9253 if (frameTypeInfo) {
9254 for (int32_t i = 0; i < aIndent; i++) {
9255 printf(" ");
9256 }
9257 if(!strcmp(frameTypeInfo->mNameAbbrev, "unknown")) {
9258 if (aFrame) {
9259 nsAutoString name;
9260 aFrame->GetFrameName(name);
9261 printf("%s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)aFrame);
9262 }
9263 else {
9264 printf("%s %p ", frameTypeInfo->mNameAbbrev, (void*)aFrame);
9265 }
9266 }
9267 else {
9268 printf("%s %p ", frameTypeInfo->mNameAbbrev, (void*)aFrame);
9269 }
9270 }
9271 }
9272
9273 bool DR_State::RuleMatches(DR_Rule& aRule,
9274 DR_FrameTreeNode& aNode)
9275 {
9276 NS_ASSERTION(aRule.mTarget, "program error");
9277
9278 DR_RulePart* rulePart;
9279 DR_FrameTreeNode* parentNode;
9280 for (rulePart = aRule.mTarget->mNext, parentNode = aNode.mParent;
9281 rulePart && parentNode;
9282 rulePart = rulePart->mNext, parentNode = parentNode->mParent) {
9283 if (rulePart->mFrameType) {
9284 if (parentNode->mFrame) {
9285 if (rulePart->mFrameType != parentNode->mFrame->GetType()) {
9286 return false;
9287 }
9288 }
9289 else NS_ASSERTION(false, "program error");
9290 }
9291 // else wild card match
9292 }
9293 return true;
9294 }
9295
9296 void DR_State::FindMatchingRule(DR_FrameTreeNode& aNode)
9297 {
9298 if (!aNode.mFrame) {
9299 NS_ASSERTION(false, "invalid DR_FrameTreeNode \n");
9300 return;
9301 }
9302
9303 bool matchingRule = false;
9304
9305 DR_FrameTypeInfo* info = GetFrameTypeInfo(aNode.mFrame->GetType());
9306 NS_ASSERTION(info, "program error");
9307 int32_t numRules = info->mRules.Length();
9308 for (int32_t ruleX = 0; ruleX < numRules; ruleX++) {
9309 DR_Rule* rule = info->mRules.ElementAt(ruleX);
9310 if (rule && RuleMatches(*rule, aNode)) {
9311 aNode.mDisplay = rule->mDisplay;
9312 matchingRule = true;
9313 break;
9314 }
9315 }
9316 if (!matchingRule) {
9317 int32_t numWildRules = mWildRules.Length();
9318 for (int32_t ruleX = 0; ruleX < numWildRules; ruleX++) {
9319 DR_Rule* rule = mWildRules.ElementAt(ruleX);
9320 if (rule && RuleMatches(*rule, aNode)) {
9321 aNode.mDisplay = rule->mDisplay;
9322 break;
9323 }
9324 }
9325 }
9326 }
9327
9328 DR_FrameTreeNode* DR_State::CreateTreeNode(nsIFrame* aFrame,
9329 const nsHTMLReflowState* aReflowState)
9330 {
9331 // find the frame of the parent reflow state (usually just the parent of aFrame)
9332 nsIFrame* parentFrame;
9333 if (aReflowState) {
9334 const nsHTMLReflowState* parentRS = aReflowState->parentReflowState;
9335 parentFrame = (parentRS) ? parentRS->frame : nullptr;
9336 } else {
9337 parentFrame = aFrame->GetParent();
9338 }
9339
9340 // find the parent tree node leaf
9341 DR_FrameTreeNode* parentNode = nullptr;
9342
9343 DR_FrameTreeNode* lastLeaf = nullptr;
9344 if(mFrameTreeLeaves.Length())
9345 lastLeaf = mFrameTreeLeaves.ElementAt(mFrameTreeLeaves.Length() - 1);
9346 if (lastLeaf) {
9347 for (parentNode = lastLeaf; parentNode && (parentNode->mFrame != parentFrame); parentNode = parentNode->mParent) {
9348 }
9349 }
9350 DR_FrameTreeNode* newNode = new DR_FrameTreeNode(aFrame, parentNode);
9351 FindMatchingRule(*newNode);
9352
9353 newNode->mIndent = mIndent;
9354 if (newNode->mDisplay || mIndentUndisplayedFrames) {
9355 ++mIndent;
9356 }
9357
9358 if (lastLeaf && (lastLeaf == parentNode)) {
9359 mFrameTreeLeaves.RemoveElementAt(mFrameTreeLeaves.Length() - 1);
9360 }
9361 mFrameTreeLeaves.AppendElement(newNode);
9362 mCount++;
9363
9364 return newNode;
9365 }
9366
9367 void DR_State::PrettyUC(nscoord aSize,
9368 char* aBuf)
9369 {
9370 if (NS_UNCONSTRAINEDSIZE == aSize) {
9371 strcpy(aBuf, "UC");
9372 }
9373 else {
9374 if ((nscoord)0xdeadbeefU == aSize)
9375 {
9376 strcpy(aBuf, "deadbeef");
9377 }
9378 else {
9379 sprintf(aBuf, "%d", aSize);
9380 }
9381 }
9382 }
9383
9384 void DR_State::PrintMargin(const char *tag, const nsMargin* aMargin)
9385 {
9386 if (aMargin) {
9387 char t[16], r[16], b[16], l[16];
9388 PrettyUC(aMargin->top, t);
9389 PrettyUC(aMargin->right, r);
9390 PrettyUC(aMargin->bottom, b);
9391 PrettyUC(aMargin->left, l);
9392 printf(" %s=%s,%s,%s,%s", tag, t, r, b, l);
9393 } else {
9394 // use %p here for consistency with other null-pointer printouts
9395 printf(" %s=%p", tag, (void*)aMargin);
9396 }
9397 }
9398
9399 void DR_State::DeleteTreeNode(DR_FrameTreeNode& aNode)
9400 {
9401 mFrameTreeLeaves.RemoveElement(&aNode);
9402 int32_t numLeaves = mFrameTreeLeaves.Length();
9403 if ((0 == numLeaves) || (aNode.mParent != mFrameTreeLeaves.ElementAt(numLeaves - 1))) {
9404 mFrameTreeLeaves.AppendElement(aNode.mParent);
9405 }
9406
9407 if (aNode.mDisplay || mIndentUndisplayedFrames) {
9408 --mIndent;
9409 }
9410 // delete the tree node
9411 delete &aNode;
9412 }
9413
9414 static void
9415 CheckPixelError(nscoord aSize,
9416 int32_t aPixelToTwips)
9417 {
9418 if (NS_UNCONSTRAINEDSIZE != aSize) {
9419 if ((aSize % aPixelToTwips) > 0) {
9420 printf("VALUE %d is not a whole pixel \n", aSize);
9421 }
9422 }
9423 }
9424
9425 static void DisplayReflowEnterPrint(nsPresContext* aPresContext,
9426 nsIFrame* aFrame,
9427 const nsHTMLReflowState& aReflowState,
9428 DR_FrameTreeNode& aTreeNode,
9429 bool aChanged)
9430 {
9431 if (aTreeNode.mDisplay) {
9432 DR_state->DisplayFrameTypeInfo(aFrame, aTreeNode.mIndent);
9433
9434 char width[16];
9435 char height[16];
9436
9437 DR_state->PrettyUC(aReflowState.AvailableWidth(), width);
9438 DR_state->PrettyUC(aReflowState.AvailableHeight(), height);
9439 printf("Reflow a=%s,%s ", width, height);
9440
9441 DR_state->PrettyUC(aReflowState.ComputedWidth(), width);
9442 DR_state->PrettyUC(aReflowState.ComputedHeight(), height);
9443 printf("c=%s,%s ", width, height);
9444
9445 if (aFrame->GetStateBits() & NS_FRAME_IS_DIRTY)
9446 printf("dirty ");
9447
9448 if (aFrame->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN)
9449 printf("dirty-children ");
9450
9451 if (aReflowState.mFlags.mSpecialHeightReflow)
9452 printf("special-height ");
9453
9454 if (aReflowState.mFlags.mHResize)
9455 printf("h-resize ");
9456
9457 if (aReflowState.mFlags.mVResize)
9458 printf("v-resize ");
9459
9460 nsIFrame* inFlow = aFrame->GetPrevInFlow();
9461 if (inFlow) {
9462 printf("pif=%p ", (void*)inFlow);
9463 }
9464 inFlow = aFrame->GetNextInFlow();
9465 if (inFlow) {
9466 printf("nif=%p ", (void*)inFlow);
9467 }
9468 if (aChanged)
9469 printf("CHANGED \n");
9470 else
9471 printf("cnt=%d \n", DR_state->mCount);
9472 if (DR_state->mDisplayPixelErrors) {
9473 int32_t p2t = aPresContext->AppUnitsPerDevPixel();
9474 CheckPixelError(aReflowState.AvailableWidth(), p2t);
9475 CheckPixelError(aReflowState.AvailableHeight(), p2t);
9476 CheckPixelError(aReflowState.ComputedWidth(), p2t);
9477 CheckPixelError(aReflowState.ComputedHeight(), p2t);
9478 }
9479 }
9480 }
9481
9482 void* nsFrame::DisplayReflowEnter(nsPresContext* aPresContext,
9483 nsIFrame* aFrame,
9484 const nsHTMLReflowState& aReflowState)
9485 {
9486 if (!DR_state->mInited) DR_state->Init();
9487 if (!DR_state->mActive) return nullptr;
9488
9489 NS_ASSERTION(aFrame, "invalid call");
9490
9491 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, &aReflowState);
9492 if (treeNode) {
9493 DisplayReflowEnterPrint(aPresContext, aFrame, aReflowState, *treeNode, false);
9494 }
9495 return treeNode;
9496 }
9497
9498 void* nsFrame::DisplayLayoutEnter(nsIFrame* aFrame)
9499 {
9500 if (!DR_state->mInited) DR_state->Init();
9501 if (!DR_state->mActive) return nullptr;
9502
9503 NS_ASSERTION(aFrame, "invalid call");
9504
9505 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
9506 if (treeNode && treeNode->mDisplay) {
9507 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
9508 printf("Layout\n");
9509 }
9510 return treeNode;
9511 }
9512
9513 void* nsFrame::DisplayIntrinsicWidthEnter(nsIFrame* aFrame,
9514 const char* aType)
9515 {
9516 if (!DR_state->mInited) DR_state->Init();
9517 if (!DR_state->mActive) return nullptr;
9518
9519 NS_ASSERTION(aFrame, "invalid call");
9520
9521 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
9522 if (treeNode && treeNode->mDisplay) {
9523 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
9524 printf("Get%sWidth\n", aType);
9525 }
9526 return treeNode;
9527 }
9528
9529 void* nsFrame::DisplayIntrinsicSizeEnter(nsIFrame* aFrame,
9530 const char* aType)
9531 {
9532 if (!DR_state->mInited) DR_state->Init();
9533 if (!DR_state->mActive) return nullptr;
9534
9535 NS_ASSERTION(aFrame, "invalid call");
9536
9537 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
9538 if (treeNode && treeNode->mDisplay) {
9539 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
9540 printf("Get%sSize\n", aType);
9541 }
9542 return treeNode;
9543 }
9544
9545 void nsFrame::DisplayReflowExit(nsPresContext* aPresContext,
9546 nsIFrame* aFrame,
9547 nsHTMLReflowMetrics& aMetrics,
9548 nsReflowStatus aStatus,
9549 void* aFrameTreeNode)
9550 {
9551 if (!DR_state->mActive) return;
9552
9553 NS_ASSERTION(aFrame, "DisplayReflowExit - invalid call");
9554 if (!aFrameTreeNode) return;
9555
9556 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
9557 if (treeNode->mDisplay) {
9558 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
9559
9560 char width[16];
9561 char height[16];
9562 char x[16];
9563 char y[16];
9564 DR_state->PrettyUC(aMetrics.Width(), width);
9565 DR_state->PrettyUC(aMetrics.Height(), height);
9566 printf("Reflow d=%s,%s", width, height);
9567
9568 if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) {
9569 printf(" status=0x%x", aStatus);
9570 }
9571 if (aFrame->HasOverflowAreas()) {
9572 DR_state->PrettyUC(aMetrics.VisualOverflow().x, x);
9573 DR_state->PrettyUC(aMetrics.VisualOverflow().y, y);
9574 DR_state->PrettyUC(aMetrics.VisualOverflow().width, width);
9575 DR_state->PrettyUC(aMetrics.VisualOverflow().height, height);
9576 printf(" vis-o=(%s,%s) %s x %s", x, y, width, height);
9577
9578 nsRect storedOverflow = aFrame->GetVisualOverflowRect();
9579 DR_state->PrettyUC(storedOverflow.x, x);
9580 DR_state->PrettyUC(storedOverflow.y, y);
9581 DR_state->PrettyUC(storedOverflow.width, width);
9582 DR_state->PrettyUC(storedOverflow.height, height);
9583 printf(" vis-sto=(%s,%s) %s x %s", x, y, width, height);
9584
9585 DR_state->PrettyUC(aMetrics.ScrollableOverflow().x, x);
9586 DR_state->PrettyUC(aMetrics.ScrollableOverflow().y, y);
9587 DR_state->PrettyUC(aMetrics.ScrollableOverflow().width, width);
9588 DR_state->PrettyUC(aMetrics.ScrollableOverflow().height, height);
9589 printf(" scr-o=(%s,%s) %s x %s", x, y, width, height);
9590
9591 storedOverflow = aFrame->GetScrollableOverflowRect();
9592 DR_state->PrettyUC(storedOverflow.x, x);
9593 DR_state->PrettyUC(storedOverflow.y, y);
9594 DR_state->PrettyUC(storedOverflow.width, width);
9595 DR_state->PrettyUC(storedOverflow.height, height);
9596 printf(" scr-sto=(%s,%s) %s x %s", x, y, width, height);
9597 }
9598 printf("\n");
9599 if (DR_state->mDisplayPixelErrors) {
9600 int32_t p2t = aPresContext->AppUnitsPerDevPixel();
9601 CheckPixelError(aMetrics.Width(), p2t);
9602 CheckPixelError(aMetrics.Height(), p2t);
9603 }
9604 }
9605 DR_state->DeleteTreeNode(*treeNode);
9606 }
9607
9608 void nsFrame::DisplayLayoutExit(nsIFrame* aFrame,
9609 void* aFrameTreeNode)
9610 {
9611 if (!DR_state->mActive) return;
9612
9613 NS_ASSERTION(aFrame, "non-null frame required");
9614 if (!aFrameTreeNode) return;
9615
9616 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
9617 if (treeNode->mDisplay) {
9618 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
9619 nsRect rect = aFrame->GetRect();
9620 printf("Layout=%d,%d,%d,%d\n", rect.x, rect.y, rect.width, rect.height);
9621 }
9622 DR_state->DeleteTreeNode(*treeNode);
9623 }
9624
9625 void nsFrame::DisplayIntrinsicWidthExit(nsIFrame* aFrame,
9626 const char* aType,
9627 nscoord aResult,
9628 void* aFrameTreeNode)
9629 {
9630 if (!DR_state->mActive) return;
9631
9632 NS_ASSERTION(aFrame, "non-null frame required");
9633 if (!aFrameTreeNode) return;
9634
9635 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
9636 if (treeNode->mDisplay) {
9637 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
9638 char width[16];
9639 DR_state->PrettyUC(aResult, width);
9640 printf("Get%sWidth=%s\n", aType, width);
9641 }
9642 DR_state->DeleteTreeNode(*treeNode);
9643 }
9644
9645 void nsFrame::DisplayIntrinsicSizeExit(nsIFrame* aFrame,
9646 const char* aType,
9647 nsSize aResult,
9648 void* aFrameTreeNode)
9649 {
9650 if (!DR_state->mActive) return;
9651
9652 NS_ASSERTION(aFrame, "non-null frame required");
9653 if (!aFrameTreeNode) return;
9654
9655 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
9656 if (treeNode->mDisplay) {
9657 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
9658
9659 char width[16];
9660 char height[16];
9661 DR_state->PrettyUC(aResult.width, width);
9662 DR_state->PrettyUC(aResult.height, height);
9663 printf("Get%sSize=%s,%s\n", aType, width, height);
9664 }
9665 DR_state->DeleteTreeNode(*treeNode);
9666 }
9667
9668 /* static */ void
9669 nsFrame::DisplayReflowStartup()
9670 {
9671 DR_state = new DR_State();
9672 }
9673
9674 /* static */ void
9675 nsFrame::DisplayReflowShutdown()
9676 {
9677 delete DR_state;
9678 DR_state = nullptr;
9679 }
9680
9681 void DR_cookie::Change() const
9682 {
9683 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)mValue;
9684 if (treeNode && treeNode->mDisplay) {
9685 DisplayReflowEnterPrint(mPresContext, mFrame, mReflowState, *treeNode, true);
9686 }
9687 }
9688
9689 /* static */ void*
9690 nsHTMLReflowState::DisplayInitConstraintsEnter(nsIFrame* aFrame,
9691 nsHTMLReflowState* aState,
9692 nscoord aContainingBlockWidth,
9693 nscoord aContainingBlockHeight,
9694 const nsMargin* aBorder,
9695 const nsMargin* aPadding)
9696 {
9697 NS_PRECONDITION(aFrame, "non-null frame required");
9698 NS_PRECONDITION(aState, "non-null state required");
9699
9700 if (!DR_state->mInited) DR_state->Init();
9701 if (!DR_state->mActive) return nullptr;
9702
9703 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, aState);
9704 if (treeNode && treeNode->mDisplay) {
9705 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
9706
9707 printf("InitConstraints parent=%p",
9708 (void*)aState->parentReflowState);
9709
9710 char width[16];
9711 char height[16];
9712
9713 DR_state->PrettyUC(aContainingBlockWidth, width);
9714 DR_state->PrettyUC(aContainingBlockHeight, height);
9715 printf(" cb=%s,%s", width, height);
9716
9717 DR_state->PrettyUC(aState->AvailableWidth(), width);
9718 DR_state->PrettyUC(aState->AvailableHeight(), height);
9719 printf(" as=%s,%s", width, height);
9720
9721 DR_state->PrintMargin("b", aBorder);
9722 DR_state->PrintMargin("p", aPadding);
9723 putchar('\n');
9724 }
9725 return treeNode;
9726 }
9727
9728 /* static */ void
9729 nsHTMLReflowState::DisplayInitConstraintsExit(nsIFrame* aFrame,
9730 nsHTMLReflowState* aState,
9731 void* aValue)
9732 {
9733 NS_PRECONDITION(aFrame, "non-null frame required");
9734 NS_PRECONDITION(aState, "non-null state required");
9735
9736 if (!DR_state->mActive) return;
9737 if (!aValue) return;
9738
9739 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue;
9740 if (treeNode->mDisplay) {
9741 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
9742 char cmiw[16], cw[16], cmxw[16], cmih[16], ch[16], cmxh[16];
9743 DR_state->PrettyUC(aState->ComputedMinWidth(), cmiw);
9744 DR_state->PrettyUC(aState->ComputedWidth(), cw);
9745 DR_state->PrettyUC(aState->ComputedMaxWidth(), cmxw);
9746 DR_state->PrettyUC(aState->ComputedMinHeight(), cmih);
9747 DR_state->PrettyUC(aState->ComputedHeight(), ch);
9748 DR_state->PrettyUC(aState->ComputedMaxHeight(), cmxh);
9749 printf("InitConstraints= cw=(%s <= %s <= %s) ch=(%s <= %s <= %s)",
9750 cmiw, cw, cmxw, cmih, ch, cmxh);
9751 DR_state->PrintMargin("co", &aState->ComputedPhysicalOffsets());
9752 putchar('\n');
9753 }
9754 DR_state->DeleteTreeNode(*treeNode);
9755 }
9756
9757
9758 /* static */ void*
9759 nsCSSOffsetState::DisplayInitOffsetsEnter(nsIFrame* aFrame,
9760 nsCSSOffsetState* aState,
9761 nscoord aHorizontalPercentBasis,
9762 nscoord aVerticalPercentBasis,
9763 const nsMargin* aBorder,
9764 const nsMargin* aPadding)
9765 {
9766 NS_PRECONDITION(aFrame, "non-null frame required");
9767 NS_PRECONDITION(aState, "non-null state required");
9768
9769 if (!DR_state->mInited) DR_state->Init();
9770 if (!DR_state->mActive) return nullptr;
9771
9772 // aState is not necessarily a nsHTMLReflowState
9773 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
9774 if (treeNode && treeNode->mDisplay) {
9775 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
9776
9777 char horizPctBasisStr[16];
9778 char vertPctBasisStr[16];
9779 DR_state->PrettyUC(aHorizontalPercentBasis, horizPctBasisStr);
9780 DR_state->PrettyUC(aVerticalPercentBasis, vertPctBasisStr);
9781 printf("InitOffsets pct_basis=%s,%s", horizPctBasisStr, vertPctBasisStr);
9782
9783 DR_state->PrintMargin("b", aBorder);
9784 DR_state->PrintMargin("p", aPadding);
9785 putchar('\n');
9786 }
9787 return treeNode;
9788 }
9789
9790 /* static */ void
9791 nsCSSOffsetState::DisplayInitOffsetsExit(nsIFrame* aFrame,
9792 nsCSSOffsetState* aState,
9793 void* aValue)
9794 {
9795 NS_PRECONDITION(aFrame, "non-null frame required");
9796 NS_PRECONDITION(aState, "non-null state required");
9797
9798 if (!DR_state->mActive) return;
9799 if (!aValue) return;
9800
9801 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue;
9802 if (treeNode->mDisplay) {
9803 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
9804 printf("InitOffsets=");
9805 DR_state->PrintMargin("m", &aState->ComputedPhysicalMargin());
9806 DR_state->PrintMargin("p", &aState->ComputedPhysicalPadding());
9807 DR_state->PrintMargin("p+b", &aState->ComputedPhysicalBorderPadding());
9808 putchar('\n');
9809 }
9810 DR_state->DeleteTreeNode(*treeNode);
9811 }
9812
9813 /* static */ void*
9814 nsHTMLReflowState::DisplayInitFrameTypeEnter(nsIFrame* aFrame,
9815 nsHTMLReflowState* aState)
9816 {
9817 NS_PRECONDITION(aFrame, "non-null frame required");
9818 NS_PRECONDITION(aState, "non-null state required");
9819
9820 if (!DR_state->mInited) DR_state->Init();
9821 if (!DR_state->mActive) return nullptr;
9822
9823 // we don't print anything here
9824 return DR_state->CreateTreeNode(aFrame, aState);
9825 }
9826
9827 /* static */ void
9828 nsHTMLReflowState::DisplayInitFrameTypeExit(nsIFrame* aFrame,
9829 nsHTMLReflowState* aState,
9830 void* aValue)
9831 {
9832 NS_PRECONDITION(aFrame, "non-null frame required");
9833 NS_PRECONDITION(aState, "non-null state required");
9834
9835 if (!DR_state->mActive) return;
9836 if (!aValue) return;
9837
9838 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue;
9839 if (treeNode->mDisplay) {
9840 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
9841 printf("InitFrameType");
9842
9843 const nsStyleDisplay *disp = aState->mStyleDisplay;
9844
9845 if (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
9846 printf(" out-of-flow");
9847 if (aFrame->GetPrevInFlow())
9848 printf(" prev-in-flow");
9849 if (aFrame->IsAbsolutelyPositioned())
9850 printf(" abspos");
9851 if (aFrame->IsFloating())
9852 printf(" float");
9853
9854 // This array must exactly match the NS_STYLE_DISPLAY constants.
9855 const char *const displayTypes[] = {
9856 "none", "block", "inline", "inline-block", "list-item", "marker",
9857 "run-in", "compact", "table", "inline-table", "table-row-group",
9858 "table-column", "table-column-group", "table-header-group",
9859 "table-footer-group", "table-row", "table-cell", "table-caption",
9860 "box", "inline-box",
9861 #ifdef MOZ_XUL
9862 "grid", "inline-grid", "grid-group", "grid-line", "stack",
9863 "inline-stack", "deck", "popup", "groupbox",
9864 #endif
9865 };
9866 if (disp->mDisplay >= ArrayLength(displayTypes))
9867 printf(" display=%u", disp->mDisplay);
9868 else
9869 printf(" display=%s", displayTypes[disp->mDisplay]);
9870
9871 // This array must exactly match the NS_CSS_FRAME_TYPE constants.
9872 const char *const cssFrameTypes[] = {
9873 "unknown", "inline", "block", "floating", "absolute", "internal-table"
9874 };
9875 nsCSSFrameType bareType = NS_FRAME_GET_TYPE(aState->mFrameType);
9876 bool repNoBlock = NS_FRAME_IS_REPLACED_NOBLOCK(aState->mFrameType);
9877 bool repBlock = NS_FRAME_IS_REPLACED_CONTAINS_BLOCK(aState->mFrameType);
9878
9879 if (bareType >= ArrayLength(cssFrameTypes)) {
9880 printf(" result=type %u", bareType);
9881 } else {
9882 printf(" result=%s", cssFrameTypes[bareType]);
9883 }
9884 printf("%s%s\n", repNoBlock ? " +rep" : "", repBlock ? " +repBlk" : "");
9885 }
9886 DR_state->DeleteTreeNode(*treeNode);
9887 }
9888
9889 #endif
9890 // End Display Reflow
9891
9892 #endif

mercurial