layout/base/nsCSSFrameConstructor.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:fa4082bd0fae
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 /*
8 * construction of a frame tree that is nearly isomorphic to the content
9 * tree and updating of that tree in response to dynamic changes
10 */
11
12 #include "nsCSSFrameConstructor.h"
13
14 #include "mozilla/AutoRestore.h"
15 #include "mozilla/DebugOnly.h"
16 #include "mozilla/dom/HTMLSelectElement.h"
17 #include "mozilla/EventStates.h"
18 #include "mozilla/Likely.h"
19 #include "mozilla/LinkedList.h"
20 #include "nsAbsoluteContainingBlock.h"
21 #include "nsIAtom.h"
22 #include "nsIFrameInlines.h"
23 #include "nsGkAtoms.h"
24 #include "nsPresContext.h"
25 #include "nsIDocument.h"
26 #include "nsTableFrame.h"
27 #include "nsTableColFrame.h"
28 #include "nsIDOMHTMLDocument.h"
29 #include "nsHTMLParts.h"
30 #include "nsIPresShell.h"
31 #include "nsUnicharUtils.h"
32 #include "nsStyleSet.h"
33 #include "nsViewManager.h"
34 #include "nsStyleConsts.h"
35 #include "nsIDOMXULElement.h"
36 #include "nsContainerFrame.h"
37 #include "nsNameSpaceManager.h"
38 #include "nsIComboboxControlFrame.h"
39 #include "nsIListControlFrame.h"
40 #include "nsIDOMCharacterData.h"
41 #include "nsPlaceholderFrame.h"
42 #include "nsTableRowGroupFrame.h"
43 #include "nsIFormControl.h"
44 #include "nsCSSAnonBoxes.h"
45 #include "nsTextFragment.h"
46 #include "nsIAnonymousContentCreator.h"
47 #include "nsBindingManager.h"
48 #include "nsXBLBinding.h"
49 #include "nsContentUtils.h"
50 #include "nsIScriptError.h"
51 #ifdef XP_MACOSX
52 #include "nsIDocShell.h"
53 #endif
54 #include "ChildIterator.h"
55 #include "nsError.h"
56 #include "nsLayoutUtils.h"
57 #include "nsAutoPtr.h"
58 #include "nsBoxFrame.h"
59 #include "nsBoxLayout.h"
60 #include "nsFlexContainerFrame.h"
61 #include "nsGridContainerFrame.h"
62 #include "nsImageFrame.h"
63 #include "nsIObjectLoadingContent.h"
64 #include "nsTArray.h"
65 #include "nsGenericDOMDataNode.h"
66 #include "mozilla/dom/Element.h"
67 #include "nsAutoLayoutPhase.h"
68 #include "nsStyleStructInlines.h"
69 #include "nsPageContentFrame.h"
70 #include "RestyleManager.h"
71 #include "StickyScrollContainer.h"
72 #include "nsFieldSetFrame.h"
73
74 #ifdef MOZ_XUL
75 #include "nsIRootBox.h"
76 #endif
77 #ifdef ACCESSIBILITY
78 #include "nsAccessibilityService.h"
79 #endif
80
81 #include "nsBlockFrame.h"
82
83 #include "nsIScrollableFrame.h"
84
85 #include "nsXBLService.h"
86
87 #undef NOISY_FIRST_LETTER
88
89 #include "nsMathMLParts.h"
90 #include "mozilla/dom/SVGTests.h"
91 #include "nsSVGUtils.h"
92
93 #include "nsRefreshDriver.h"
94 #include "nsRuleProcessorData.h"
95 #include "nsTextNode.h"
96
97 using namespace mozilla;
98 using namespace mozilla::dom;
99
100 // An alias for convenience.
101 static const nsIFrame::ChildListID kPrincipalList = nsIFrame::kPrincipalList;
102
103 nsIFrame*
104 NS_NewHTMLCanvasFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
105
106 nsIFrame*
107 NS_NewHTMLVideoFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
108
109 nsIFrame*
110 NS_NewSVGOuterSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
111 nsIFrame*
112 NS_NewSVGOuterSVGAnonChildFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
113 nsIFrame*
114 NS_NewSVGInnerSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
115 nsIFrame*
116 NS_NewSVGPathGeometryFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
117 nsIFrame*
118 NS_NewSVGGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
119 nsIFrame*
120 NS_NewSVGGenericContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
121 nsIFrame*
122 NS_NewSVGForeignObjectFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
123 nsIFrame*
124 NS_NewSVGAFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
125 nsIFrame*
126 NS_NewSVGSwitchFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
127 nsIFrame*
128 NS_NewSVGTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
129 nsIFrame*
130 NS_NewSVGContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
131 nsIFrame*
132 NS_NewSVGUseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
133 nsIFrame*
134 NS_NewSVGViewFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
135 extern nsIFrame*
136 NS_NewSVGLinearGradientFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
137 extern nsIFrame*
138 NS_NewSVGRadialGradientFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
139 extern nsIFrame*
140 NS_NewSVGStopFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
141 nsIFrame*
142 NS_NewSVGMarkerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
143 nsIFrame*
144 NS_NewSVGMarkerAnonChildFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
145 extern nsIFrame*
146 NS_NewSVGImageFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
147 nsIFrame*
148 NS_NewSVGClipPathFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
149 nsIFrame*
150 NS_NewSVGFilterFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
151 nsIFrame*
152 NS_NewSVGPatternFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
153 nsIFrame*
154 NS_NewSVGMaskFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
155 nsIFrame*
156 NS_NewSVGFEContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
157 nsIFrame*
158 NS_NewSVGFELeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
159 nsIFrame*
160 NS_NewSVGFEImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
161 nsIFrame*
162 NS_NewSVGFEUnstyledLeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
163
164 #include "nsINodeInfo.h"
165 #include "prenv.h"
166 #include "nsNodeInfoManager.h"
167 #include "nsContentCreatorFunctions.h"
168
169 #ifdef DEBUG
170 // Set the environment variable GECKO_FRAMECTOR_DEBUG_FLAGS to one or
171 // more of the following flags (comma separated) for handy debug
172 // output.
173 static bool gNoisyContentUpdates = false;
174 static bool gReallyNoisyContentUpdates = false;
175 static bool gNoisyInlineConstruction = false;
176
177 struct FrameCtorDebugFlags {
178 const char* name;
179 bool* on;
180 };
181
182 static FrameCtorDebugFlags gFlags[] = {
183 { "content-updates", &gNoisyContentUpdates },
184 { "really-noisy-content-updates", &gReallyNoisyContentUpdates },
185 { "noisy-inline", &gNoisyInlineConstruction }
186 };
187
188 #define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
189 #endif
190
191
192 #ifdef MOZ_XUL
193 #include "nsMenuFrame.h"
194 #include "nsPopupSetFrame.h"
195 #include "nsTreeColFrame.h"
196 #include "nsIBoxObject.h"
197 #include "nsPIListBoxObject.h"
198 #include "nsListBoxBodyFrame.h"
199 #include "nsListItemFrame.h"
200 #include "nsXULLabelFrame.h"
201
202 //------------------------------------------------------------------
203
204 nsIFrame*
205 NS_NewAutoRepeatBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
206
207 nsIFrame*
208 NS_NewRootBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
209
210 nsIFrame*
211 NS_NewDocElementBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
212
213 nsIFrame*
214 NS_NewThumbFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
215
216 nsIFrame*
217 NS_NewDeckFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
218
219 nsIFrame*
220 NS_NewLeafBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
221
222 nsIFrame*
223 NS_NewStackFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
224
225 nsIFrame*
226 NS_NewProgressMeterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
227
228 nsIFrame*
229 NS_NewRangeFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
230
231 nsIFrame*
232 NS_NewImageBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
233
234 nsIFrame*
235 NS_NewTextBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
236
237 nsIFrame*
238 NS_NewGroupBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
239
240 nsIFrame*
241 NS_NewButtonBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
242
243 nsIFrame*
244 NS_NewSplitterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
245
246 nsIFrame*
247 NS_NewMenuPopupFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
248
249 nsIFrame*
250 NS_NewPopupSetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
251
252 nsIFrame*
253 NS_NewMenuFrame (nsIPresShell* aPresShell, nsStyleContext* aContext, uint32_t aFlags);
254
255 nsIFrame*
256 NS_NewMenuBarFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
257
258 nsIFrame*
259 NS_NewTreeBodyFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
260
261 // grid
262 nsresult
263 NS_NewGridLayout2 ( nsIPresShell* aPresShell, nsBoxLayout** aNewLayout );
264 nsIFrame*
265 NS_NewGridRowLeafFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
266 nsIFrame*
267 NS_NewGridRowGroupFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
268
269 // end grid
270
271 nsIFrame*
272 NS_NewTitleBarFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
273
274 nsIFrame*
275 NS_NewResizerFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
276
277
278 #endif
279
280 nsIFrame*
281 NS_NewHTMLScrollFrame (nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot);
282
283 nsIFrame*
284 NS_NewXULScrollFrame (nsIPresShell* aPresShell, nsStyleContext* aContext,
285 bool aIsRoot, bool aClipAllDescendants);
286
287 nsIFrame*
288 NS_NewSliderFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
289
290 nsIFrame*
291 NS_NewScrollbarFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
292
293 nsIFrame*
294 NS_NewScrollbarButtonFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
295
296
297 #ifdef NOISY_FINDFRAME
298 static int32_t FFWC_totalCount=0;
299 static int32_t FFWC_doLoop=0;
300 static int32_t FFWC_doSibling=0;
301 static int32_t FFWC_recursions=0;
302 static int32_t FFWC_nextInFlows=0;
303 #endif
304
305 // Returns true if aFrame is an anonymous flex item
306 static inline bool
307 IsAnonymousFlexItem(const nsIFrame* aFrame)
308 {
309 const nsIAtom* pseudoType = aFrame->StyleContext()->GetPseudo();
310 return pseudoType == nsCSSAnonBoxes::anonymousFlexItem;
311 }
312
313 static inline nsIFrame*
314 GetFieldSetBlockFrame(nsIFrame* aFieldsetFrame)
315 {
316 // Depends on the fieldset child frame order - see ConstructFieldSetFrame() below.
317 nsIFrame* firstChild = aFieldsetFrame->GetFirstPrincipalChild();
318 nsIFrame* inner = firstChild && firstChild->GetNextSibling() ? firstChild->GetNextSibling() : firstChild;
319 return inner ? inner->GetContentInsertionFrame() : nullptr;
320 }
321
322 #define FCDATA_DECL(_flags, _func) \
323 { _flags, { (FrameCreationFunc)_func }, nullptr, nullptr }
324 #define FCDATA_WITH_WRAPPING_BLOCK(_flags, _func, _anon_box) \
325 { _flags | FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS, \
326 { (FrameCreationFunc)_func }, nullptr, &_anon_box }
327
328 //----------------------------------------------------------------------
329
330 /**
331 * True if aFrame is an actual inline frame in the sense of non-replaced
332 * display:inline CSS boxes. In other words, it can be affected by {ib}
333 * splitting and can contain first-letter frames. Basically, this is either an
334 * inline frame (positioned or otherwise) or an line frame (this last because
335 * it can contain first-letter and because inserting blocks in the middle of it
336 * needs to terminate it).
337 */
338 static bool
339 IsInlineFrame(const nsIFrame* aFrame)
340 {
341 return aFrame->IsFrameOfType(nsIFrame::eLineParticipant);
342 }
343
344 /**
345 * True if aFrame is an instance of an SVG frame class or is an inline/block
346 * frame being used for SVG text.
347 */
348 static bool
349 IsFrameForSVG(const nsIFrame* aFrame)
350 {
351 return aFrame->IsFrameOfType(nsIFrame::eSVG) ||
352 aFrame->IsSVGText();
353 }
354
355 /**
356 * Returns true iff aFrame explicitly prevents its descendants from floating
357 * (at least, down to the level of descendants which themselves are
358 * float-containing blocks -- those will manage the floating status of any
359 * lower-level descendents inside them, of course).
360 */
361 static bool
362 ShouldSuppressFloatingOfDescendants(nsIFrame* aFrame)
363 {
364 return aFrame->IsFrameOfType(nsIFrame::eMathML) ||
365 aFrame->IsBoxFrame() ||
366 aFrame->GetType() == nsGkAtoms::flexContainerFrame ||
367 aFrame->GetType() == nsGkAtoms::gridContainerFrame;
368 }
369
370 /**
371 * If any children require a block parent, return the first such child.
372 * Otherwise return null.
373 */
374 static nsIContent*
375 AnyKidsNeedBlockParent(nsIFrame *aFrameList)
376 {
377 for (nsIFrame *k = aFrameList; k; k = k->GetNextSibling()) {
378 // Line participants, such as text and inline frames, can't be
379 // directly inside a XUL box; they must be wrapped in an
380 // intermediate block.
381 if (k->IsFrameOfType(nsIFrame::eLineParticipant)) {
382 return k->GetContent();
383 }
384 }
385 return nullptr;
386 }
387
388 // Reparent a frame into a wrapper frame that is a child of its old parent.
389 static void
390 ReparentFrame(RestyleManager* aRestyleManager,
391 nsIFrame* aNewParentFrame,
392 nsIFrame* aFrame)
393 {
394 aFrame->SetParent(aNewParentFrame);
395 aRestyleManager->ReparentStyleContext(aFrame);
396 }
397
398 static void
399 ReparentFrames(nsCSSFrameConstructor* aFrameConstructor,
400 nsIFrame* aNewParentFrame,
401 const nsFrameList& aFrameList)
402 {
403 RestyleManager* restyleManager = aFrameConstructor->RestyleManager();
404 for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
405 ReparentFrame(restyleManager, aNewParentFrame, e.get());
406 }
407 }
408
409 //----------------------------------------------------------------------
410 //
411 // When inline frames get weird and have block frames in them, we
412 // annotate them to help us respond to incremental content changes
413 // more easily.
414
415 static inline bool
416 IsFramePartOfIBSplit(nsIFrame* aFrame)
417 {
418 return (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) != 0;
419 }
420
421 static nsIFrame* GetIBSplitSibling(nsIFrame* aFrame)
422 {
423 NS_PRECONDITION(IsFramePartOfIBSplit(aFrame), "Shouldn't call this");
424
425 // We only store the "ib-split sibling" annotation with the first
426 // frame in the continuation chain. Walk back to find that frame now.
427 return static_cast<nsIFrame*>
428 (aFrame->FirstContinuation()->
429 Properties().Get(nsIFrame::IBSplitSibling()));
430 }
431
432 static nsIFrame* GetIBSplitPrevSibling(nsIFrame* aFrame)
433 {
434 NS_PRECONDITION(IsFramePartOfIBSplit(aFrame), "Shouldn't call this");
435
436 // We only store the ib-split sibling annotation with the first
437 // frame in the continuation chain. Walk back to find that frame now.
438 return static_cast<nsIFrame*>
439 (aFrame->FirstContinuation()->
440 Properties().Get(nsIFrame::IBSplitPrevSibling()));
441 }
442
443 static nsIFrame*
444 GetLastIBSplitSibling(nsIFrame* aFrame, bool aReturnEmptyTrailingInline)
445 {
446 for (nsIFrame *frame = aFrame, *next; ; frame = next) {
447 next = GetIBSplitSibling(frame);
448 if (!next ||
449 (!aReturnEmptyTrailingInline && !next->GetFirstPrincipalChild() &&
450 !GetIBSplitSibling(next))) {
451 NS_ASSERTION(!next || !frame->IsInlineOutside(),
452 "Should have a block here!");
453 return frame;
454 }
455 }
456 NS_NOTREACHED("unreachable code");
457 return nullptr;
458 }
459
460 static void
461 SetFrameIsIBSplit(nsIFrame* aFrame, nsIFrame* aIBSplitSibling)
462 {
463 NS_PRECONDITION(aFrame, "bad args!");
464
465 // We should be the only continuation
466 NS_ASSERTION(!aFrame->GetPrevContinuation(),
467 "assigning ib-split sibling to other than first continuation!");
468 NS_ASSERTION(!aFrame->GetNextContinuation() ||
469 IsFramePartOfIBSplit(aFrame->GetNextContinuation()),
470 "should have no non-ib-split continuations here");
471
472 // Mark the frame as ib-split.
473 aFrame->AddStateBits(NS_FRAME_PART_OF_IBSPLIT);
474
475 if (aIBSplitSibling) {
476 NS_ASSERTION(!aIBSplitSibling->GetPrevContinuation(),
477 "assigning something other than the first continuation as the "
478 "ib-split sibling");
479
480 // Store the ib-split sibling (if we were given one) with the
481 // first frame in the flow.
482 FramePropertyTable* props = aFrame->PresContext()->PropertyTable();
483 props->Set(aFrame, nsIFrame::IBSplitSibling(), aIBSplitSibling);
484 props->Set(aIBSplitSibling, nsIFrame::IBSplitPrevSibling(), aFrame);
485 }
486 }
487
488 static nsIFrame*
489 GetIBContainingBlockFor(nsIFrame* aFrame)
490 {
491 NS_PRECONDITION(IsFramePartOfIBSplit(aFrame),
492 "GetIBContainingBlockFor() should only be called on known IB frames");
493
494 // Get the first "normal" ancestor of the target frame.
495 nsIFrame* parentFrame;
496 do {
497 parentFrame = aFrame->GetParent();
498
499 if (! parentFrame) {
500 NS_ERROR("no unsplit block frame in IB hierarchy");
501 return aFrame;
502 }
503
504 // Note that we ignore non-ib-split frames which have a pseudo on their
505 // style context -- they're not the frames we're looking for! In
506 // particular, they may be hiding a real parent that _is_ in an ib-split.
507 if (!IsFramePartOfIBSplit(parentFrame) &&
508 !parentFrame->StyleContext()->GetPseudo())
509 break;
510
511 aFrame = parentFrame;
512 } while (1);
513
514 // post-conditions
515 NS_ASSERTION(parentFrame, "no normal ancestor found for ib-split frame "
516 "in GetIBContainingBlockFor");
517 NS_ASSERTION(parentFrame != aFrame, "parentFrame is actually the child frame - bogus reslt");
518
519 return parentFrame;
520 }
521
522 //----------------------------------------------------------------------
523
524 // Block/inline frame construction logic. We maintain a few invariants here:
525 //
526 // 1. Block frames contain block and inline frames.
527 //
528 // 2. Inline frames only contain inline frames. If an inline parent has a block
529 // child then the block child is migrated upward until it lands in a block
530 // parent (the inline frames containing block is where it will end up).
531
532 // After this function returns, aLink is pointing to the first link at or
533 // after its starting position for which the next frame is a block. If there
534 // is no such link, it points to the end of the list.
535 static void
536 FindFirstBlock(nsFrameList::FrameLinkEnumerator& aLink)
537 {
538 for ( ; !aLink.AtEnd(); aLink.Next()) {
539 if (!aLink.NextFrame()->IsInlineOutside()) {
540 return;
541 }
542 }
543 }
544
545 // This function returns a frame link enumerator pointing to the first link in
546 // the list for which the next frame is not block. If there is no such link,
547 // it points to the end of the list.
548 static nsFrameList::FrameLinkEnumerator
549 FindFirstNonBlock(const nsFrameList& aList)
550 {
551 nsFrameList::FrameLinkEnumerator link(aList);
552 for (; !link.AtEnd(); link.Next()) {
553 if (link.NextFrame()->IsInlineOutside()) {
554 break;
555 }
556 }
557 return link;
558 }
559
560 inline void
561 SetInitialSingleChild(nsIFrame* aParent, nsIFrame* aFrame)
562 {
563 NS_PRECONDITION(!aFrame->GetNextSibling(), "Should be using a frame list");
564 nsFrameList temp(aFrame, aFrame);
565 aParent->SetInitialChildList(kPrincipalList, temp);
566 }
567
568 // -----------------------------------------------------------
569
570 // Structure used when constructing formatting object trees.
571 struct nsFrameItems : public nsFrameList
572 {
573 // Appends the frame to the end of the list
574 void AddChild(nsIFrame* aChild);
575 };
576
577 void
578 nsFrameItems::AddChild(nsIFrame* aChild)
579 {
580 NS_PRECONDITION(aChild, "nsFrameItems::AddChild");
581
582 // It'd be really nice if we could just AppendFrames(kPrincipalList, aChild) here,
583 // but some of our callers put frames that have different
584 // parents (caption, I'm looking at you) on the same framelist, and
585 // nsFrameList asserts if you try to do that.
586 if (IsEmpty()) {
587 SetFrames(aChild);
588 }
589 else {
590 NS_ASSERTION(aChild != mLastChild,
591 "Same frame being added to frame list twice?");
592 mLastChild->SetNextSibling(aChild);
593 mLastChild = nsLayoutUtils::GetLastSibling(aChild);
594 }
595 }
596
597 // -----------------------------------------------------------
598
599 // Structure used when constructing formatting object trees. Contains
600 // state information needed for absolutely positioned elements
601 struct nsAbsoluteItems : nsFrameItems {
602 // containing block for absolutely positioned elements
603 nsIFrame* containingBlock;
604
605 nsAbsoluteItems(nsIFrame* aContainingBlock);
606 #ifdef DEBUG
607 // XXXbz Does this need a debug-only assignment operator that nulls out the
608 // childList in the nsAbsoluteItems we're copying? Introducing a difference
609 // between debug and non-debug behavior seems bad, so I guess not...
610 ~nsAbsoluteItems() {
611 NS_ASSERTION(!FirstChild(),
612 "Dangling child list. Someone forgot to insert it?");
613 }
614 #endif
615
616 // Appends the frame to the end of the list
617 void AddChild(nsIFrame* aChild);
618 };
619
620 nsAbsoluteItems::nsAbsoluteItems(nsIFrame* aContainingBlock)
621 : containingBlock(aContainingBlock)
622 {
623 }
624
625 // Additional behavior is that it sets the frame's NS_FRAME_OUT_OF_FLOW flag
626 void
627 nsAbsoluteItems::AddChild(nsIFrame* aChild)
628 {
629 NS_ASSERTION(aChild->PresContext()->FrameManager()->
630 GetPlaceholderFrameFor(aChild),
631 "Child without placeholder being added to nsAbsoluteItems?");
632 aChild->AddStateBits(NS_FRAME_OUT_OF_FLOW);
633 nsFrameItems::AddChild(aChild);
634 }
635
636 // -----------------------------------------------------------
637
638 // Structure for saving the existing state when pushing/poping containing
639 // blocks. The destructor restores the state to its previous state
640 class MOZ_STACK_CLASS nsFrameConstructorSaveState {
641 public:
642 typedef nsIFrame::ChildListID ChildListID;
643 nsFrameConstructorSaveState();
644 ~nsFrameConstructorSaveState();
645
646 private:
647 nsAbsoluteItems* mItems; // pointer to struct whose data we save/restore
648 nsAbsoluteItems mSavedItems; // copy of original data
649
650 // The name of the child list in which our frames would belong
651 ChildListID mChildListID;
652 nsFrameConstructorState* mState;
653
654 // State used only when we're saving the abs-pos state for a transformed
655 // element.
656 nsAbsoluteItems mSavedFixedItems;
657
658 bool mSavedFixedPosIsAbsPos;
659
660 friend class nsFrameConstructorState;
661 };
662
663 // Structure used to keep track of a list of bindings we need to call
664 // AddToAttachedQueue on. These should be in post-order depth-first
665 // flattened tree traversal order.
666 struct PendingBinding : public LinkedListElement<PendingBinding>
667 {
668 #ifdef NS_BUILD_REFCNT_LOGGING
669 PendingBinding() {
670 MOZ_COUNT_CTOR(PendingBinding);
671 }
672 ~PendingBinding() {
673 MOZ_COUNT_DTOR(PendingBinding);
674 }
675 #endif
676
677 nsRefPtr<nsXBLBinding> mBinding;
678 };
679
680 // Structure used for maintaining state information during the
681 // frame construction process
682 class MOZ_STACK_CLASS nsFrameConstructorState {
683 public:
684 typedef nsIFrame::ChildListID ChildListID;
685
686 nsPresContext *mPresContext;
687 nsIPresShell *mPresShell;
688 nsFrameManager *mFrameManager;
689
690 #ifdef MOZ_XUL
691 // Frames destined for the kPopupList.
692 nsAbsoluteItems mPopupItems;
693 #endif
694
695 // Containing block information for out-of-flow frames.
696 nsAbsoluteItems mFixedItems;
697 nsAbsoluteItems mAbsoluteItems;
698 nsAbsoluteItems mFloatedItems;
699
700 nsCOMPtr<nsILayoutHistoryState> mFrameState;
701 // These bits will be added to the state bits of any frame we construct
702 // using this state.
703 nsFrameState mAdditionalStateBits;
704
705 // When working with the -moz-transform property, we want to hook
706 // the abs-pos and fixed-pos lists together, since transformed
707 // elements are fixed-pos containing blocks. This flag determines
708 // whether or not we want to wire the fixed-pos and abs-pos lists
709 // together.
710 bool mFixedPosIsAbsPos;
711
712 // A boolean to indicate whether we have a "pending" popupgroup. That is, we
713 // have already created the FrameConstructionItem for the root popupgroup but
714 // we have not yet created the relevant frame.
715 bool mHavePendingPopupgroup;
716
717 // If false (which is the default) then call SetPrimaryFrame() as needed
718 // during frame construction. If true, don't make any SetPrimaryFrame()
719 // calls, except for generated content which doesn't have a primary frame
720 // yet. The mCreatingExtraFrames == true mode is meant to be used for
721 // construction of random "extra" frames for elements via normal frame
722 // construction APIs (e.g. replication of things across pages in paginated
723 // mode).
724 bool mCreatingExtraFrames;
725
726 nsCOMArray<nsIContent> mGeneratedTextNodesWithInitializer;
727
728 TreeMatchContext mTreeMatchContext;
729
730 // Constructor
731 // Use the passed-in history state.
732 nsFrameConstructorState(nsIPresShell* aPresShell,
733 nsIFrame* aFixedContainingBlock,
734 nsIFrame* aAbsoluteContainingBlock,
735 nsIFrame* aFloatContainingBlock,
736 nsILayoutHistoryState* aHistoryState);
737 // Get the history state from the pres context's pres shell.
738 nsFrameConstructorState(nsIPresShell* aPresShell,
739 nsIFrame* aFixedContainingBlock,
740 nsIFrame* aAbsoluteContainingBlock,
741 nsIFrame* aFloatContainingBlock);
742
743 ~nsFrameConstructorState();
744
745 // Function to push the existing absolute containing block state and
746 // create a new scope. Code that uses this function should get matching
747 // logic in GetAbsoluteContainingBlock.
748 // Also makes aNewAbsoluteContainingBlock the containing block for
749 // fixed-pos elements if necessary.
750 // aPositionedFrame is the frame whose style actually makes
751 // aNewAbsoluteContainingBlock a containing block. E.g. for a scrollable element
752 // aPositionedFrame is the element's primary frame and
753 // aNewAbsoluteContainingBlock is the scrolled frame.
754 void PushAbsoluteContainingBlock(nsIFrame* aNewAbsoluteContainingBlock,
755 nsIFrame* aPositionedFrame,
756 nsFrameConstructorSaveState& aSaveState);
757
758 // Function to push the existing float containing block state and
759 // create a new scope. Code that uses this function should get matching
760 // logic in GetFloatContainingBlock.
761 // Pushing a null float containing block forbids any frames from being
762 // floated until a new float containing block is pushed.
763 // XXX we should get rid of null float containing blocks and teach the
764 // various frame classes to deal with floats instead.
765 void PushFloatContainingBlock(nsIFrame* aNewFloatContainingBlock,
766 nsFrameConstructorSaveState& aSaveState);
767
768 // Function to return the proper geometric parent for a frame with display
769 // struct given by aStyleDisplay and parent's frame given by
770 // aContentParentFrame.
771 nsIFrame* GetGeometricParent(const nsStyleDisplay* aStyleDisplay,
772 nsIFrame* aContentParentFrame) const;
773
774 /**
775 * Function to add a new frame to the right frame list. This MUST be called
776 * on frames before their children have been processed if the frames might
777 * conceivably be out-of-flow; otherwise cleanup in error cases won't work
778 * right. Also, this MUST be called on frames after they have been
779 * initialized.
780 * @param aNewFrame the frame to add
781 * @param aFrameItems the list to add in-flow frames to
782 * @param aContent the content pointer for aNewFrame
783 * @param aStyleContext the style context resolved for aContent
784 * @param aParentFrame the parent frame for the content if it were in-flow
785 * @param aCanBePositioned pass false if the frame isn't allowed to be
786 * positioned
787 * @param aCanBeFloated pass false if the frame isn't allowed to be
788 * floated
789 * @param aIsOutOfFlowPopup pass true if the frame is an out-of-flow popup
790 * (XUL-only)
791 */
792 void AddChild(nsIFrame* aNewFrame,
793 nsFrameItems& aFrameItems,
794 nsIContent* aContent,
795 nsStyleContext* aStyleContext,
796 nsIFrame* aParentFrame,
797 bool aCanBePositioned = true,
798 bool aCanBeFloated = true,
799 bool aIsOutOfFlowPopup = false,
800 bool aInsertAfter = false,
801 nsIFrame* aInsertAfterFrame = nullptr);
802
803 /**
804 * Function to return the fixed-pos element list. Normally this will just hand back the
805 * fixed-pos element list, but in case we're dealing with a transformed element that's
806 * acting as an abs-pos and fixed-pos container, we'll hand back the abs-pos list. Callers should
807 * use this function if they want to get the list acting as the fixed-pos item parent.
808 */
809 nsAbsoluteItems& GetFixedItems()
810 {
811 return mFixedPosIsAbsPos ? mAbsoluteItems : mFixedItems;
812 }
813 const nsAbsoluteItems& GetFixedItems() const
814 {
815 return mFixedPosIsAbsPos ? mAbsoluteItems : mFixedItems;
816 }
817
818
819 /**
820 * class to automatically push and pop a pending binding in the frame
821 * constructor state. See nsCSSFrameConstructor::FrameConstructionItem
822 * mPendingBinding documentation.
823 */
824 class PendingBindingAutoPusher;
825 friend class PendingBindingAutoPusher;
826 class MOZ_STACK_CLASS PendingBindingAutoPusher {
827 public:
828 PendingBindingAutoPusher(nsFrameConstructorState& aState,
829 PendingBinding* aPendingBinding) :
830 mState(aState),
831 mPendingBinding(aState.mCurrentPendingBindingInsertionPoint)
832 {
833 if (aPendingBinding) {
834 aState.mCurrentPendingBindingInsertionPoint = aPendingBinding;
835 }
836 }
837
838 ~PendingBindingAutoPusher()
839 {
840 mState.mCurrentPendingBindingInsertionPoint = mPendingBinding;
841 }
842
843 private:
844 nsFrameConstructorState& mState;
845 PendingBinding* mPendingBinding;
846 };
847
848 /**
849 * Add a new pending binding to the list
850 */
851 void AddPendingBinding(PendingBinding* aPendingBinding) {
852 if (mCurrentPendingBindingInsertionPoint) {
853 mCurrentPendingBindingInsertionPoint->setPrevious(aPendingBinding);
854 } else {
855 mPendingBindings.insertBack(aPendingBinding);
856 }
857 }
858
859 protected:
860 friend class nsFrameConstructorSaveState;
861
862 /**
863 * ProcessFrameInsertions takes the frames in aFrameItems and adds them as
864 * kids to the aChildListID child list of |aFrameItems.containingBlock|.
865 */
866 void ProcessFrameInsertions(nsAbsoluteItems& aFrameItems,
867 ChildListID aChildListID);
868
869 // Our list of all pending bindings. When we're done, we need to call
870 // AddToAttachedQueue on all of them, in order.
871 LinkedList<PendingBinding> mPendingBindings;
872
873 PendingBinding* mCurrentPendingBindingInsertionPoint;
874 };
875
876 nsFrameConstructorState::nsFrameConstructorState(nsIPresShell* aPresShell,
877 nsIFrame* aFixedContainingBlock,
878 nsIFrame* aAbsoluteContainingBlock,
879 nsIFrame* aFloatContainingBlock,
880 nsILayoutHistoryState* aHistoryState)
881 : mPresContext(aPresShell->GetPresContext()),
882 mPresShell(aPresShell),
883 mFrameManager(aPresShell->FrameManager()),
884 #ifdef MOZ_XUL
885 mPopupItems(nullptr),
886 #endif
887 mFixedItems(aFixedContainingBlock),
888 mAbsoluteItems(aAbsoluteContainingBlock),
889 mFloatedItems(aFloatContainingBlock),
890 // See PushAbsoluteContaningBlock below
891 mFrameState(aHistoryState),
892 mAdditionalStateBits(nsFrameState(0)),
893 // If the fixed-pos containing block is equal to the abs-pos containing
894 // block, use the abs-pos containing block's abs-pos list for fixed-pos
895 // frames.
896 mFixedPosIsAbsPos(aFixedContainingBlock == aAbsoluteContainingBlock),
897 mHavePendingPopupgroup(false),
898 mCreatingExtraFrames(false),
899 mTreeMatchContext(true, nsRuleWalker::eRelevantLinkUnvisited,
900 aPresShell->GetDocument()),
901 mCurrentPendingBindingInsertionPoint(nullptr)
902 {
903 #ifdef MOZ_XUL
904 nsIRootBox* rootBox = nsIRootBox::GetRootBox(aPresShell);
905 if (rootBox) {
906 mPopupItems.containingBlock = rootBox->GetPopupSetFrame();
907 }
908 #endif
909 MOZ_COUNT_CTOR(nsFrameConstructorState);
910 }
911
912 nsFrameConstructorState::nsFrameConstructorState(nsIPresShell* aPresShell,
913 nsIFrame* aFixedContainingBlock,
914 nsIFrame* aAbsoluteContainingBlock,
915 nsIFrame* aFloatContainingBlock)
916 : mPresContext(aPresShell->GetPresContext()),
917 mPresShell(aPresShell),
918 mFrameManager(aPresShell->FrameManager()),
919 #ifdef MOZ_XUL
920 mPopupItems(nullptr),
921 #endif
922 mFixedItems(aFixedContainingBlock),
923 mAbsoluteItems(aAbsoluteContainingBlock),
924 mFloatedItems(aFloatContainingBlock),
925 // See PushAbsoluteContaningBlock below
926 mAdditionalStateBits(nsFrameState(0)),
927 // If the fixed-pos containing block is equal to the abs-pos containing
928 // block, use the abs-pos containing block's abs-pos list for fixed-pos
929 // frames.
930 mFixedPosIsAbsPos(aFixedContainingBlock == aAbsoluteContainingBlock),
931 mHavePendingPopupgroup(false),
932 mCreatingExtraFrames(false),
933 mTreeMatchContext(true, nsRuleWalker::eRelevantLinkUnvisited,
934 aPresShell->GetDocument()),
935 mCurrentPendingBindingInsertionPoint(nullptr)
936 {
937 #ifdef MOZ_XUL
938 nsIRootBox* rootBox = nsIRootBox::GetRootBox(aPresShell);
939 if (rootBox) {
940 mPopupItems.containingBlock = rootBox->GetPopupSetFrame();
941 }
942 #endif
943 MOZ_COUNT_CTOR(nsFrameConstructorState);
944 mFrameState = aPresShell->GetDocument()->GetLayoutHistoryState();
945 }
946
947 nsFrameConstructorState::~nsFrameConstructorState()
948 {
949 // Frame order comparison functions only work properly when the placeholders
950 // have been inserted into the frame tree. So for example if we have a new float
951 // containing the placeholder for a new abs-pos frame, and we process the abs-pos
952 // insertion first, then we won't be able to find the right place to insert in
953 // in the abs-pos list. So put floats in first, because they can contain placeholders
954 // for abs-pos and fixed-pos items whose containing blocks are outside the floats.
955 // Then put abs-pos frames in, because they can contain placeholders for fixed-pos
956 // items whose containing block is outside the abs-pos frames.
957 MOZ_COUNT_DTOR(nsFrameConstructorState);
958 ProcessFrameInsertions(mFloatedItems, nsIFrame::kFloatList);
959 ProcessFrameInsertions(mAbsoluteItems, nsIFrame::kAbsoluteList);
960 ProcessFrameInsertions(mFixedItems, nsIFrame::kFixedList);
961 #ifdef MOZ_XUL
962 ProcessFrameInsertions(mPopupItems, nsIFrame::kPopupList);
963 #endif
964 for (int32_t i = mGeneratedTextNodesWithInitializer.Count() - 1; i >= 0; --i) {
965 mGeneratedTextNodesWithInitializer[i]->
966 DeleteProperty(nsGkAtoms::genConInitializerProperty);
967 }
968 if (!mPendingBindings.isEmpty()) {
969 nsBindingManager* bindingManager = mPresShell->GetDocument()->BindingManager();
970 do {
971 nsAutoPtr<PendingBinding> pendingBinding;
972 pendingBinding = mPendingBindings.popFirst();
973 bindingManager->AddToAttachedQueue(pendingBinding->mBinding);
974 } while (!mPendingBindings.isEmpty());
975 mCurrentPendingBindingInsertionPoint = nullptr;
976 }
977 }
978
979 static nsIFrame*
980 AdjustAbsoluteContainingBlock(nsIFrame* aContainingBlockIn)
981 {
982 if (!aContainingBlockIn) {
983 return nullptr;
984 }
985
986 // Always use the container's first continuation. (Inline frames can have
987 // non-fluid bidi continuations...)
988 return aContainingBlockIn->FirstContinuation();
989 }
990
991 void
992 nsFrameConstructorState::PushAbsoluteContainingBlock(nsIFrame* aNewAbsoluteContainingBlock,
993 nsIFrame* aPositionedFrame,
994 nsFrameConstructorSaveState& aSaveState)
995 {
996 aSaveState.mItems = &mAbsoluteItems;
997 aSaveState.mSavedItems = mAbsoluteItems;
998 aSaveState.mChildListID = nsIFrame::kAbsoluteList;
999 aSaveState.mState = this;
1000 aSaveState.mSavedFixedPosIsAbsPos = mFixedPosIsAbsPos;
1001
1002 if (mFixedPosIsAbsPos) {
1003 // Since we're going to replace mAbsoluteItems, we need to save it into
1004 // mFixedItems now (and save the current value of mFixedItems).
1005 aSaveState.mSavedFixedItems = mFixedItems;
1006 mFixedItems = mAbsoluteItems;
1007 }
1008
1009 mAbsoluteItems =
1010 nsAbsoluteItems(AdjustAbsoluteContainingBlock(aNewAbsoluteContainingBlock));
1011
1012 /* See if we're wiring the fixed-pos and abs-pos lists together. This happens iff
1013 * we're a transformed element.
1014 */
1015 mFixedPosIsAbsPos = aPositionedFrame &&
1016 (aPositionedFrame->StyleDisplay()->HasTransform(aPositionedFrame) ||
1017 aPositionedFrame->StyleDisplay()->HasPerspectiveStyle());
1018
1019 if (aNewAbsoluteContainingBlock) {
1020 aNewAbsoluteContainingBlock->MarkAsAbsoluteContainingBlock();
1021 }
1022 }
1023
1024 void
1025 nsFrameConstructorState::PushFloatContainingBlock(nsIFrame* aNewFloatContainingBlock,
1026 nsFrameConstructorSaveState& aSaveState)
1027 {
1028 NS_PRECONDITION(!aNewFloatContainingBlock ||
1029 aNewFloatContainingBlock->IsFloatContainingBlock(),
1030 "Please push a real float containing block!");
1031 NS_ASSERTION(!aNewFloatContainingBlock ||
1032 !ShouldSuppressFloatingOfDescendants(aNewFloatContainingBlock),
1033 "We should not push a frame that is supposed to _suppress_ "
1034 "floats as a float containing block!");
1035 aSaveState.mItems = &mFloatedItems;
1036 aSaveState.mSavedItems = mFloatedItems;
1037 aSaveState.mChildListID = nsIFrame::kFloatList;
1038 aSaveState.mState = this;
1039 mFloatedItems = nsAbsoluteItems(aNewFloatContainingBlock);
1040 }
1041
1042 nsIFrame*
1043 nsFrameConstructorState::GetGeometricParent(const nsStyleDisplay* aStyleDisplay,
1044 nsIFrame* aContentParentFrame) const
1045 {
1046 NS_PRECONDITION(aStyleDisplay, "Must have display struct!");
1047
1048 // If there is no container for a fixed, absolute, or floating root
1049 // frame, we will ignore the positioning. This hack is originally
1050 // brought to you by the letter T: tables, since other roots don't
1051 // even call into this code. See bug 178855.
1052 //
1053 // XXX Disabling positioning in this case is a hack. If one was so inclined,
1054 // one could support this either by (1) inserting a dummy block between the
1055 // table and the canvas or (2) teaching the canvas how to reflow positioned
1056 // elements. (1) has the usual problems when multiple frames share the same
1057 // content (notice all the special cases in this file dealing with inner
1058 // tables and outer tables which share the same content). (2) requires some
1059 // work and possible factoring.
1060 //
1061 // XXXbz couldn't we just force position to "static" on roots and
1062 // float to "none"? That's OK per CSS 2.1, as far as I can tell.
1063
1064 if (aContentParentFrame && aContentParentFrame->IsSVGText()) {
1065 return aContentParentFrame;
1066 }
1067
1068 if (aStyleDisplay->IsFloatingStyle() && mFloatedItems.containingBlock) {
1069 NS_ASSERTION(!aStyleDisplay->IsAbsolutelyPositionedStyle(),
1070 "Absolutely positioned _and_ floating?");
1071 return mFloatedItems.containingBlock;
1072 }
1073
1074 if (aStyleDisplay->mPosition == NS_STYLE_POSITION_ABSOLUTE &&
1075 mAbsoluteItems.containingBlock) {
1076 return mAbsoluteItems.containingBlock;
1077 }
1078
1079 if (aStyleDisplay->mPosition == NS_STYLE_POSITION_FIXED &&
1080 GetFixedItems().containingBlock) {
1081 return GetFixedItems().containingBlock;
1082 }
1083
1084 return aContentParentFrame;
1085 }
1086
1087 void
1088 nsFrameConstructorState::AddChild(nsIFrame* aNewFrame,
1089 nsFrameItems& aFrameItems,
1090 nsIContent* aContent,
1091 nsStyleContext* aStyleContext,
1092 nsIFrame* aParentFrame,
1093 bool aCanBePositioned,
1094 bool aCanBeFloated,
1095 bool aIsOutOfFlowPopup,
1096 bool aInsertAfter,
1097 nsIFrame* aInsertAfterFrame)
1098 {
1099 NS_PRECONDITION(!aNewFrame->GetNextSibling(), "Shouldn't happen");
1100
1101 const nsStyleDisplay* disp = aNewFrame->StyleDisplay();
1102
1103 // The comments in GetGeometricParent regarding root table frames
1104 // all apply here, unfortunately.
1105
1106 bool needPlaceholder = false;
1107 nsFrameState placeholderType;
1108 nsFrameItems* frameItems = &aFrameItems;
1109 #ifdef MOZ_XUL
1110 if (MOZ_UNLIKELY(aIsOutOfFlowPopup)) {
1111 NS_ASSERTION(aNewFrame->GetParent() == mPopupItems.containingBlock,
1112 "Popup whose parent is not the popup containing block?");
1113 NS_ASSERTION(mPopupItems.containingBlock, "Must have a popup set frame!");
1114 needPlaceholder = true;
1115 frameItems = &mPopupItems;
1116 placeholderType = PLACEHOLDER_FOR_POPUP;
1117 }
1118 else
1119 #endif // MOZ_XUL
1120 if (aCanBeFloated && aNewFrame->IsFloating() &&
1121 mFloatedItems.containingBlock) {
1122 NS_ASSERTION(aNewFrame->GetParent() == mFloatedItems.containingBlock,
1123 "Float whose parent is not the float containing block?");
1124 needPlaceholder = true;
1125 frameItems = &mFloatedItems;
1126 placeholderType = PLACEHOLDER_FOR_FLOAT;
1127 }
1128 else if (aCanBePositioned) {
1129 if (disp->mPosition == NS_STYLE_POSITION_ABSOLUTE &&
1130 mAbsoluteItems.containingBlock) {
1131 NS_ASSERTION(aNewFrame->GetParent() == mAbsoluteItems.containingBlock,
1132 "Abs pos whose parent is not the abs pos containing block?");
1133 needPlaceholder = true;
1134 frameItems = &mAbsoluteItems;
1135 placeholderType = PLACEHOLDER_FOR_ABSPOS;
1136 }
1137 if (disp->mPosition == NS_STYLE_POSITION_FIXED &&
1138 GetFixedItems().containingBlock) {
1139 NS_ASSERTION(aNewFrame->GetParent() == GetFixedItems().containingBlock,
1140 "Fixed pos whose parent is not the fixed pos containing block?");
1141 needPlaceholder = true;
1142 frameItems = &GetFixedItems();
1143 placeholderType = PLACEHOLDER_FOR_FIXEDPOS;
1144 }
1145 }
1146
1147 if (needPlaceholder) {
1148 NS_ASSERTION(frameItems != &aFrameItems,
1149 "Putting frame in-flow _and_ want a placeholder?");
1150 nsIFrame* placeholderFrame =
1151 nsCSSFrameConstructor::CreatePlaceholderFrameFor(mPresShell,
1152 aContent,
1153 aNewFrame,
1154 aStyleContext,
1155 aParentFrame,
1156 nullptr,
1157 placeholderType);
1158
1159 placeholderFrame->AddStateBits(mAdditionalStateBits);
1160 // Add the placeholder frame to the flow
1161 aFrameItems.AddChild(placeholderFrame);
1162 }
1163 #ifdef DEBUG
1164 else {
1165 NS_ASSERTION(aNewFrame->GetParent() == aParentFrame,
1166 "In-flow frame has wrong parent");
1167 }
1168 #endif
1169
1170 if (aInsertAfter) {
1171 frameItems->InsertFrame(nullptr, aInsertAfterFrame, aNewFrame);
1172 } else {
1173 frameItems->AddChild(aNewFrame);
1174 }
1175 }
1176
1177 void
1178 nsFrameConstructorState::ProcessFrameInsertions(nsAbsoluteItems& aFrameItems,
1179 ChildListID aChildListID)
1180 {
1181 #define NS_NONXUL_LIST_TEST (&aFrameItems == &mFloatedItems && \
1182 aChildListID == nsIFrame::kFloatList) || \
1183 (&aFrameItems == &mAbsoluteItems && \
1184 aChildListID == nsIFrame::kAbsoluteList) || \
1185 (&aFrameItems == &mFixedItems && \
1186 aChildListID == nsIFrame::kFixedList)
1187 #ifdef MOZ_XUL
1188 NS_PRECONDITION(NS_NONXUL_LIST_TEST ||
1189 (&aFrameItems == &mPopupItems &&
1190 aChildListID == nsIFrame::kPopupList),
1191 "Unexpected aFrameItems/aChildListID combination");
1192 #else
1193 NS_PRECONDITION(NS_NONXUL_LIST_TEST,
1194 "Unexpected aFrameItems/aChildListID combination");
1195 #endif
1196
1197 if (aFrameItems.IsEmpty()) {
1198 return;
1199 }
1200
1201 nsIFrame* containingBlock = aFrameItems.containingBlock;
1202
1203 NS_ASSERTION(containingBlock,
1204 "Child list without containing block?");
1205
1206 if (aChildListID == nsIFrame::kFixedList) {
1207 // Put this frame on the transformed-frame's abs-pos list instead, if
1208 // it has abs-pos children instead of fixed-pos children.
1209 aChildListID = containingBlock->GetAbsoluteListID();
1210 }
1211
1212 // Insert the frames hanging out in aItems. We can use SetInitialChildList()
1213 // if the containing block hasn't been reflowed yet (so NS_FRAME_FIRST_REFLOW
1214 // is set) and doesn't have any frames in the aChildListID child list yet.
1215 const nsFrameList& childList = containingBlock->GetChildList(aChildListID);
1216 DebugOnly<nsresult> rv = NS_OK;
1217 if (childList.IsEmpty() &&
1218 (containingBlock->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
1219 // If we're injecting absolutely positioned frames, inject them on the
1220 // absolute containing block
1221 if (aChildListID == containingBlock->GetAbsoluteListID()) {
1222 rv = containingBlock->GetAbsoluteContainingBlock()->
1223 SetInitialChildList(containingBlock, aChildListID, aFrameItems);
1224 } else {
1225 rv = containingBlock->SetInitialChildList(aChildListID, aFrameItems);
1226 }
1227 } else {
1228 // Note that whether the frame construction context is doing an append or
1229 // not is not helpful here, since it could be appending to some frame in
1230 // the middle of the document, which means we're not necessarily
1231 // appending to the children of the containing block.
1232 //
1233 // We need to make sure the 'append to the end of document' case is fast.
1234 // So first test the last child of the containing block
1235 nsIFrame* lastChild = childList.LastChild();
1236
1237 // CompareTreePosition uses placeholder hierarchy for out of flow frames,
1238 // so this will make out-of-flows respect the ordering of placeholders,
1239 // which is great because it takes care of anonymous content.
1240 nsIFrame* firstNewFrame = aFrameItems.FirstChild();
1241
1242 // Cache the ancestor chain so that we can reuse it if needed.
1243 nsAutoTArray<nsIFrame*, 20> firstNewFrameAncestors;
1244 nsIFrame* notCommonAncestor = nullptr;
1245 if (lastChild) {
1246 notCommonAncestor = nsLayoutUtils::FillAncestors(firstNewFrame,
1247 containingBlock,
1248 &firstNewFrameAncestors);
1249 }
1250
1251 if (!lastChild ||
1252 nsLayoutUtils::CompareTreePosition(lastChild, firstNewFrame,
1253 firstNewFrameAncestors,
1254 notCommonAncestor ?
1255 containingBlock : nullptr) < 0) {
1256 // no lastChild, or lastChild comes before the new children, so just append
1257 rv = mFrameManager->AppendFrames(containingBlock, aChildListID, aFrameItems);
1258 } else {
1259 // Try the other children. First collect them to an array so that a
1260 // reasonable fast binary search can be used to find the insertion point.
1261 nsAutoTArray<nsIFrame*, 128> children;
1262 for (nsIFrame* f = childList.FirstChild(); f != lastChild;
1263 f = f->GetNextSibling()) {
1264 children.AppendElement(f);
1265 }
1266
1267 nsIFrame* insertionPoint = nullptr;
1268 int32_t imin = 0;
1269 int32_t max = children.Length();
1270 while (max > imin) {
1271 int32_t imid = imin + ((max - imin) / 2);
1272 nsIFrame* f = children[imid];
1273 int32_t compare =
1274 nsLayoutUtils::CompareTreePosition(f, firstNewFrame, firstNewFrameAncestors,
1275 notCommonAncestor ? containingBlock : nullptr);
1276 if (compare > 0) {
1277 // f is after the new frame.
1278 max = imid;
1279 insertionPoint = imid > 0 ? children[imid - 1] : nullptr;
1280 } else if (compare < 0) {
1281 // f is before the new frame.
1282 imin = imid + 1;
1283 insertionPoint = f;
1284 } else {
1285 // This is for the old behavior. Should be removed once it is
1286 // guaranteed that CompareTreePosition can't return 0!
1287 // See bug 928645.
1288 NS_WARNING("Something odd happening???");
1289 insertionPoint = nullptr;
1290 for (uint32_t i = 0; i < children.Length(); ++i) {
1291 nsIFrame* f = children[i];
1292 if (nsLayoutUtils::CompareTreePosition(f, firstNewFrame,
1293 firstNewFrameAncestors,
1294 notCommonAncestor ?
1295 containingBlock : nullptr) > 0) {
1296 break;
1297 }
1298 insertionPoint = f;
1299 }
1300 break;
1301 }
1302 }
1303 rv = mFrameManager->InsertFrames(containingBlock, aChildListID,
1304 insertionPoint, aFrameItems);
1305 }
1306 }
1307
1308 NS_POSTCONDITION(aFrameItems.IsEmpty(), "How did that happen?");
1309
1310 // XXXbz And if NS_FAILED(rv), what? I guess we need to clean up the list
1311 // and deal with all the placeholders... but what if the placeholders aren't
1312 // in the document yet? Could that happen?
1313 NS_ASSERTION(NS_SUCCEEDED(rv), "Frames getting lost!");
1314 }
1315
1316
1317 nsFrameConstructorSaveState::nsFrameConstructorSaveState()
1318 : mItems(nullptr),
1319 mSavedItems(nullptr),
1320 mChildListID(kPrincipalList),
1321 mState(nullptr),
1322 mSavedFixedItems(nullptr),
1323 mSavedFixedPosIsAbsPos(false)
1324 {
1325 }
1326
1327 nsFrameConstructorSaveState::~nsFrameConstructorSaveState()
1328 {
1329 // Restore the state
1330 if (mItems) {
1331 NS_ASSERTION(mState, "Can't have mItems set without having a state!");
1332 mState->ProcessFrameInsertions(*mItems, mChildListID);
1333 *mItems = mSavedItems;
1334 #ifdef DEBUG
1335 // We've transferred the child list, so drop the pointer we held to it.
1336 // Note that this only matters for the assert in ~nsAbsoluteItems.
1337 mSavedItems.Clear();
1338 #endif
1339 if (mItems == &mState->mAbsoluteItems) {
1340 mState->mFixedPosIsAbsPos = mSavedFixedPosIsAbsPos;
1341 if (mSavedFixedPosIsAbsPos) {
1342 // mAbsoluteItems was moved to mFixedItems, so move mFixedItems back
1343 // and repair the old mFixedItems now.
1344 mState->mAbsoluteItems = mState->mFixedItems;
1345 mState->mFixedItems = mSavedFixedItems;
1346 #ifdef DEBUG
1347 mSavedFixedItems.Clear();
1348 #endif
1349 }
1350 }
1351 NS_ASSERTION(!mItems->LastChild() || !mItems->LastChild()->GetNextSibling(),
1352 "Something corrupted our list");
1353 }
1354 }
1355
1356 static
1357 bool IsBorderCollapse(nsIFrame* aFrame)
1358 {
1359 for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
1360 if (nsGkAtoms::tableFrame == frame->GetType()) {
1361 return ((nsTableFrame*)frame)->IsBorderCollapse();
1362 }
1363 }
1364 NS_ASSERTION(false, "program error");
1365 return false;
1366 }
1367
1368 /**
1369 * Moves aFrameList from aOldParent to aNewParent. This updates the parent
1370 * pointer of the frames in the list, and reparents their views as needed.
1371 * nsFrame::SetParent sets the NS_FRAME_HAS_VIEW bit on aNewParent and its
1372 * ancestors as needed. Then it sets the list as the initial child list
1373 * on aNewParent, unless aNewParent either already has kids or has been
1374 * reflowed; in that case it appends the new frames. Note that this
1375 * method differs from ReparentFrames in that it doesn't change the kids'
1376 * style contexts.
1377 */
1378 // XXXbz Since this is only used for {ib} splits, could we just copy the view
1379 // bits from aOldParent to aNewParent and then use the
1380 // nsFrameList::ApplySetParent? That would still leave us doing two passes
1381 // over the list, of course; if we really wanted to we could factor out the
1382 // relevant part of ReparentFrameViewList, I suppose... Or just get rid of
1383 // views, which would make most of this function go away.
1384 static void
1385 MoveChildrenTo(nsPresContext* aPresContext,
1386 nsIFrame* aOldParent,
1387 nsIFrame* aNewParent,
1388 nsFrameList& aFrameList)
1389 {
1390 bool sameGrandParent = aOldParent->GetParent() == aNewParent->GetParent();
1391
1392 if (aNewParent->HasView() || aOldParent->HasView() || !sameGrandParent) {
1393 // Move the frames into the new view
1394 nsContainerFrame::ReparentFrameViewList(aFrameList, aOldParent, aNewParent);
1395 }
1396
1397 for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
1398 e.get()->SetParent(aNewParent);
1399 }
1400
1401 if (aNewParent->PrincipalChildList().IsEmpty() &&
1402 (aNewParent->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
1403 aNewParent->SetInitialChildList(kPrincipalList, aFrameList);
1404 } else {
1405 aNewParent->AppendFrames(kPrincipalList, aFrameList);
1406 }
1407 }
1408
1409 //----------------------------------------------------------------------
1410
1411 nsCSSFrameConstructor::nsCSSFrameConstructor(nsIDocument *aDocument,
1412 nsIPresShell *aPresShell,
1413 nsStyleSet* aStyleSet)
1414 : nsFrameManager(aPresShell, aStyleSet)
1415 , mDocument(aDocument)
1416 , mRootElementFrame(nullptr)
1417 , mRootElementStyleFrame(nullptr)
1418 , mFixedContainingBlock(nullptr)
1419 , mDocElementContainingBlock(nullptr)
1420 , mGfxScrollFrame(nullptr)
1421 , mPageSequenceFrame(nullptr)
1422 , mCurrentDepth(0)
1423 , mUpdateCount(0)
1424 , mQuotesDirty(false)
1425 , mCountersDirty(false)
1426 , mIsDestroyingFrameTree(false)
1427 , mHasRootAbsPosContainingBlock(false)
1428 , mAlwaysCreateFramesForIgnorableWhitespace(false)
1429 {
1430 #ifdef DEBUG
1431 static bool gFirstTime = true;
1432 if (gFirstTime) {
1433 gFirstTime = false;
1434 char* flags = PR_GetEnv("GECKO_FRAMECTOR_DEBUG_FLAGS");
1435 if (flags) {
1436 bool error = false;
1437 for (;;) {
1438 char* comma = PL_strchr(flags, ',');
1439 if (comma)
1440 *comma = '\0';
1441
1442 bool found = false;
1443 FrameCtorDebugFlags* flag = gFlags;
1444 FrameCtorDebugFlags* limit = gFlags + NUM_DEBUG_FLAGS;
1445 while (flag < limit) {
1446 if (PL_strcasecmp(flag->name, flags) == 0) {
1447 *(flag->on) = true;
1448 printf("nsCSSFrameConstructor: setting %s debug flag on\n", flag->name);
1449 found = true;
1450 break;
1451 }
1452 ++flag;
1453 }
1454
1455 if (! found)
1456 error = true;
1457
1458 if (! comma)
1459 break;
1460
1461 *comma = ',';
1462 flags = comma + 1;
1463 }
1464
1465 if (error) {
1466 printf("Here are the available GECKO_FRAMECTOR_DEBUG_FLAGS:\n");
1467 FrameCtorDebugFlags* flag = gFlags;
1468 FrameCtorDebugFlags* limit = gFlags + NUM_DEBUG_FLAGS;
1469 while (flag < limit) {
1470 printf(" %s\n", flag->name);
1471 ++flag;
1472 }
1473 printf("Note: GECKO_FRAMECTOR_DEBUG_FLAGS is a comma separated list of flag\n");
1474 printf("names (no whitespace)\n");
1475 }
1476 }
1477 }
1478 #endif
1479 }
1480
1481 void
1482 nsCSSFrameConstructor::NotifyDestroyingFrame(nsIFrame* aFrame)
1483 {
1484 NS_PRECONDITION(mUpdateCount != 0,
1485 "Should be in an update while destroying frames");
1486
1487 if (aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT) {
1488 if (mQuoteList.DestroyNodesFor(aFrame))
1489 QuotesDirty();
1490 }
1491
1492 if (mCounterManager.DestroyNodesFor(aFrame)) {
1493 // Technically we don't need to update anything if we destroyed only
1494 // USE nodes. However, this is unlikely to happen in the real world
1495 // since USE nodes generally go along with INCREMENT nodes.
1496 CountersDirty();
1497 }
1498
1499 RestyleManager()->NotifyDestroyingFrame(aFrame);
1500
1501 nsFrameManager::NotifyDestroyingFrame(aFrame);
1502 }
1503
1504 struct nsGenConInitializer {
1505 nsAutoPtr<nsGenConNode> mNode;
1506 nsGenConList* mList;
1507 void (nsCSSFrameConstructor::*mDirtyAll)();
1508
1509 nsGenConInitializer(nsGenConNode* aNode, nsGenConList* aList,
1510 void (nsCSSFrameConstructor::*aDirtyAll)())
1511 : mNode(aNode), mList(aList), mDirtyAll(aDirtyAll) {}
1512 };
1513
1514 already_AddRefed<nsIContent>
1515 nsCSSFrameConstructor::CreateGenConTextNode(nsFrameConstructorState& aState,
1516 const nsString& aString,
1517 nsCOMPtr<nsIDOMCharacterData>* aText,
1518 nsGenConInitializer* aInitializer)
1519 {
1520 nsRefPtr<nsTextNode> content = new nsTextNode(mDocument->NodeInfoManager());
1521 content->SetText(aString, false);
1522 if (aText) {
1523 *aText = content;
1524 }
1525 if (aInitializer) {
1526 content->SetProperty(nsGkAtoms::genConInitializerProperty, aInitializer,
1527 nsINode::DeleteProperty<nsGenConInitializer>);
1528 aState.mGeneratedTextNodesWithInitializer.AppendObject(content);
1529 }
1530 return content.forget();
1531 }
1532
1533 already_AddRefed<nsIContent>
1534 nsCSSFrameConstructor::CreateGeneratedContent(nsFrameConstructorState& aState,
1535 nsIContent* aParentContent,
1536 nsStyleContext* aStyleContext,
1537 uint32_t aContentIndex)
1538 {
1539 // Get the content value
1540 const nsStyleContentData &data =
1541 aStyleContext->StyleContent()->ContentAt(aContentIndex);
1542 nsStyleContentType type = data.mType;
1543
1544 if (eStyleContentType_Image == type) {
1545 if (!data.mContent.mImage) {
1546 // CSS had something specified that couldn't be converted to an
1547 // image object
1548 return nullptr;
1549 }
1550
1551 // Create an image content object and pass it the image request.
1552 // XXX Check if it's an image type we can handle...
1553
1554 nsCOMPtr<nsINodeInfo> nodeInfo;
1555 nodeInfo = mDocument->NodeInfoManager()->
1556 GetNodeInfo(nsGkAtoms::mozgeneratedcontentimage, nullptr,
1557 kNameSpaceID_XHTML, nsIDOMNode::ELEMENT_NODE);
1558
1559 nsCOMPtr<nsIContent> content;
1560 NS_NewGenConImageContent(getter_AddRefs(content), nodeInfo.forget(),
1561 data.mContent.mImage);
1562 return content.forget();
1563 }
1564
1565 switch (type) {
1566 case eStyleContentType_String:
1567 return CreateGenConTextNode(aState,
1568 nsDependentString(data.mContent.mString),
1569 nullptr, nullptr);
1570
1571 case eStyleContentType_Attr:
1572 {
1573 nsCOMPtr<nsIAtom> attrName;
1574 int32_t attrNameSpace = kNameSpaceID_None;
1575 nsAutoString contentString(data.mContent.mString);
1576
1577 int32_t barIndex = contentString.FindChar('|'); // CSS namespace delimiter
1578 if (-1 != barIndex) {
1579 nsAutoString nameSpaceVal;
1580 contentString.Left(nameSpaceVal, barIndex);
1581 nsresult error;
1582 attrNameSpace = nameSpaceVal.ToInteger(&error);
1583 contentString.Cut(0, barIndex + 1);
1584 if (contentString.Length()) {
1585 if (mDocument->IsHTML() && aParentContent->IsHTML()) {
1586 ToLowerCase(contentString);
1587 }
1588 attrName = do_GetAtom(contentString);
1589 }
1590 }
1591 else {
1592 if (mDocument->IsHTML() && aParentContent->IsHTML()) {
1593 ToLowerCase(contentString);
1594 }
1595 attrName = do_GetAtom(contentString);
1596 }
1597
1598 if (!attrName) {
1599 return nullptr;
1600 }
1601
1602 nsCOMPtr<nsIContent> content;
1603 NS_NewAttributeContent(mDocument->NodeInfoManager(),
1604 attrNameSpace, attrName, getter_AddRefs(content));
1605 return content.forget();
1606 }
1607
1608 case eStyleContentType_Counter:
1609 case eStyleContentType_Counters:
1610 {
1611 nsCSSValue::Array* counters = data.mContent.mCounters;
1612 nsCounterList* counterList = mCounterManager.CounterListFor(
1613 nsDependentString(counters->Item(0).GetStringBufferValue()));
1614 if (!counterList)
1615 return nullptr;
1616
1617 nsCounterUseNode* node =
1618 new nsCounterUseNode(counters, aContentIndex,
1619 type == eStyleContentType_Counters);
1620
1621 nsGenConInitializer* initializer =
1622 new nsGenConInitializer(node, counterList,
1623 &nsCSSFrameConstructor::CountersDirty);
1624 return CreateGenConTextNode(aState, EmptyString(), &node->mText,
1625 initializer);
1626 }
1627
1628 case eStyleContentType_Image:
1629 NS_NOTREACHED("handled by if above");
1630 return nullptr;
1631
1632 case eStyleContentType_OpenQuote:
1633 case eStyleContentType_CloseQuote:
1634 case eStyleContentType_NoOpenQuote:
1635 case eStyleContentType_NoCloseQuote:
1636 {
1637 nsQuoteNode* node =
1638 new nsQuoteNode(type, aContentIndex);
1639
1640 nsGenConInitializer* initializer =
1641 new nsGenConInitializer(node, &mQuoteList,
1642 &nsCSSFrameConstructor::QuotesDirty);
1643 return CreateGenConTextNode(aState, EmptyString(), &node->mText,
1644 initializer);
1645 }
1646
1647 case eStyleContentType_AltContent:
1648 {
1649 // Use the "alt" attribute; if that fails and the node is an HTML
1650 // <input>, try the value attribute and then fall back to some default
1651 // localized text we have.
1652 // XXX what if the 'alt' attribute is added later, how will we
1653 // detect that and do the right thing here?
1654 if (aParentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::alt)) {
1655 nsCOMPtr<nsIContent> content;
1656 NS_NewAttributeContent(mDocument->NodeInfoManager(),
1657 kNameSpaceID_None, nsGkAtoms::alt, getter_AddRefs(content));
1658 return content.forget();
1659 }
1660
1661 if (aParentContent->IsHTML() &&
1662 aParentContent->NodeInfo()->Equals(nsGkAtoms::input)) {
1663 if (aParentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
1664 nsCOMPtr<nsIContent> content;
1665 NS_NewAttributeContent(mDocument->NodeInfoManager(),
1666 kNameSpaceID_None, nsGkAtoms::value, getter_AddRefs(content));
1667 return content.forget();
1668 }
1669
1670 nsXPIDLString temp;
1671 nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
1672 "Submit", temp);
1673 return CreateGenConTextNode(aState, temp, nullptr, nullptr);
1674 }
1675
1676 break;
1677 }
1678
1679 case eStyleContentType_Uninitialized:
1680 NS_NOTREACHED("uninitialized content type");
1681 return nullptr;
1682 } // switch
1683
1684 return nullptr;
1685 }
1686
1687 /*
1688 * aParentFrame - the frame that should be the parent of the generated
1689 * content. This is the frame for the corresponding content node,
1690 * which must not be a leaf frame.
1691 *
1692 * Any items created are added to aItems.
1693 *
1694 * We create an XML element (tag _moz_generated_content_before or
1695 * _moz_generated_content_after) representing the pseudoelement. We
1696 * create a DOM node for each 'content' item and make those nodes the
1697 * children of the XML element. Then we create a frame subtree for
1698 * the XML element as if it were a regular child of
1699 * aParentFrame/aParentContent, giving the XML element the ::before or
1700 * ::after style.
1701 */
1702 void
1703 nsCSSFrameConstructor::CreateGeneratedContentItem(nsFrameConstructorState& aState,
1704 nsIFrame* aParentFrame,
1705 nsIContent* aParentContent,
1706 nsStyleContext* aStyleContext,
1707 nsCSSPseudoElements::Type aPseudoElement,
1708 FrameConstructionItemList& aItems)
1709 {
1710 // XXXbz is this ever true?
1711 if (!aParentContent->IsElement()) {
1712 NS_ERROR("Bogus generated content parent");
1713 return;
1714 }
1715
1716 nsStyleSet *styleSet = mPresShell->StyleSet();
1717
1718 // Probe for the existence of the pseudo-element
1719 nsRefPtr<nsStyleContext> pseudoStyleContext;
1720 pseudoStyleContext =
1721 styleSet->ProbePseudoElementStyle(aParentContent->AsElement(),
1722 aPseudoElement,
1723 aStyleContext,
1724 aState.mTreeMatchContext);
1725 if (!pseudoStyleContext)
1726 return;
1727 // |ProbePseudoStyleFor| checked the 'display' property and the
1728 // |ContentCount()| of the 'content' property for us.
1729 nsCOMPtr<nsINodeInfo> nodeInfo;
1730 nsIAtom* elemName = aPseudoElement == nsCSSPseudoElements::ePseudo_before ?
1731 nsGkAtoms::mozgeneratedcontentbefore : nsGkAtoms::mozgeneratedcontentafter;
1732 nodeInfo = mDocument->NodeInfoManager()->GetNodeInfo(elemName, nullptr,
1733 kNameSpaceID_None,
1734 nsIDOMNode::ELEMENT_NODE);
1735 nsCOMPtr<Element> container;
1736 nsresult rv = NS_NewXMLElement(getter_AddRefs(container), nodeInfo.forget());
1737 if (NS_FAILED(rv))
1738 return;
1739 container->SetIsNativeAnonymousRoot();
1740
1741 rv = container->BindToTree(mDocument, aParentContent, aParentContent, true);
1742 if (NS_FAILED(rv)) {
1743 container->UnbindFromTree();
1744 return;
1745 }
1746
1747 uint32_t contentCount = pseudoStyleContext->StyleContent()->ContentCount();
1748 for (uint32_t contentIndex = 0; contentIndex < contentCount; contentIndex++) {
1749 nsCOMPtr<nsIContent> content =
1750 CreateGeneratedContent(aState, aParentContent, pseudoStyleContext,
1751 contentIndex);
1752 if (content) {
1753 container->AppendChildTo(content, false);
1754 }
1755 }
1756
1757 AddFrameConstructionItemsInternal(aState, container, aParentFrame, elemName,
1758 kNameSpaceID_None, true,
1759 pseudoStyleContext,
1760 ITEM_IS_GENERATED_CONTENT, nullptr,
1761 aItems);
1762 }
1763
1764 /****************************************************
1765 ** BEGIN TABLE SECTION
1766 ****************************************************/
1767
1768 // The term pseudo frame is being used instead of anonymous frame, since anonymous
1769 // frame has been used elsewhere to refer to frames that have generated content
1770
1771 // Return whether the given frame is a table pseudo-frame. Note that
1772 // cell-content and table-outer frames have pseudo-types, but are always
1773 // created, even for non-anonymous cells and tables respectively. So for those
1774 // we have to examine the cell or table frame to see whether it's a pseudo
1775 // frame. In particular, a lone table caption will have an outer table as its
1776 // parent, but will also trigger construction of an empty inner table, which
1777 // will be the one we can examine to see whether the outer was a pseudo-frame.
1778 static bool
1779 IsTablePseudo(nsIFrame* aFrame)
1780 {
1781 nsIAtom* pseudoType = aFrame->StyleContext()->GetPseudo();
1782 return pseudoType &&
1783 (pseudoType == nsCSSAnonBoxes::table ||
1784 pseudoType == nsCSSAnonBoxes::inlineTable ||
1785 pseudoType == nsCSSAnonBoxes::tableColGroup ||
1786 pseudoType == nsCSSAnonBoxes::tableRowGroup ||
1787 pseudoType == nsCSSAnonBoxes::tableRow ||
1788 pseudoType == nsCSSAnonBoxes::tableCell ||
1789 (pseudoType == nsCSSAnonBoxes::cellContent &&
1790 aFrame->GetParent()->StyleContext()->GetPseudo() ==
1791 nsCSSAnonBoxes::tableCell) ||
1792 (pseudoType == nsCSSAnonBoxes::tableOuter &&
1793 (aFrame->GetFirstPrincipalChild()->StyleContext()->GetPseudo() ==
1794 nsCSSAnonBoxes::table ||
1795 aFrame->GetFirstPrincipalChild()->StyleContext()->GetPseudo() ==
1796 nsCSSAnonBoxes::inlineTable)));
1797 }
1798
1799 /* static */
1800 nsCSSFrameConstructor::ParentType
1801 nsCSSFrameConstructor::GetParentType(nsIAtom* aFrameType)
1802 {
1803 if (aFrameType == nsGkAtoms::tableFrame) {
1804 return eTypeTable;
1805 }
1806 if (aFrameType == nsGkAtoms::tableRowGroupFrame) {
1807 return eTypeRowGroup;
1808 }
1809 if (aFrameType == nsGkAtoms::tableRowFrame) {
1810 return eTypeRow;
1811 }
1812 if (aFrameType == nsGkAtoms::tableColGroupFrame) {
1813 return eTypeColGroup;
1814 }
1815
1816 return eTypeBlock;
1817 }
1818
1819 static nsIFrame*
1820 AdjustCaptionParentFrame(nsIFrame* aParentFrame)
1821 {
1822 if (nsGkAtoms::tableFrame == aParentFrame->GetType()) {
1823 return aParentFrame->GetParent();;
1824 }
1825 return aParentFrame;
1826 }
1827
1828 /**
1829 * If the parent frame is a |tableFrame| and the child is a
1830 * |captionFrame|, then we want to insert the frames beneath the
1831 * |tableFrame|'s parent frame. Returns |true| if the parent frame
1832 * needed to be fixed up.
1833 */
1834 static bool
1835 GetCaptionAdjustedParent(nsIFrame* aParentFrame,
1836 const nsIFrame* aChildFrame,
1837 nsIFrame** aAdjParentFrame)
1838 {
1839 *aAdjParentFrame = aParentFrame;
1840 bool haveCaption = false;
1841
1842 if (nsGkAtoms::tableCaptionFrame == aChildFrame->GetType()) {
1843 haveCaption = true;
1844 *aAdjParentFrame = AdjustCaptionParentFrame(aParentFrame);
1845 }
1846 return haveCaption;
1847 }
1848
1849 void
1850 nsCSSFrameConstructor::AdjustParentFrame(nsIFrame* & aParentFrame,
1851 const FrameConstructionData* aFCData,
1852 nsStyleContext* aStyleContext)
1853 {
1854 NS_PRECONDITION(aStyleContext, "Must have child's style context");
1855 NS_PRECONDITION(aFCData, "Must have frame construction data");
1856
1857 bool tablePart = ((aFCData->mBits & FCDATA_IS_TABLE_PART) != 0);
1858
1859 if (tablePart && aStyleContext->StyleDisplay()->mDisplay ==
1860 NS_STYLE_DISPLAY_TABLE_CAPTION) {
1861 aParentFrame = AdjustCaptionParentFrame(aParentFrame);
1862 }
1863 }
1864
1865 // Pull all the captions present in aItems out into aCaptions
1866 static void
1867 PullOutCaptionFrames(nsFrameItems& aItems, nsFrameItems& aCaptions)
1868 {
1869 nsIFrame *child = aItems.FirstChild();
1870 while (child) {
1871 nsIFrame *nextSibling = child->GetNextSibling();
1872 if (nsGkAtoms::tableCaptionFrame == child->GetType()) {
1873 aItems.RemoveFrame(child);
1874 aCaptions.AddChild(child);
1875 }
1876 child = nextSibling;
1877 }
1878 }
1879
1880
1881 // Construct the outer, inner table frames and the children frames for the table.
1882 // XXX Page break frames for pseudo table frames are not constructed to avoid the risk
1883 // associated with revising the pseudo frame mechanism. The long term solution
1884 // of having frames handle page-break-before/after will solve the problem.
1885 nsIFrame*
1886 nsCSSFrameConstructor::ConstructTable(nsFrameConstructorState& aState,
1887 FrameConstructionItem& aItem,
1888 nsIFrame* aParentFrame,
1889 const nsStyleDisplay* aDisplay,
1890 nsFrameItems& aFrameItems)
1891 {
1892 NS_PRECONDITION(aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE ||
1893 aDisplay->mDisplay == NS_STYLE_DISPLAY_INLINE_TABLE,
1894 "Unexpected call");
1895
1896 nsIContent* const content = aItem.mContent;
1897 nsStyleContext* const styleContext = aItem.mStyleContext;
1898 const uint32_t nameSpaceID = aItem.mNameSpaceID;
1899
1900 // create the pseudo SC for the outer table as a child of the inner SC
1901 nsRefPtr<nsStyleContext> outerStyleContext;
1902 outerStyleContext = mPresShell->StyleSet()->
1903 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::tableOuter, styleContext);
1904
1905 // Create the outer table frame which holds the caption and inner table frame
1906 nsIFrame* newFrame;
1907 if (kNameSpaceID_MathML == nameSpaceID)
1908 newFrame = NS_NewMathMLmtableOuterFrame(mPresShell, outerStyleContext);
1909 else
1910 newFrame = NS_NewTableOuterFrame(mPresShell, outerStyleContext);
1911
1912 nsIFrame* geometricParent =
1913 aState.GetGeometricParent(outerStyleContext->StyleDisplay(),
1914 aParentFrame);
1915
1916 // Init the table outer frame
1917 InitAndRestoreFrame(aState, content, geometricParent, newFrame);
1918
1919 // Create the inner table frame
1920 nsIFrame* innerFrame;
1921 if (kNameSpaceID_MathML == nameSpaceID)
1922 innerFrame = NS_NewMathMLmtableFrame(mPresShell, styleContext);
1923 else
1924 innerFrame = NS_NewTableFrame(mPresShell, styleContext);
1925
1926 InitAndRestoreFrame(aState, content, newFrame, innerFrame);
1927
1928 // Put the newly created frames into the right child list
1929 SetInitialSingleChild(newFrame, innerFrame);
1930
1931 aState.AddChild(newFrame, aFrameItems, content, styleContext, aParentFrame);
1932
1933 if (!mRootElementFrame) {
1934 // The frame we're constructing will be the root element frame.
1935 // Set mRootElementFrame before processing children.
1936 mRootElementFrame = newFrame;
1937 }
1938
1939 nsFrameItems childItems;
1940
1941 // Process children
1942 nsFrameConstructorSaveState absoluteSaveState;
1943 const nsStyleDisplay* display = outerStyleContext->StyleDisplay();
1944
1945 // Mark the table frame as an absolute container if needed
1946 newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
1947 if (display->IsPositioned(newFrame)) {
1948 aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState);
1949 }
1950 NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
1951 "nsIAnonymousContentCreator::CreateAnonymousContent "
1952 "implementations for table frames are not currently expected "
1953 "to output a list where the items have their own children");
1954 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
1955 ConstructFramesFromItemList(aState, aItem.mChildItems,
1956 innerFrame, childItems);
1957 } else {
1958 ProcessChildren(aState, content, styleContext, innerFrame,
1959 true, childItems, false, aItem.mPendingBinding);
1960 }
1961
1962 nsFrameItems captionItems;
1963 PullOutCaptionFrames(childItems, captionItems);
1964
1965 // Set the inner table frame's initial primary list
1966 innerFrame->SetInitialChildList(kPrincipalList, childItems);
1967
1968 // Set the outer table frame's secondary childlist lists
1969 if (captionItems.NotEmpty()) {
1970 newFrame->SetInitialChildList(nsIFrame::kCaptionList, captionItems);
1971 }
1972
1973 return newFrame;
1974 }
1975
1976 static void
1977 MakeTablePartAbsoluteContainingBlockIfNeeded(nsFrameConstructorState& aState,
1978 const nsStyleDisplay* aDisplay,
1979 nsFrameConstructorSaveState& aAbsSaveState,
1980 nsIFrame* aFrame)
1981 {
1982 // If we're positioned, then we need to become an absolute containing block
1983 // for any absolutely positioned children and register for post-reflow fixup.
1984 //
1985 // Note that usually if a frame type can be an absolute containing block, we
1986 // always set NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN, whether it actually is or not.
1987 // However, in this case flag serves the additional purpose of indicating that
1988 // the frame was registered with its table frame. This allows us to avoid the
1989 // overhead of unregistering the frame in most cases.
1990 if (aDisplay->IsPositioned(aFrame)) {
1991 aFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
1992 aState.PushAbsoluteContainingBlock(aFrame, aFrame, aAbsSaveState);
1993 nsTableFrame::RegisterPositionedTablePart(aFrame);
1994 }
1995 }
1996
1997 nsIFrame*
1998 nsCSSFrameConstructor::ConstructTableRowOrRowGroup(nsFrameConstructorState& aState,
1999 FrameConstructionItem& aItem,
2000 nsIFrame* aParentFrame,
2001 const nsStyleDisplay* aDisplay,
2002 nsFrameItems& aFrameItems)
2003 {
2004 NS_PRECONDITION(aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_ROW ||
2005 aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_ROW_GROUP ||
2006 aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP ||
2007 aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_HEADER_GROUP,
2008 "Unexpected call");
2009 MOZ_ASSERT(aItem.mStyleContext->StyleDisplay() == aDisplay,
2010 "Display style doesn't match style context");
2011 nsIContent* const content = aItem.mContent;
2012 nsStyleContext* const styleContext = aItem.mStyleContext;
2013 const uint32_t nameSpaceID = aItem.mNameSpaceID;
2014
2015 nsIFrame* newFrame;
2016 if (aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_ROW) {
2017 if (kNameSpaceID_MathML == nameSpaceID)
2018 newFrame = NS_NewMathMLmtrFrame(mPresShell, styleContext);
2019 else
2020 newFrame = NS_NewTableRowFrame(mPresShell, styleContext);
2021 } else {
2022 newFrame = NS_NewTableRowGroupFrame(mPresShell, styleContext);
2023 }
2024
2025 InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
2026
2027 nsFrameConstructorSaveState absoluteSaveState;
2028 MakeTablePartAbsoluteContainingBlockIfNeeded(aState, aDisplay,
2029 absoluteSaveState,
2030 newFrame);
2031
2032 nsFrameItems childItems;
2033 NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
2034 "nsIAnonymousContentCreator::CreateAnonymousContent "
2035 "implementations for table frames are not currently expected "
2036 "to output a list where the items have their own children");
2037 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
2038 ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame,
2039 childItems);
2040 } else {
2041 ProcessChildren(aState, content, styleContext, newFrame,
2042 true, childItems, false, aItem.mPendingBinding);
2043 }
2044
2045 newFrame->SetInitialChildList(kPrincipalList, childItems);
2046 aFrameItems.AddChild(newFrame);
2047 return newFrame;
2048 }
2049
2050 nsIFrame*
2051 nsCSSFrameConstructor::ConstructTableCol(nsFrameConstructorState& aState,
2052 FrameConstructionItem& aItem,
2053 nsIFrame* aParentFrame,
2054 const nsStyleDisplay* aStyleDisplay,
2055 nsFrameItems& aFrameItems)
2056 {
2057 nsIContent* const content = aItem.mContent;
2058 nsStyleContext* const styleContext = aItem.mStyleContext;
2059
2060 nsTableColFrame* colFrame = NS_NewTableColFrame(mPresShell, styleContext);
2061 InitAndRestoreFrame(aState, content, aParentFrame, colFrame);
2062
2063 NS_ASSERTION(colFrame->StyleContext() == styleContext,
2064 "Unexpected style context");
2065
2066 aFrameItems.AddChild(colFrame);
2067
2068 // construct additional col frames if the col frame has a span > 1
2069 int32_t span = colFrame->GetSpan();
2070 for (int32_t spanX = 1; spanX < span; spanX++) {
2071 nsTableColFrame* newCol = NS_NewTableColFrame(mPresShell, styleContext);
2072 InitAndRestoreFrame(aState, content, aParentFrame, newCol, false);
2073 aFrameItems.LastChild()->SetNextContinuation(newCol);
2074 newCol->SetPrevContinuation(aFrameItems.LastChild());
2075 aFrameItems.AddChild(newCol);
2076 newCol->SetColType(eColAnonymousCol);
2077 }
2078
2079 return colFrame;
2080 }
2081
2082 nsIFrame*
2083 nsCSSFrameConstructor::ConstructTableCell(nsFrameConstructorState& aState,
2084 FrameConstructionItem& aItem,
2085 nsIFrame* aParentFrame,
2086 const nsStyleDisplay* aDisplay,
2087 nsFrameItems& aFrameItems)
2088 {
2089 NS_PRECONDITION(aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_CELL,
2090 "Unexpected call");
2091
2092 nsIContent* const content = aItem.mContent;
2093 nsStyleContext* const styleContext = aItem.mStyleContext;
2094 const uint32_t nameSpaceID = aItem.mNameSpaceID;
2095
2096 bool borderCollapse = IsBorderCollapse(aParentFrame);
2097 nsIFrame* newFrame;
2098 // <mtable> is border separate in mathml.css and the MathML code doesn't implement
2099 // border collapse. For those users who style <mtable> with border collapse,
2100 // give them the default non-MathML table frames that understand border collapse.
2101 // This won't break us because MathML table frames are all subclasses of the default
2102 // table code, and so we can freely mix <mtable> with <mtr> or <tr>, <mtd> or <td>.
2103 // What will happen is just that non-MathML frames won't understand MathML attributes
2104 // and will therefore miss the special handling that the MathML code does.
2105 if (kNameSpaceID_MathML == nameSpaceID && !borderCollapse)
2106 newFrame = NS_NewMathMLmtdFrame(mPresShell, styleContext);
2107 else
2108 // Warning: If you change this and add a wrapper frame around table cell
2109 // frames, make sure Bug 368554 doesn't regress!
2110 // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp.
2111 newFrame = NS_NewTableCellFrame(mPresShell, styleContext, borderCollapse);
2112
2113 // Initialize the table cell frame
2114 InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
2115
2116 // Resolve pseudo style and initialize the body cell frame
2117 nsRefPtr<nsStyleContext> innerPseudoStyle;
2118 innerPseudoStyle = mPresShell->StyleSet()->
2119 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::cellContent, styleContext);
2120
2121 // Create a block frame that will format the cell's content
2122 bool isBlock;
2123 nsIFrame* cellInnerFrame;
2124 if (kNameSpaceID_MathML == nameSpaceID) {
2125 cellInnerFrame = NS_NewMathMLmtdInnerFrame(mPresShell, innerPseudoStyle);
2126 isBlock = false;
2127 } else {
2128 cellInnerFrame = NS_NewBlockFormattingContext(mPresShell, innerPseudoStyle);
2129 isBlock = true;
2130 }
2131
2132 InitAndRestoreFrame(aState, content, newFrame, cellInnerFrame);
2133
2134 nsFrameConstructorSaveState absoluteSaveState;
2135 MakeTablePartAbsoluteContainingBlockIfNeeded(aState, aDisplay,
2136 absoluteSaveState,
2137 newFrame);
2138
2139 nsFrameItems childItems;
2140 NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
2141 "nsIAnonymousContentCreator::CreateAnonymousContent "
2142 "implementations for table frames are not currently expected "
2143 "to output a list where the items have their own children");
2144 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
2145 // Need to push ourselves as a float containing block.
2146 // XXXbz it might be nice to work on getting the parent
2147 // FrameConstructionItem down into ProcessChildren and just making use of
2148 // the push there, but that's a bit of work.
2149 nsFrameConstructorSaveState floatSaveState;
2150 if (!isBlock) { /* MathML case */
2151 aState.PushFloatContainingBlock(nullptr, floatSaveState);
2152 } else {
2153 aState.PushFloatContainingBlock(cellInnerFrame, floatSaveState);
2154 }
2155
2156 ConstructFramesFromItemList(aState, aItem.mChildItems, cellInnerFrame,
2157 childItems);
2158 } else {
2159 // Process the child content
2160 ProcessChildren(aState, content, styleContext, cellInnerFrame,
2161 true, childItems, isBlock, aItem.mPendingBinding);
2162 }
2163
2164 cellInnerFrame->SetInitialChildList(kPrincipalList, childItems);
2165 SetInitialSingleChild(newFrame, cellInnerFrame);
2166 aFrameItems.AddChild(newFrame);
2167 return newFrame;
2168 }
2169
2170 static inline bool
2171 NeedFrameFor(const nsFrameConstructorState& aState,
2172 nsIFrame* aParentFrame,
2173 nsIContent* aChildContent)
2174 {
2175 // XXX the GetContent() != aChildContent check is needed due to bug 135040.
2176 // Remove it once that's fixed.
2177 NS_PRECONDITION(!aChildContent->GetPrimaryFrame() ||
2178 aState.mCreatingExtraFrames ||
2179 aChildContent->GetPrimaryFrame()->GetContent() != aChildContent,
2180 "Why did we get called?");
2181
2182 // don't create a whitespace frame if aParentFrame doesn't want it.
2183 // always create frames for children in generated content. counter(),
2184 // quotes, and attr() content can easily change dynamically and we don't
2185 // want to be reconstructing frames. It's not even clear that these
2186 // should be considered ignorable just because they evaluate to
2187 // whitespace.
2188
2189 // We could handle all this in CreateNeededTablePseudos or some other place
2190 // after we build our frame construction items, but that would involve
2191 // creating frame construction items for whitespace kids of
2192 // eExcludesIgnorableWhitespace frames, where we know we'll be dropping them
2193 // all anyway, and involve an extra walk down the frame construction item
2194 // list.
2195 if (!aParentFrame->IsFrameOfType(nsIFrame::eExcludesIgnorableWhitespace) ||
2196 aParentFrame->IsGeneratedContentFrame() ||
2197 !aChildContent->IsNodeOfType(nsINode::eTEXT)) {
2198 return true;
2199 }
2200
2201 aChildContent->SetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
2202 NS_REFRAME_IF_WHITESPACE);
2203 return !aChildContent->TextIsOnlyWhitespace();
2204 }
2205
2206 /***********************************************
2207 * END TABLE SECTION
2208 ***********************************************/
2209
2210 static bool CheckOverflow(nsPresContext* aPresContext,
2211 const nsStyleDisplay* aDisplay)
2212 {
2213 if (aDisplay->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE)
2214 return false;
2215
2216 if (aDisplay->mOverflowX == NS_STYLE_OVERFLOW_CLIP)
2217 aPresContext->SetViewportOverflowOverride(NS_STYLE_OVERFLOW_HIDDEN,
2218 NS_STYLE_OVERFLOW_HIDDEN);
2219 else
2220 aPresContext->SetViewportOverflowOverride(aDisplay->mOverflowX,
2221 aDisplay->mOverflowY);
2222 return true;
2223 }
2224
2225 /**
2226 * This checks the root element and the HTML BODY, if any, for an "overflow" property
2227 * that should be applied to the viewport. If one is found then we return the
2228 * element that we took the overflow from (which should then be treated as
2229 * "overflow:visible"), and we store the overflow style in the prescontext.
2230 * @return if scroll was propagated from some content node, the content node it
2231 * was propagated from.
2232 */
2233 nsIContent*
2234 nsCSSFrameConstructor::PropagateScrollToViewport()
2235 {
2236 // Set default
2237 nsPresContext* presContext = mPresShell->GetPresContext();
2238 presContext->SetViewportOverflowOverride(NS_STYLE_OVERFLOW_AUTO,
2239 NS_STYLE_OVERFLOW_AUTO);
2240
2241 // We never mess with the viewport scroll state
2242 // when printing or in print preview
2243 if (presContext->IsPaginated()) {
2244 return nullptr;
2245 }
2246
2247 Element* docElement = mDocument->GetRootElement();
2248
2249 // Check the style on the document root element
2250 nsStyleSet *styleSet = mPresShell->StyleSet();
2251 nsRefPtr<nsStyleContext> rootStyle;
2252 rootStyle = styleSet->ResolveStyleFor(docElement, nullptr);
2253 if (CheckOverflow(presContext, rootStyle->StyleDisplay())) {
2254 // tell caller we stole the overflow style from the root element
2255 return docElement;
2256 }
2257
2258 // Don't look in the BODY for non-HTML documents or HTML documents
2259 // with non-HTML roots
2260 // XXX this should be earlier; we shouldn't even look at the document root
2261 // for non-HTML documents. Fix this once we support explicit CSS styling
2262 // of the viewport
2263 // XXX what about XHTML?
2264 nsCOMPtr<nsIDOMHTMLDocument> htmlDoc(do_QueryInterface(mDocument));
2265 if (!htmlDoc || !docElement->IsHTML()) {
2266 return nullptr;
2267 }
2268
2269 nsCOMPtr<nsIDOMHTMLElement> body;
2270 htmlDoc->GetBody(getter_AddRefs(body));
2271 nsCOMPtr<nsIContent> bodyElement = do_QueryInterface(body);
2272
2273 if (!bodyElement ||
2274 !bodyElement->NodeInfo()->Equals(nsGkAtoms::body)) {
2275 // The body is not a <body> tag, it's a <frameset>.
2276 return nullptr;
2277 }
2278
2279 nsRefPtr<nsStyleContext> bodyStyle;
2280 bodyStyle = styleSet->ResolveStyleFor(bodyElement->AsElement(), rootStyle);
2281
2282 if (CheckOverflow(presContext, bodyStyle->StyleDisplay())) {
2283 // tell caller we stole the overflow style from the body element
2284 return bodyElement;
2285 }
2286
2287 return nullptr;
2288 }
2289
2290 nsIFrame*
2291 nsCSSFrameConstructor::ConstructDocElementFrame(Element* aDocElement,
2292 nsILayoutHistoryState* aFrameState)
2293 {
2294 NS_PRECONDITION(mFixedContainingBlock,
2295 "No viewport? Someone forgot to call ConstructRootFrame!");
2296 NS_PRECONDITION(mFixedContainingBlock == GetRootFrame(),
2297 "Unexpected mFixedContainingBlock");
2298 NS_PRECONDITION(!mDocElementContainingBlock,
2299 "Shouldn't have a doc element containing block here");
2300
2301 // Make sure to call PropagateScrollToViewport before
2302 // SetUpDocElementContainingBlock, since it sets up our scrollbar state
2303 // properly.
2304 #ifdef DEBUG
2305 nsIContent* propagatedScrollFrom =
2306 #endif
2307 PropagateScrollToViewport();
2308
2309 SetUpDocElementContainingBlock(aDocElement);
2310
2311 NS_ASSERTION(mDocElementContainingBlock, "Should have parent by now");
2312
2313 nsFrameConstructorState state(mPresShell, mFixedContainingBlock, nullptr,
2314 nullptr, aFrameState);
2315 // Initialize the ancestor filter with null for now; we'll push
2316 // aDocElement once we finish resolving style for it.
2317 state.mTreeMatchContext.InitAncestors(nullptr);
2318
2319 // XXXbz why, exactly?
2320 if (!mTempFrameTreeState)
2321 state.mPresShell->CaptureHistoryState(getter_AddRefs(mTempFrameTreeState));
2322
2323 // Make sure that we'll handle restyles for this document element in
2324 // the future. We need this, because the document element might
2325 // have stale restyle bits from a previous frame constructor for
2326 // this document. Unlike in AddFrameConstructionItems, it's safe to
2327 // unset all element restyle flags, since we don't have any
2328 // siblings.
2329 aDocElement->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
2330
2331 // --------- CREATE AREA OR BOX FRAME -------
2332 nsRefPtr<nsStyleContext> styleContext;
2333 styleContext = mPresShell->StyleSet()->ResolveStyleFor(aDocElement,
2334 nullptr);
2335
2336 const nsStyleDisplay* display = styleContext->StyleDisplay();
2337
2338 // Ensure that our XBL bindings are installed.
2339 if (display->mBinding) {
2340 // Get the XBL loader.
2341 nsresult rv;
2342 bool resolveStyle;
2343
2344 nsXBLService* xblService = nsXBLService::GetInstance();
2345 if (!xblService) {
2346 return nullptr;
2347 }
2348
2349 nsRefPtr<nsXBLBinding> binding;
2350 rv = xblService->LoadBindings(aDocElement, display->mBinding->GetURI(),
2351 display->mBinding->mOriginPrincipal,
2352 getter_AddRefs(binding), &resolveStyle);
2353 if (NS_FAILED(rv) && rv != NS_ERROR_XBL_BLOCKED)
2354 return nullptr; // Binding will load asynchronously.
2355
2356 if (binding) {
2357 // For backwards compat, keep firing the root's constructor
2358 // after all of its kids' constructors. So tell the binding
2359 // manager about it right now.
2360 mDocument->BindingManager()->AddToAttachedQueue(binding);
2361 }
2362
2363 if (resolveStyle) {
2364 styleContext = mPresShell->StyleSet()->ResolveStyleFor(aDocElement,
2365 nullptr);
2366 display = styleContext->StyleDisplay();
2367 }
2368 }
2369
2370 // --------- IF SCROLLABLE WRAP IN SCROLLFRAME --------
2371
2372 #ifdef DEBUG
2373 NS_ASSERTION(!display->IsScrollableOverflow() ||
2374 state.mPresContext->IsPaginated() ||
2375 propagatedScrollFrom == aDocElement,
2376 "Scrollbars should have been propagated to the viewport");
2377 #endif
2378
2379 if (MOZ_UNLIKELY(display->mDisplay == NS_STYLE_DISPLAY_NONE)) {
2380 SetUndisplayedContent(aDocElement, styleContext);
2381 return nullptr;
2382 }
2383
2384 TreeMatchContext::AutoAncestorPusher ancestorPusher(state.mTreeMatchContext);
2385 ancestorPusher.PushAncestorAndStyleScope(aDocElement);
2386
2387 // Make sure to start any background image loads for the root element now.
2388 styleContext->StartBackgroundImageLoads();
2389
2390 nsFrameConstructorSaveState absoluteSaveState;
2391 if (mHasRootAbsPosContainingBlock) {
2392 // Push the absolute containing block now so we can absolutely position
2393 // the root element
2394 mDocElementContainingBlock->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2395 state.PushAbsoluteContainingBlock(mDocElementContainingBlock,
2396 mDocElementContainingBlock,
2397 absoluteSaveState);
2398 }
2399
2400 // The rules from CSS 2.1, section 9.2.4, have already been applied
2401 // by the style system, so we can assume that display->mDisplay is
2402 // either NONE, BLOCK, or TABLE.
2403
2404 // contentFrame is the primary frame for the root element. newFrame
2405 // is the frame that will be the child of the initial containing block.
2406 // These are usually the same frame but they can be different, in
2407 // particular if the root frame is positioned, in which case
2408 // contentFrame is the out-of-flow frame and newFrame is the
2409 // placeholder.
2410 nsIFrame* contentFrame;
2411 nsIFrame* newFrame;
2412 bool processChildren = false;
2413
2414 // Check whether we need to build a XUL box or SVG root frame
2415 #ifdef MOZ_XUL
2416 if (aDocElement->IsXUL()) {
2417 contentFrame = NS_NewDocElementBoxFrame(mPresShell, styleContext);
2418 InitAndRestoreFrame(state, aDocElement, mDocElementContainingBlock,
2419 contentFrame);
2420 newFrame = contentFrame;
2421 processChildren = true;
2422 }
2423 else
2424 #endif
2425 if (aDocElement->IsSVG()) {
2426 if (aDocElement->Tag() != nsGkAtoms::svg) {
2427 return nullptr;
2428 }
2429 // We're going to call the right function ourselves, so no need to give a
2430 // function to this FrameConstructionData.
2431
2432 // XXXbz on the other hand, if we converted this whole function to
2433 // FrameConstructionData/Item, then we'd need the right function
2434 // here... but would probably be able to get away with less code in this
2435 // function in general.
2436 // Use a null PendingBinding, since our binding is not in fact pending.
2437 static const FrameConstructionData rootSVGData = FCDATA_DECL(0, nullptr);
2438 already_AddRefed<nsStyleContext> extraRef =
2439 nsRefPtr<nsStyleContext>(styleContext).forget();
2440 FrameConstructionItem item(&rootSVGData, aDocElement,
2441 aDocElement->Tag(), kNameSpaceID_SVG,
2442 nullptr, extraRef, true, nullptr);
2443
2444 nsFrameItems frameItems;
2445 contentFrame = ConstructOuterSVG(state, item, mDocElementContainingBlock,
2446 styleContext->StyleDisplay(),
2447 frameItems);
2448 newFrame = frameItems.FirstChild();
2449 NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
2450 } else if (display->mDisplay == NS_STYLE_DISPLAY_FLEX) {
2451 contentFrame = NS_NewFlexContainerFrame(mPresShell, styleContext);
2452 InitAndRestoreFrame(state, aDocElement, mDocElementContainingBlock,
2453 contentFrame);
2454 newFrame = contentFrame;
2455 processChildren = true;
2456 } else if (display->mDisplay == NS_STYLE_DISPLAY_GRID) {
2457 contentFrame = NS_NewGridContainerFrame(mPresShell, styleContext);
2458 InitAndRestoreFrame(state, aDocElement, mDocElementContainingBlock,
2459 contentFrame);
2460 newFrame = contentFrame;
2461 processChildren = true;
2462 } else if (display->mDisplay == NS_STYLE_DISPLAY_TABLE) {
2463 // We're going to call the right function ourselves, so no need to give a
2464 // function to this FrameConstructionData.
2465
2466 // XXXbz on the other hand, if we converted this whole function to
2467 // FrameConstructionData/Item, then we'd need the right function
2468 // here... but would probably be able to get away with less code in this
2469 // function in general.
2470 // Use a null PendingBinding, since our binding is not in fact pending.
2471 static const FrameConstructionData rootTableData = FCDATA_DECL(0, nullptr);
2472 already_AddRefed<nsStyleContext> extraRef =
2473 nsRefPtr<nsStyleContext>(styleContext).forget();
2474 FrameConstructionItem item(&rootTableData, aDocElement,
2475 aDocElement->Tag(), kNameSpaceID_None,
2476 nullptr, extraRef, true, nullptr);
2477
2478 nsFrameItems frameItems;
2479 // if the document is a table then just populate it.
2480 contentFrame = ConstructTable(state, item, mDocElementContainingBlock,
2481 styleContext->StyleDisplay(),
2482 frameItems);
2483 newFrame = frameItems.FirstChild();
2484 NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
2485 } else {
2486 MOZ_ASSERT(display->mDisplay == NS_STYLE_DISPLAY_BLOCK,
2487 "Unhandled display type for root element");
2488 contentFrame = NS_NewBlockFormattingContext(mPresShell, styleContext);
2489 nsFrameItems frameItems;
2490 // Use a null PendingBinding, since our binding is not in fact pending.
2491 ConstructBlock(state, display, aDocElement,
2492 state.GetGeometricParent(display,
2493 mDocElementContainingBlock),
2494 mDocElementContainingBlock, styleContext,
2495 &contentFrame, frameItems,
2496 display->IsPositioned(contentFrame) ? contentFrame : nullptr,
2497 nullptr);
2498 newFrame = frameItems.FirstChild();
2499 NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
2500 }
2501
2502 MOZ_ASSERT(newFrame);
2503 MOZ_ASSERT(contentFrame);
2504
2505 NS_ASSERTION(processChildren ? !mRootElementFrame :
2506 mRootElementFrame == contentFrame,
2507 "unexpected mRootElementFrame");
2508 mRootElementFrame = contentFrame;
2509
2510 // Figure out which frame has the main style for the document element,
2511 // assigning it to mRootElementStyleFrame.
2512 // Backgrounds should be propagated from that frame to the viewport.
2513 mRootElementStyleFrame = contentFrame->GetParentStyleContextFrame();
2514 bool isChild = mRootElementStyleFrame &&
2515 mRootElementStyleFrame->GetParent() == contentFrame;
2516 if (!isChild) {
2517 mRootElementStyleFrame = mRootElementFrame;
2518 }
2519
2520 if (processChildren) {
2521 // Still need to process the child content
2522 nsFrameItems childItems;
2523
2524 NS_ASSERTION(!nsLayoutUtils::GetAsBlock(contentFrame) &&
2525 !contentFrame->IsFrameOfType(nsIFrame::eSVG),
2526 "Only XUL frames should reach here");
2527 // Use a null PendingBinding, since our binding is not in fact pending.
2528 ProcessChildren(state, aDocElement, styleContext, contentFrame, true,
2529 childItems, false, nullptr);
2530
2531 // Set the initial child lists
2532 contentFrame->SetInitialChildList(kPrincipalList, childItems);
2533 }
2534
2535 // set the primary frame
2536 aDocElement->SetPrimaryFrame(contentFrame);
2537
2538 SetInitialSingleChild(mDocElementContainingBlock, newFrame);
2539
2540 return newFrame;
2541 }
2542
2543
2544 nsIFrame*
2545 nsCSSFrameConstructor::ConstructRootFrame()
2546 {
2547 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
2548
2549 nsStyleSet *styleSet = mPresShell->StyleSet();
2550
2551 // Set up our style rule observer.
2552 // XXXbz wouldn't this make more sense as part of presshell init?
2553 {
2554 styleSet->SetBindingManager(mDocument->BindingManager());
2555 }
2556
2557 // --------- BUILD VIEWPORT -----------
2558 nsIFrame* viewportFrame = nullptr;
2559 nsRefPtr<nsStyleContext> viewportPseudoStyle;
2560
2561 viewportPseudoStyle =
2562 styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::viewport, nullptr);
2563
2564 viewportFrame = NS_NewViewportFrame(mPresShell, viewportPseudoStyle);
2565
2566 // XXXbz do we _have_ to pass a null content pointer to that frame?
2567 // Would it really kill us to pass in the root element or something?
2568 // What would that break?
2569 viewportFrame->Init(nullptr, nullptr, nullptr);
2570
2571 // Bind the viewport frame to the root view
2572 nsView* rootView = mPresShell->GetViewManager()->GetRootView();
2573 viewportFrame->SetView(rootView);
2574
2575 nsContainerFrame::SyncFrameViewProperties(mPresShell->GetPresContext(), viewportFrame,
2576 viewportPseudoStyle, rootView);
2577 nsContainerFrame::SyncWindowProperties(mPresShell->GetPresContext(), viewportFrame,
2578 rootView);
2579
2580 // The viewport is the containing block for 'fixed' elements
2581 mFixedContainingBlock = viewportFrame;
2582 // Make it an absolute container for fixed-pos elements
2583 mFixedContainingBlock->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2584 mFixedContainingBlock->MarkAsAbsoluteContainingBlock();
2585
2586 return viewportFrame;
2587 }
2588
2589 void
2590 nsCSSFrameConstructor::SetUpDocElementContainingBlock(nsIContent* aDocElement)
2591 {
2592 NS_PRECONDITION(aDocElement, "No element?");
2593 NS_PRECONDITION(!aDocElement->GetParent(), "Not root content?");
2594 NS_PRECONDITION(aDocElement->GetCurrentDoc(), "Not in a document?");
2595 NS_PRECONDITION(aDocElement->GetCurrentDoc()->GetRootElement() ==
2596 aDocElement, "Not the root of the document?");
2597
2598 /*
2599 how the root frame hierarchy should look
2600
2601 Galley presentation, non-XUL, with scrolling:
2602
2603 ViewportFrame [fixed-cb]
2604 nsHTMLScrollFrame
2605 nsCanvasFrame [abs-cb]
2606 root element frame (nsBlockFrame, nsSVGOuterSVGFrame,
2607 nsTableOuterFrame, nsPlaceholderFrame)
2608
2609 Galley presentation, XUL
2610
2611 ViewportFrame [fixed-cb]
2612 nsRootBoxFrame
2613 root element frame (nsDocElementBoxFrame)
2614
2615 Print presentation, non-XUL
2616
2617 ViewportFrame
2618 nsSimplePageSequenceFrame
2619 nsPageFrame [fixed-cb]
2620 nsPageContentFrame
2621 nsCanvasFrame [abs-cb]
2622 root element frame (nsBlockFrame, nsSVGOuterSVGFrame,
2623 nsTableOuterFrame, nsPlaceholderFrame)
2624
2625 Print-preview presentation, non-XUL
2626
2627 ViewportFrame
2628 nsHTMLScrollFrame
2629 nsSimplePageSequenceFrame
2630 nsPageFrame [fixed-cb]
2631 nsPageContentFrame
2632 nsCanvasFrame [abs-cb]
2633 root element frame (nsBlockFrame, nsSVGOuterSVGFrame,
2634 nsTableOuterFrame, nsPlaceholderFrame)
2635
2636 Print/print preview of XUL is not supported.
2637 [fixed-cb]: the default containing block for fixed-pos content
2638 [abs-cb]: the default containing block for abs-pos content
2639
2640 Meaning of nsCSSFrameConstructor fields:
2641 mRootElementFrame is "root element frame". This is the primary frame for
2642 the root element.
2643 mDocElementContainingBlock is the parent of mRootElementFrame
2644 (i.e. nsCanvasFrame or nsRootBoxFrame)
2645 mFixedContainingBlock is the [fixed-cb]
2646 mGfxScrollFrame is the nsHTMLScrollFrame mentioned above, or null if there isn't one
2647 mPageSequenceFrame is the nsSimplePageSequenceFrame, or null if there isn't one
2648 */
2649
2650 // --------- CREATE ROOT FRAME -------
2651
2652
2653 // Create the root frame. The document element's frame is a child of the
2654 // root frame.
2655 //
2656 // The root frame serves two purposes:
2657 // - reserves space for any margins needed for the document element's frame
2658 // - renders the document element's background. This ensures the background covers
2659 // the entire canvas as specified by the CSS2 spec
2660
2661 nsPresContext* presContext = mPresShell->GetPresContext();
2662 bool isPaginated = presContext->IsRootPaginatedDocument();
2663 nsIFrame* viewportFrame = mFixedContainingBlock;
2664 nsStyleContext* viewportPseudoStyle = viewportFrame->StyleContext();
2665
2666 nsIFrame* rootFrame = nullptr;
2667 nsIAtom* rootPseudo;
2668
2669 if (!isPaginated) {
2670 #ifdef MOZ_XUL
2671 if (aDocElement->IsXUL())
2672 {
2673 // pass a temporary stylecontext, the correct one will be set later
2674 rootFrame = NS_NewRootBoxFrame(mPresShell, viewportPseudoStyle);
2675 } else
2676 #endif
2677 {
2678 // pass a temporary stylecontext, the correct one will be set later
2679 rootFrame = NS_NewCanvasFrame(mPresShell, viewportPseudoStyle);
2680 mHasRootAbsPosContainingBlock = true;
2681 }
2682
2683 rootPseudo = nsCSSAnonBoxes::canvas;
2684 mDocElementContainingBlock = rootFrame;
2685 } else {
2686 // Create a page sequence frame
2687 rootFrame = NS_NewSimplePageSequenceFrame(mPresShell, viewportPseudoStyle);
2688 mPageSequenceFrame = rootFrame;
2689 rootPseudo = nsCSSAnonBoxes::pageSequence;
2690 }
2691
2692
2693 // --------- IF SCROLLABLE WRAP IN SCROLLFRAME --------
2694
2695 // If the device supports scrolling (e.g., in galley mode on the screen and
2696 // for print-preview, but not when printing), then create a scroll frame that
2697 // will act as the scrolling mechanism for the viewport.
2698 // XXX Do we even need a viewport when printing to a printer?
2699
2700 bool isHTML = aDocElement->IsHTML();
2701 bool isXUL = false;
2702
2703 if (!isHTML) {
2704 isXUL = aDocElement->IsXUL();
2705 }
2706
2707 // Never create scrollbars for XUL documents
2708 bool isScrollable = isPaginated ? presContext->HasPaginatedScrolling() : !isXUL;
2709
2710 // We no longer need to do overflow propagation here. It's taken care of
2711 // when we construct frames for the element whose overflow might be
2712 // propagated
2713 NS_ASSERTION(!isScrollable || !isXUL,
2714 "XUL documents should never be scrollable - see above");
2715
2716 nsIFrame* newFrame = rootFrame;
2717 nsRefPtr<nsStyleContext> rootPseudoStyle;
2718 // we must create a state because if the scrollbars are GFX it needs the
2719 // state to build the scrollbar frames.
2720 nsFrameConstructorState state(mPresShell, nullptr, nullptr, nullptr);
2721
2722 // Start off with the viewport as parent; we'll adjust it as needed.
2723 nsIFrame* parentFrame = viewportFrame;
2724
2725 nsStyleSet* styleSet = mPresShell->StyleSet();
2726 // If paginated, make sure we don't put scrollbars in
2727 if (!isScrollable) {
2728 rootPseudoStyle = styleSet->ResolveAnonymousBoxStyle(rootPseudo,
2729 viewportPseudoStyle);
2730 } else {
2731 if (rootPseudo == nsCSSAnonBoxes::canvas) {
2732 rootPseudo = nsCSSAnonBoxes::scrolledCanvas;
2733 } else {
2734 NS_ASSERTION(rootPseudo == nsCSSAnonBoxes::pageSequence,
2735 "Unknown root pseudo");
2736 rootPseudo = nsCSSAnonBoxes::scrolledPageSequence;
2737 }
2738
2739 // Build the frame. We give it the content we are wrapping which is the
2740 // document element, the root frame, the parent view port frame, and we
2741 // should get back the new frame and the scrollable view if one was
2742 // created.
2743
2744 // resolve a context for the scrollframe
2745 nsRefPtr<nsStyleContext> styleContext;
2746 styleContext = styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::viewportScroll,
2747 viewportPseudoStyle);
2748
2749 // Note that the viewport scrollframe is always built with
2750 // overflow:auto style. This forces the scroll frame to create
2751 // anonymous content for both scrollbars. This is necessary even
2752 // if the HTML or BODY elements are overriding the viewport
2753 // scroll style to 'hidden' --- dynamic style changes might put
2754 // scrollbars back on the viewport and we don't want to have to
2755 // reframe the viewport to create the scrollbar content.
2756 newFrame = nullptr;
2757 rootPseudoStyle = BeginBuildingScrollFrame( state,
2758 aDocElement,
2759 styleContext,
2760 viewportFrame,
2761 rootPseudo,
2762 true,
2763 newFrame);
2764 parentFrame = newFrame;
2765 mGfxScrollFrame = newFrame;
2766 }
2767
2768 rootFrame->SetStyleContextWithoutNotification(rootPseudoStyle);
2769 rootFrame->Init(aDocElement, parentFrame, nullptr);
2770
2771 if (isScrollable) {
2772 FinishBuildingScrollFrame(parentFrame, rootFrame);
2773 }
2774
2775 if (isPaginated) { // paginated
2776 // Create the first page
2777 // Set the initial child lists
2778 nsIFrame* canvasFrame;
2779 nsIFrame* pageFrame =
2780 ConstructPageFrame(mPresShell, presContext, rootFrame, nullptr,
2781 canvasFrame);
2782 SetInitialSingleChild(rootFrame, pageFrame);
2783
2784 // The eventual parent of the document element frame.
2785 // XXX should this be set for every new page (in ConstructPageFrame)?
2786 mDocElementContainingBlock = canvasFrame;
2787 mHasRootAbsPosContainingBlock = true;
2788 }
2789
2790 if (viewportFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
2791 SetInitialSingleChild(viewportFrame, newFrame);
2792 } else {
2793 nsFrameList newFrameList(newFrame, newFrame);
2794 viewportFrame->AppendFrames(kPrincipalList, newFrameList);
2795 }
2796 }
2797
2798 nsIFrame*
2799 nsCSSFrameConstructor::ConstructPageFrame(nsIPresShell* aPresShell,
2800 nsPresContext* aPresContext,
2801 nsIFrame* aParentFrame,
2802 nsIFrame* aPrevPageFrame,
2803 nsIFrame*& aCanvasFrame)
2804 {
2805 nsStyleContext* parentStyleContext = aParentFrame->StyleContext();
2806 nsStyleSet *styleSet = aPresShell->StyleSet();
2807
2808 nsRefPtr<nsStyleContext> pagePseudoStyle;
2809 pagePseudoStyle = styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::page,
2810 parentStyleContext);
2811
2812 nsIFrame* pageFrame = NS_NewPageFrame(aPresShell, pagePseudoStyle);
2813
2814 // Initialize the page frame and force it to have a view. This makes printing of
2815 // the pages easier and faster.
2816 pageFrame->Init(nullptr, aParentFrame, aPrevPageFrame);
2817
2818 nsRefPtr<nsStyleContext> pageContentPseudoStyle;
2819 pageContentPseudoStyle =
2820 styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::pageContent,
2821 pagePseudoStyle);
2822
2823 nsIFrame* pageContentFrame = NS_NewPageContentFrame(aPresShell, pageContentPseudoStyle);
2824
2825 // Initialize the page content frame and force it to have a view. Also make it the
2826 // containing block for fixed elements which are repeated on every page.
2827 nsIFrame* prevPageContentFrame = nullptr;
2828 if (aPrevPageFrame) {
2829 prevPageContentFrame = aPrevPageFrame->GetFirstPrincipalChild();
2830 NS_ASSERTION(prevPageContentFrame, "missing page content frame");
2831 }
2832 pageContentFrame->Init(nullptr, pageFrame, prevPageContentFrame);
2833 SetInitialSingleChild(pageFrame, pageContentFrame);
2834 mFixedContainingBlock = pageContentFrame;
2835 // Make it an absolute container for fixed-pos elements
2836 mFixedContainingBlock->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2837 mFixedContainingBlock->MarkAsAbsoluteContainingBlock();
2838
2839 nsRefPtr<nsStyleContext> canvasPseudoStyle;
2840 canvasPseudoStyle = styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::canvas,
2841 pageContentPseudoStyle);
2842
2843 aCanvasFrame = NS_NewCanvasFrame(aPresShell, canvasPseudoStyle);
2844
2845 nsIFrame* prevCanvasFrame = nullptr;
2846 if (prevPageContentFrame) {
2847 prevCanvasFrame = prevPageContentFrame->GetFirstPrincipalChild();
2848 NS_ASSERTION(prevCanvasFrame, "missing canvas frame");
2849 }
2850 aCanvasFrame->Init(nullptr, pageContentFrame, prevCanvasFrame);
2851 SetInitialSingleChild(pageContentFrame, aCanvasFrame);
2852 return pageFrame;
2853 }
2854
2855 /* static */
2856 nsIFrame*
2857 nsCSSFrameConstructor::CreatePlaceholderFrameFor(nsIPresShell* aPresShell,
2858 nsIContent* aContent,
2859 nsIFrame* aFrame,
2860 nsStyleContext* aStyleContext,
2861 nsIFrame* aParentFrame,
2862 nsIFrame* aPrevInFlow,
2863 nsFrameState aTypeBit)
2864 {
2865 nsRefPtr<nsStyleContext> placeholderStyle = aPresShell->StyleSet()->
2866 ResolveStyleForNonElement(aStyleContext->GetParent());
2867
2868 // The placeholder frame gets a pseudo style context
2869 nsPlaceholderFrame* placeholderFrame =
2870 (nsPlaceholderFrame*)NS_NewPlaceholderFrame(aPresShell, placeholderStyle,
2871 aTypeBit);
2872
2873 placeholderFrame->Init(aContent, aParentFrame, aPrevInFlow);
2874
2875 // The placeholder frame has a pointer back to the out-of-flow frame
2876 placeholderFrame->SetOutOfFlowFrame(aFrame);
2877
2878 aFrame->AddStateBits(NS_FRAME_OUT_OF_FLOW);
2879
2880 // Add mapping from absolutely positioned frame to its placeholder frame
2881 aPresShell->FrameManager()->RegisterPlaceholderFrame(placeholderFrame);
2882
2883 return placeholderFrame;
2884 }
2885
2886 // Clears any lazy bits set in the range [aStartContent, aEndContent). If
2887 // aEndContent is null, that means to clear bits in all siblings starting with
2888 // aStartContent. aStartContent must not be null unless aEndContent is also
2889 // null. We do this so that when new children are inserted under elements whose
2890 // frame is a leaf the new children don't cause us to try to construct frames
2891 // for the existing children again.
2892 static inline void
2893 ClearLazyBits(nsIContent* aStartContent, nsIContent* aEndContent)
2894 {
2895 NS_PRECONDITION(aStartContent || !aEndContent,
2896 "Must have start child if we have an end child");
2897 for (nsIContent* cur = aStartContent; cur != aEndContent;
2898 cur = cur->GetNextSibling()) {
2899 cur->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
2900 }
2901 }
2902
2903 nsIFrame*
2904 nsCSSFrameConstructor::ConstructSelectFrame(nsFrameConstructorState& aState,
2905 FrameConstructionItem& aItem,
2906 nsIFrame* aParentFrame,
2907 const nsStyleDisplay* aStyleDisplay,
2908 nsFrameItems& aFrameItems)
2909 {
2910 nsIContent* const content = aItem.mContent;
2911 nsStyleContext* const styleContext = aItem.mStyleContext;
2912
2913 // Construct a frame-based listbox or combobox
2914 dom::HTMLSelectElement* sel = dom::HTMLSelectElement::FromContent(content);
2915 MOZ_ASSERT(sel);
2916 if (sel->IsCombobox()) {
2917 // Construct a frame-based combo box.
2918 // The frame-based combo box is built out of three parts. A display area, a button and
2919 // a dropdown list. The display area and button are created through anonymous content.
2920 // The drop-down list's frame is created explicitly. The combobox frame shares its content
2921 // with the drop-down list.
2922 nsFrameState flags = NS_BLOCK_FLOAT_MGR;
2923 nsIFrame* comboboxFrame = NS_NewComboboxControlFrame(mPresShell, styleContext, flags);
2924
2925 // Save the history state so we don't restore during construction
2926 // since the complete tree is required before we restore.
2927 nsILayoutHistoryState *historyState = aState.mFrameState;
2928 aState.mFrameState = nullptr;
2929 // Initialize the combobox frame
2930 InitAndRestoreFrame(aState, content,
2931 aState.GetGeometricParent(aStyleDisplay, aParentFrame),
2932 comboboxFrame);
2933
2934 aState.AddChild(comboboxFrame, aFrameItems, content, styleContext,
2935 aParentFrame);
2936
2937 nsIComboboxControlFrame* comboBox = do_QueryFrame(comboboxFrame);
2938 NS_ASSERTION(comboBox, "NS_NewComboboxControlFrame returned frame that "
2939 "doesn't implement nsIComboboxControlFrame");
2940
2941 // Resolve pseudo element style for the dropdown list
2942 nsRefPtr<nsStyleContext> listStyle;
2943 listStyle = mPresShell->StyleSet()->
2944 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::dropDownList, styleContext);
2945
2946 // Create a listbox
2947 nsIFrame* listFrame = NS_NewListControlFrame(mPresShell, listStyle);
2948
2949 // Notify the listbox that it is being used as a dropdown list.
2950 nsIListControlFrame * listControlFrame = do_QueryFrame(listFrame);
2951 if (listControlFrame) {
2952 listControlFrame->SetComboboxFrame(comboboxFrame);
2953 }
2954 // Notify combobox that it should use the listbox as it's popup
2955 comboBox->SetDropDown(listFrame);
2956
2957 NS_ASSERTION(!listFrame->IsPositioned(),
2958 "Ended up with positioned dropdown list somehow.");
2959 NS_ASSERTION(!listFrame->IsFloating(),
2960 "Ended up with floating dropdown list somehow.");
2961
2962 // Initialize the scroll frame positioned. Note that it is NOT
2963 // initialized as absolutely positioned.
2964 nsIFrame* scrolledFrame = NS_NewSelectsAreaFrame(mPresShell, styleContext, flags);
2965
2966 InitializeSelectFrame(aState, listFrame, scrolledFrame, content,
2967 comboboxFrame, listStyle, true,
2968 aItem.mPendingBinding, aFrameItems);
2969
2970 NS_ASSERTION(listFrame->GetView(), "ListFrame's view is nullptr");
2971
2972 // Create display and button frames from the combobox's anonymous content.
2973 // The anonymous content is appended to existing anonymous content for this
2974 // element (the scrollbars).
2975
2976 nsFrameItems childItems;
2977 CreateAnonymousFrames(aState, content, comboboxFrame,
2978 aItem.mPendingBinding, childItems);
2979
2980 comboboxFrame->SetInitialChildList(kPrincipalList, childItems);
2981
2982 // Initialize the additional popup child list which contains the
2983 // dropdown list frame.
2984 nsFrameItems popupItems;
2985 popupItems.AddChild(listFrame);
2986 comboboxFrame->SetInitialChildList(nsIFrame::kSelectPopupList,
2987 popupItems);
2988
2989 aState.mFrameState = historyState;
2990 if (aState.mFrameState) {
2991 // Restore frame state for the entire subtree of |comboboxFrame|.
2992 RestoreFrameState(comboboxFrame, aState.mFrameState);
2993 }
2994 return comboboxFrame;
2995 }
2996
2997 // Listbox, not combobox
2998 nsIFrame* listFrame = NS_NewListControlFrame(mPresShell, styleContext);
2999
3000 nsIFrame* scrolledFrame = NS_NewSelectsAreaFrame(
3001 mPresShell, styleContext, NS_BLOCK_FLOAT_MGR);
3002
3003 // ******* this code stolen from Initialze ScrollFrame ********
3004 // please adjust this code to use BuildScrollFrame.
3005
3006 InitializeSelectFrame(aState, listFrame, scrolledFrame, content,
3007 aParentFrame, styleContext, false,
3008 aItem.mPendingBinding, aFrameItems);
3009
3010 return listFrame;
3011 }
3012
3013 /**
3014 * Used to be InitializeScrollFrame but now it's only used for the select tag
3015 * But the select tag should really be fixed to use GFX scrollbars that can
3016 * be create with BuildScrollFrame.
3017 */
3018 nsresult
3019 nsCSSFrameConstructor::InitializeSelectFrame(nsFrameConstructorState& aState,
3020 nsIFrame* scrollFrame,
3021 nsIFrame* scrolledFrame,
3022 nsIContent* aContent,
3023 nsIFrame* aParentFrame,
3024 nsStyleContext* aStyleContext,
3025 bool aBuildCombobox,
3026 PendingBinding* aPendingBinding,
3027 nsFrameItems& aFrameItems)
3028 {
3029 const nsStyleDisplay* display = aStyleContext->StyleDisplay();
3030
3031 // Initialize it
3032 nsIFrame* geometricParent = aState.GetGeometricParent(display, aParentFrame);
3033
3034 // We don't call InitAndRestoreFrame for scrollFrame because we can only
3035 // restore the frame state after its parts have been created (in particular,
3036 // the scrollable view). So we have to split Init and Restore.
3037
3038 // Initialize the frame
3039 scrollFrame->Init(aContent, geometricParent, nullptr);
3040
3041 if (!aBuildCombobox) {
3042 aState.AddChild(scrollFrame, aFrameItems, aContent,
3043 aStyleContext, aParentFrame);
3044 }
3045
3046 if (aBuildCombobox) {
3047 nsContainerFrame::CreateViewForFrame(scrollFrame, true);
3048 }
3049
3050 BuildScrollFrame(aState, aContent, aStyleContext, scrolledFrame,
3051 geometricParent, scrollFrame);
3052
3053 if (aState.mFrameState) {
3054 // Restore frame state for the scroll frame
3055 RestoreFrameStateFor(scrollFrame, aState.mFrameState);
3056 }
3057
3058 // Process children
3059 nsFrameItems childItems;
3060
3061 ProcessChildren(aState, aContent, aStyleContext, scrolledFrame, false,
3062 childItems, false, aPendingBinding);
3063
3064 // Set the scrolled frame's initial child lists
3065 scrolledFrame->SetInitialChildList(kPrincipalList, childItems);
3066 return NS_OK;
3067 }
3068
3069 nsIFrame*
3070 nsCSSFrameConstructor::ConstructFieldSetFrame(nsFrameConstructorState& aState,
3071 FrameConstructionItem& aItem,
3072 nsIFrame* aParentFrame,
3073 const nsStyleDisplay* aStyleDisplay,
3074 nsFrameItems& aFrameItems)
3075 {
3076 nsIContent* const content = aItem.mContent;
3077 nsStyleContext* const styleContext = aItem.mStyleContext;
3078
3079 nsIFrame* fieldsetFrame = NS_NewFieldSetFrame(mPresShell, styleContext);
3080
3081 // Initialize it
3082 InitAndRestoreFrame(aState, content,
3083 aState.GetGeometricParent(aStyleDisplay, aParentFrame),
3084 fieldsetFrame);
3085
3086 // Resolve style and initialize the frame
3087 nsRefPtr<nsStyleContext> fieldsetContentStyle;
3088 fieldsetContentStyle = mPresShell->StyleSet()->
3089 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::fieldsetContent, styleContext);
3090
3091 const nsStyleDisplay* fieldsetContentDisplay = fieldsetContentStyle->StyleDisplay();
3092 bool isScrollable = fieldsetContentDisplay->IsScrollableOverflow();
3093 nsIFrame* scrollFrame = nullptr;
3094 if (isScrollable) {
3095 fieldsetContentStyle =
3096 BeginBuildingScrollFrame(aState, content, fieldsetContentStyle,
3097 fieldsetFrame, nsCSSAnonBoxes::scrolledContent,
3098 false, scrollFrame);
3099 }
3100
3101 nsIFrame* blockFrame = NS_NewBlockFrame(mPresShell, fieldsetContentStyle,
3102 NS_BLOCK_FLOAT_MGR |
3103 NS_BLOCK_MARGIN_ROOT);
3104 InitAndRestoreFrame(aState, content,
3105 scrollFrame ? scrollFrame : fieldsetFrame, blockFrame);
3106
3107 aState.AddChild(fieldsetFrame, aFrameItems, content, styleContext, aParentFrame);
3108
3109 // Process children
3110 nsFrameConstructorSaveState absoluteSaveState;
3111 nsFrameItems childItems;
3112
3113 blockFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
3114 if (fieldsetFrame->IsPositioned()) {
3115 aState.PushAbsoluteContainingBlock(blockFrame, fieldsetFrame, absoluteSaveState);
3116 }
3117
3118 ProcessChildren(aState, content, styleContext, blockFrame, true,
3119 childItems, true, aItem.mPendingBinding);
3120
3121 nsFrameItems fieldsetKids;
3122 fieldsetKids.AddChild(scrollFrame ? scrollFrame : blockFrame);
3123
3124 for (nsFrameList::Enumerator e(childItems); !e.AtEnd(); e.Next()) {
3125 nsIFrame* child = e.get();
3126 if (child->GetContentInsertionFrame()->GetType() == nsGkAtoms::legendFrame) {
3127 // We want the legend to be the first frame in the fieldset child list.
3128 // That way the EventStateManager will do the right thing when tabbing
3129 // from a selection point within the legend (bug 236071), which is
3130 // used for implementing legend access keys (bug 81481).
3131 // GetAdjustedParentFrame() below depends on this frame order.
3132 childItems.RemoveFrame(child);
3133 // Make sure to reparent the legend so it has the fieldset as the parent.
3134 fieldsetKids.InsertFrame(fieldsetFrame, nullptr, child);
3135 if (scrollFrame) {
3136 StickyScrollContainer::NotifyReparentedFrameAcrossScrollFrameBoundary(
3137 child, blockFrame);
3138 }
3139 break;
3140 }
3141 }
3142
3143 if (isScrollable) {
3144 FinishBuildingScrollFrame(scrollFrame, blockFrame);
3145 }
3146
3147 // Set the inner frame's initial child lists
3148 blockFrame->SetInitialChildList(kPrincipalList, childItems);
3149
3150 // Set the outer frame's initial child list
3151 fieldsetFrame->SetInitialChildList(kPrincipalList, fieldsetKids);
3152
3153 fieldsetFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT);
3154
3155 // Our new frame returned is the outer frame, which is the fieldset frame.
3156 return fieldsetFrame;
3157 }
3158
3159 static nsIFrame*
3160 FindAncestorWithGeneratedContentPseudo(nsIFrame* aFrame)
3161 {
3162 for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) {
3163 NS_ASSERTION(f->IsGeneratedContentFrame(),
3164 "should not have exited generated content");
3165 nsIAtom* pseudo = f->StyleContext()->GetPseudo();
3166 if (pseudo == nsCSSPseudoElements::before ||
3167 pseudo == nsCSSPseudoElements::after)
3168 return f;
3169 }
3170 return nullptr;
3171 }
3172
3173 #define SIMPLE_FCDATA(_func) FCDATA_DECL(0, _func)
3174 #define FULL_CTOR_FCDATA(_flags, _func) \
3175 { _flags | FCDATA_FUNC_IS_FULL_CTOR, { nullptr }, _func, nullptr }
3176
3177 /* static */
3178 const nsCSSFrameConstructor::FrameConstructionData*
3179 nsCSSFrameConstructor::FindTextData(nsIFrame* aParentFrame)
3180 {
3181 if (aParentFrame && IsFrameForSVG(aParentFrame)) {
3182 nsIFrame *ancestorFrame =
3183 nsSVGUtils::GetFirstNonAAncestorFrame(aParentFrame);
3184 if (ancestorFrame) {
3185 static const FrameConstructionData sSVGTextData =
3186 FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT | FCDATA_IS_SVG_TEXT,
3187 NS_NewTextFrame);
3188 if (ancestorFrame->IsSVGText()) {
3189 return &sSVGTextData;
3190 }
3191 }
3192 return nullptr;
3193 }
3194
3195 static const FrameConstructionData sTextData =
3196 FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT, NS_NewTextFrame);
3197 return &sTextData;
3198 }
3199
3200 void
3201 nsCSSFrameConstructor::ConstructTextFrame(const FrameConstructionData* aData,
3202 nsFrameConstructorState& aState,
3203 nsIContent* aContent,
3204 nsIFrame* aParentFrame,
3205 nsStyleContext* aStyleContext,
3206 nsFrameItems& aFrameItems)
3207 {
3208 NS_PRECONDITION(aData, "Must have frame construction data");
3209
3210 nsIFrame* newFrame = (*aData->mFunc.mCreationFunc)(mPresShell, aStyleContext);
3211
3212 InitAndRestoreFrame(aState, aContent, aParentFrame, newFrame);
3213
3214 // We never need to create a view for a text frame.
3215
3216 if (newFrame->IsGeneratedContentFrame()) {
3217 nsAutoPtr<nsGenConInitializer> initializer;
3218 initializer =
3219 static_cast<nsGenConInitializer*>(
3220 aContent->UnsetProperty(nsGkAtoms::genConInitializerProperty));
3221 if (initializer) {
3222 if (initializer->mNode->InitTextFrame(initializer->mList,
3223 FindAncestorWithGeneratedContentPseudo(newFrame), newFrame)) {
3224 (this->*(initializer->mDirtyAll))();
3225 }
3226 initializer->mNode.forget();
3227 }
3228 }
3229
3230 // Add the newly constructed frame to the flow
3231 aFrameItems.AddChild(newFrame);
3232
3233 if (!aState.mCreatingExtraFrames)
3234 aContent->SetPrimaryFrame(newFrame);
3235 }
3236
3237 /* static */
3238 const nsCSSFrameConstructor::FrameConstructionData*
3239 nsCSSFrameConstructor::FindDataByInt(int32_t aInt,
3240 Element* aElement,
3241 nsStyleContext* aStyleContext,
3242 const FrameConstructionDataByInt* aDataPtr,
3243 uint32_t aDataLength)
3244 {
3245 for (const FrameConstructionDataByInt *curData = aDataPtr,
3246 *endData = aDataPtr + aDataLength;
3247 curData != endData;
3248 ++curData) {
3249 if (curData->mInt == aInt) {
3250 const FrameConstructionData* data = &curData->mData;
3251 if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) {
3252 return data->mFunc.mDataGetter(aElement, aStyleContext);
3253 }
3254
3255 return data;
3256 }
3257 }
3258
3259 return nullptr;
3260 }
3261
3262 /* static */
3263 const nsCSSFrameConstructor::FrameConstructionData*
3264 nsCSSFrameConstructor::FindDataByTag(nsIAtom* aTag,
3265 Element* aElement,
3266 nsStyleContext* aStyleContext,
3267 const FrameConstructionDataByTag* aDataPtr,
3268 uint32_t aDataLength)
3269 {
3270 for (const FrameConstructionDataByTag *curData = aDataPtr,
3271 *endData = aDataPtr + aDataLength;
3272 curData != endData;
3273 ++curData) {
3274 if (*curData->mTag == aTag) {
3275 const FrameConstructionData* data = &curData->mData;
3276 if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) {
3277 return data->mFunc.mDataGetter(aElement, aStyleContext);
3278 }
3279
3280 return data;
3281 }
3282 }
3283
3284 return nullptr;
3285 }
3286
3287 #define SUPPRESS_FCDATA() FCDATA_DECL(FCDATA_SUPPRESS_FRAME, nullptr)
3288 #define SIMPLE_INT_CREATE(_int, _func) { _int, SIMPLE_FCDATA(_func) }
3289 #define SIMPLE_INT_CHAIN(_int, _func) \
3290 { _int, FCDATA_DECL(FCDATA_FUNC_IS_DATA_GETTER, _func) }
3291 #define COMPLEX_INT_CREATE(_int, _func) \
3292 { _int, FULL_CTOR_FCDATA(0, _func) }
3293
3294 #define SIMPLE_TAG_CREATE(_tag, _func) \
3295 { &nsGkAtoms::_tag, SIMPLE_FCDATA(_func) }
3296 #define SIMPLE_TAG_CHAIN(_tag, _func) \
3297 { &nsGkAtoms::_tag, FCDATA_DECL(FCDATA_FUNC_IS_DATA_GETTER, _func) }
3298 #define COMPLEX_TAG_CREATE(_tag, _func) \
3299 { &nsGkAtoms::_tag, FULL_CTOR_FCDATA(0, _func) }
3300
3301 static bool
3302 IsFrameForFieldSet(nsIFrame* aFrame, nsIAtom* aFrameType)
3303 {
3304 nsIAtom* pseudo = aFrame->StyleContext()->GetPseudo();
3305 if (pseudo == nsCSSAnonBoxes::fieldsetContent ||
3306 pseudo == nsCSSAnonBoxes::scrolledContent) {
3307 return IsFrameForFieldSet(aFrame->GetParent(), aFrame->GetParent()->GetType());
3308 }
3309 return aFrameType == nsGkAtoms::fieldSetFrame;
3310 }
3311
3312 /* static */
3313 const nsCSSFrameConstructor::FrameConstructionData*
3314 nsCSSFrameConstructor::FindHTMLData(Element* aElement,
3315 nsIAtom* aTag,
3316 int32_t aNameSpaceID,
3317 nsIFrame* aParentFrame,
3318 nsStyleContext* aStyleContext)
3319 {
3320 // Ignore the tag if it's not HTML content and if it doesn't extend (via XBL)
3321 // a valid HTML namespace. This check must match the one in
3322 // ShouldHaveFirstLineStyle.
3323 if (aNameSpaceID != kNameSpaceID_XHTML) {
3324 return nullptr;
3325 }
3326
3327 NS_ASSERTION(!aParentFrame ||
3328 aParentFrame->StyleContext()->GetPseudo() !=
3329 nsCSSAnonBoxes::fieldsetContent ||
3330 aParentFrame->GetParent()->GetType() == nsGkAtoms::fieldSetFrame,
3331 "Unexpected parent for fieldset content anon box");
3332 if (aTag == nsGkAtoms::legend &&
3333 (!aParentFrame ||
3334 !IsFrameForFieldSet(aParentFrame, aParentFrame->GetType()) ||
3335 !aElement->GetParent() ||
3336 !aElement->GetParent()->IsHTML(nsGkAtoms::fieldset) ||
3337 aStyleContext->StyleDisplay()->IsFloatingStyle() ||
3338 aStyleContext->StyleDisplay()->IsAbsolutelyPositionedStyle())) {
3339 // <legend> is only special inside fieldset, check both the frame tree
3340 // parent and content tree parent due to XBL issues. For floated or
3341 // absolutely positioned legends we want to construct by display type and
3342 // not do special legend stuff.
3343 // XXXbz it would be nice if we could just decide this based on the parent
3344 // tag, and hence just use a SIMPLE_TAG_CHAIN for legend below, but the
3345 // fact that with XBL we could end up with this legend element in some
3346 // totally weird insertion point makes that chancy, I think.
3347 return nullptr;
3348 }
3349
3350 static const FrameConstructionDataByTag sHTMLData[] = {
3351 SIMPLE_TAG_CHAIN(img, nsCSSFrameConstructor::FindImgData),
3352 SIMPLE_TAG_CHAIN(mozgeneratedcontentimage,
3353 nsCSSFrameConstructor::FindImgData),
3354 { &nsGkAtoms::br,
3355 FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT | FCDATA_IS_LINE_BREAK,
3356 NS_NewBRFrame) },
3357 SIMPLE_TAG_CREATE(wbr, NS_NewWBRFrame),
3358 SIMPLE_TAG_CHAIN(input, nsCSSFrameConstructor::FindInputData),
3359 SIMPLE_TAG_CREATE(textarea, NS_NewTextControlFrame),
3360 COMPLEX_TAG_CREATE(select, &nsCSSFrameConstructor::ConstructSelectFrame),
3361 SIMPLE_TAG_CHAIN(object, nsCSSFrameConstructor::FindObjectData),
3362 SIMPLE_TAG_CHAIN(applet, nsCSSFrameConstructor::FindObjectData),
3363 SIMPLE_TAG_CHAIN(embed, nsCSSFrameConstructor::FindObjectData),
3364 COMPLEX_TAG_CREATE(fieldset,
3365 &nsCSSFrameConstructor::ConstructFieldSetFrame),
3366 { &nsGkAtoms::legend,
3367 FCDATA_DECL(FCDATA_ALLOW_BLOCK_STYLES | FCDATA_MAY_NEED_SCROLLFRAME,
3368 NS_NewLegendFrame) },
3369 SIMPLE_TAG_CREATE(frameset, NS_NewHTMLFramesetFrame),
3370 SIMPLE_TAG_CREATE(iframe, NS_NewSubDocumentFrame),
3371 { &nsGkAtoms::button,
3372 FCDATA_WITH_WRAPPING_BLOCK(FCDATA_ALLOW_BLOCK_STYLES,
3373 NS_NewHTMLButtonControlFrame,
3374 nsCSSAnonBoxes::buttonContent) },
3375 SIMPLE_TAG_CHAIN(canvas, nsCSSFrameConstructor::FindCanvasData),
3376 SIMPLE_TAG_CREATE(video, NS_NewHTMLVideoFrame),
3377 SIMPLE_TAG_CREATE(audio, NS_NewHTMLVideoFrame),
3378 SIMPLE_TAG_CREATE(progress, NS_NewProgressFrame),
3379 SIMPLE_TAG_CREATE(meter, NS_NewMeterFrame)
3380 };
3381
3382 return FindDataByTag(aTag, aElement, aStyleContext, sHTMLData,
3383 ArrayLength(sHTMLData));
3384 }
3385
3386 /* static */
3387 const nsCSSFrameConstructor::FrameConstructionData*
3388 nsCSSFrameConstructor::FindImgData(Element* aElement,
3389 nsStyleContext* aStyleContext)
3390 {
3391 if (!nsImageFrame::ShouldCreateImageFrameFor(aElement, aStyleContext)) {
3392 return nullptr;
3393 }
3394
3395 static const FrameConstructionData sImgData = SIMPLE_FCDATA(NS_NewImageFrame);
3396 return &sImgData;
3397 }
3398
3399 /* static */
3400 const nsCSSFrameConstructor::FrameConstructionData*
3401 nsCSSFrameConstructor::FindImgControlData(Element* aElement,
3402 nsStyleContext* aStyleContext)
3403 {
3404 if (!nsImageFrame::ShouldCreateImageFrameFor(aElement, aStyleContext)) {
3405 return nullptr;
3406 }
3407
3408 static const FrameConstructionData sImgControlData =
3409 SIMPLE_FCDATA(NS_NewImageControlFrame);
3410 return &sImgControlData;
3411 }
3412
3413 /* static */
3414 const nsCSSFrameConstructor::FrameConstructionData*
3415 nsCSSFrameConstructor::FindInputData(Element* aElement,
3416 nsStyleContext* aStyleContext)
3417 {
3418 static const FrameConstructionDataByInt sInputData[] = {
3419 SIMPLE_INT_CREATE(NS_FORM_INPUT_CHECKBOX, NS_NewGfxCheckboxControlFrame),
3420 SIMPLE_INT_CREATE(NS_FORM_INPUT_RADIO, NS_NewGfxRadioControlFrame),
3421 SIMPLE_INT_CREATE(NS_FORM_INPUT_FILE, NS_NewFileControlFrame),
3422 SIMPLE_INT_CHAIN(NS_FORM_INPUT_IMAGE,
3423 nsCSSFrameConstructor::FindImgControlData),
3424 SIMPLE_INT_CREATE(NS_FORM_INPUT_EMAIL, NS_NewTextControlFrame),
3425 SIMPLE_INT_CREATE(NS_FORM_INPUT_SEARCH, NS_NewTextControlFrame),
3426 SIMPLE_INT_CREATE(NS_FORM_INPUT_TEXT, NS_NewTextControlFrame),
3427 SIMPLE_INT_CREATE(NS_FORM_INPUT_TEL, NS_NewTextControlFrame),
3428 SIMPLE_INT_CREATE(NS_FORM_INPUT_URL, NS_NewTextControlFrame),
3429 SIMPLE_INT_CREATE(NS_FORM_INPUT_RANGE, NS_NewRangeFrame),
3430 SIMPLE_INT_CREATE(NS_FORM_INPUT_PASSWORD, NS_NewTextControlFrame),
3431 { NS_FORM_INPUT_COLOR,
3432 FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewColorControlFrame,
3433 nsCSSAnonBoxes::buttonContent) },
3434 // TODO: this is temporary until a frame is written: bug 635240.
3435 SIMPLE_INT_CREATE(NS_FORM_INPUT_NUMBER, NS_NewNumberControlFrame),
3436 // TODO: this is temporary until a frame is written: bug 773205.
3437 SIMPLE_INT_CREATE(NS_FORM_INPUT_DATE, NS_NewTextControlFrame),
3438 // TODO: this is temporary until a frame is written: bug 773205
3439 SIMPLE_INT_CREATE(NS_FORM_INPUT_TIME, NS_NewTextControlFrame),
3440 { NS_FORM_INPUT_SUBMIT,
3441 FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame,
3442 nsCSSAnonBoxes::buttonContent) },
3443 { NS_FORM_INPUT_RESET,
3444 FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame,
3445 nsCSSAnonBoxes::buttonContent) },
3446 { NS_FORM_INPUT_BUTTON,
3447 FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame,
3448 nsCSSAnonBoxes::buttonContent) }
3449 // Keeping hidden inputs out of here on purpose for so they get frames by
3450 // display (in practice, none).
3451 };
3452
3453 nsCOMPtr<nsIFormControl> control = do_QueryInterface(aElement);
3454 NS_ASSERTION(control, "input doesn't implement nsIFormControl?");
3455
3456 return FindDataByInt(control->GetType(), aElement, aStyleContext,
3457 sInputData, ArrayLength(sInputData));
3458 }
3459
3460 /* static */
3461 const nsCSSFrameConstructor::FrameConstructionData*
3462 nsCSSFrameConstructor::FindObjectData(Element* aElement,
3463 nsStyleContext* aStyleContext)
3464 {
3465 // GetDisplayedType isn't necessarily nsIObjectLoadingContent::TYPE_NULL for
3466 // cases when the object is broken/suppressed/etc (e.g. a broken image), but
3467 // we want to treat those cases as TYPE_NULL
3468 uint32_t type;
3469 if (aElement->State().HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
3470 NS_EVENT_STATE_USERDISABLED |
3471 NS_EVENT_STATE_SUPPRESSED)) {
3472 type = nsIObjectLoadingContent::TYPE_NULL;
3473 } else {
3474 nsCOMPtr<nsIObjectLoadingContent> objContent(do_QueryInterface(aElement));
3475 NS_ASSERTION(objContent,
3476 "applet, embed and object must implement "
3477 "nsIObjectLoadingContent!");
3478
3479 objContent->GetDisplayedType(&type);
3480 }
3481
3482 static const FrameConstructionDataByInt sObjectData[] = {
3483 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_LOADING,
3484 NS_NewEmptyFrame),
3485 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_PLUGIN,
3486 NS_NewObjectFrame),
3487 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_IMAGE,
3488 NS_NewImageFrame),
3489 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_DOCUMENT,
3490 NS_NewSubDocumentFrame)
3491 // Nothing for TYPE_NULL so we'll construct frames by display there
3492 };
3493
3494 return FindDataByInt((int32_t)type, aElement, aStyleContext,
3495 sObjectData, ArrayLength(sObjectData));
3496 }
3497
3498 /* static */
3499 const nsCSSFrameConstructor::FrameConstructionData*
3500 nsCSSFrameConstructor::FindCanvasData(Element* aElement,
3501 nsStyleContext* aStyleContext)
3502 {
3503 // We want to check whether script is enabled on the document that
3504 // could be painting to the canvas. That's the owner document of
3505 // the canvas, except when the owner document is a static document,
3506 // in which case it's the original document it was cloned from.
3507 nsIDocument* doc = aElement->OwnerDoc();
3508 if (doc->IsStaticDocument()) {
3509 doc = doc->GetOriginalDocument();
3510 }
3511 if (!doc->IsScriptEnabled()) {
3512 return nullptr;
3513 }
3514
3515 static const FrameConstructionData sCanvasData =
3516 FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewHTMLCanvasFrame,
3517 nsCSSAnonBoxes::htmlCanvasContent);
3518 return &sCanvasData;
3519 }
3520
3521 void
3522 nsCSSFrameConstructor::ConstructFrameFromItemInternal(FrameConstructionItem& aItem,
3523 nsFrameConstructorState& aState,
3524 nsIFrame* aParentFrame,
3525 nsFrameItems& aFrameItems)
3526 {
3527 const FrameConstructionData* data = aItem.mFCData;
3528 NS_ASSERTION(data, "Must have frame construction data");
3529
3530 uint32_t bits = data->mBits;
3531
3532 NS_ASSERTION(!(bits & FCDATA_FUNC_IS_DATA_GETTER),
3533 "Should have dealt with this inside the data finder");
3534
3535 // Some sets of bits are not compatible with each other
3536 #define CHECK_ONLY_ONE_BIT(_bit1, _bit2) \
3537 NS_ASSERTION(!(bits & _bit1) || !(bits & _bit2), \
3538 "Only one of these bits should be set")
3539 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_FORCE_NULL_ABSPOS_CONTAINER);
3540 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_WRAP_KIDS_IN_BLOCKS);
3541 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_MAY_NEED_SCROLLFRAME);
3542 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_IS_POPUP);
3543 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_SKIP_ABSPOS_PUSH);
3544 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
3545 FCDATA_DISALLOW_GENERATED_CONTENT);
3546 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_ALLOW_BLOCK_STYLES);
3547 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
3548 FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS);
3549 CHECK_ONLY_ONE_BIT(FCDATA_WRAP_KIDS_IN_BLOCKS,
3550 FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS);
3551 #undef CHECK_ONLY_ONE_BIT
3552 NS_ASSERTION(!(bits & FCDATA_FORCED_NON_SCROLLABLE_BLOCK) ||
3553 ((bits & FCDATA_FUNC_IS_FULL_CTOR) &&
3554 data->mFullConstructor ==
3555 &nsCSSFrameConstructor::ConstructNonScrollableBlock),
3556 "Unexpected FCDATA_FORCED_NON_SCROLLABLE_BLOCK flag");
3557
3558 // Don't create a subdocument frame for iframes if we're creating extra frames
3559 if (aState.mCreatingExtraFrames && aItem.mContent->IsHTML() &&
3560 aItem.mContent->Tag() == nsGkAtoms::iframe)
3561 {
3562 return;
3563 }
3564
3565 nsStyleContext* const styleContext = aItem.mStyleContext;
3566 const nsStyleDisplay* display = styleContext->StyleDisplay();
3567 nsIContent* const content = aItem.mContent;
3568
3569 // Get the parent of the content and check if it is a XBL children element.
3570 // Push the children element as an ancestor here because it does
3571 // not have a frame and would not otherwise be pushed as an ancestor. It is
3572 // necessary to do so in order to correctly handle style resolution on
3573 // descendants.
3574 nsIContent* parent = content->GetParent();
3575 TreeMatchContext::AutoAncestorPusher
3576 insertionPointPusher(aState.mTreeMatchContext);
3577 if (parent && nsContentUtils::IsContentInsertionPoint(parent)) {
3578 if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
3579 insertionPointPusher.PushAncestorAndStyleScope(parent);
3580 } else {
3581 insertionPointPusher.PushStyleScope(parent);
3582 }
3583 }
3584
3585 // Push the content as a style ancestor now, so we don't have to do
3586 // it in our various full-constructor functions. In particular,
3587 // since a number of full-constructor functions don't actually call
3588 // ProcessChildren in some cases (e.g. for CSS anonymous table boxes
3589 // or for situations where only anonymouse children are having
3590 // frames constructed), this is the best place to bottleneck the
3591 // pushing of the content instead of having to do it in multiple
3592 // places.
3593 TreeMatchContext::AutoAncestorPusher
3594 ancestorPusher(aState.mTreeMatchContext);
3595 if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
3596 ancestorPusher.PushAncestorAndStyleScope(content);
3597 } else {
3598 ancestorPusher.PushStyleScope(content);
3599 }
3600
3601 nsIFrame* newFrame;
3602 nsIFrame* primaryFrame;
3603 if (bits & FCDATA_FUNC_IS_FULL_CTOR) {
3604 newFrame =
3605 (this->*(data->mFullConstructor))(aState, aItem, aParentFrame,
3606 display, aFrameItems);
3607 MOZ_ASSERT(newFrame, "Full constructor failed");
3608 primaryFrame = newFrame;
3609 } else {
3610 newFrame =
3611 (*data->mFunc.mCreationFunc)(mPresShell, styleContext);
3612
3613 bool allowOutOfFlow = !(bits & FCDATA_DISALLOW_OUT_OF_FLOW);
3614 bool isPopup = aItem.mIsPopup;
3615 NS_ASSERTION(!isPopup ||
3616 (aState.mPopupItems.containingBlock &&
3617 aState.mPopupItems.containingBlock->GetType() ==
3618 nsGkAtoms::popupSetFrame),
3619 "Should have a containing block here!");
3620
3621 nsIFrame* geometricParent =
3622 isPopup ? aState.mPopupItems.containingBlock :
3623 (allowOutOfFlow ? aState.GetGeometricParent(display, aParentFrame)
3624 : aParentFrame);
3625
3626 // Must init frameToAddToList to null, since it's inout
3627 nsIFrame* frameToAddToList = nullptr;
3628 if ((bits & FCDATA_MAY_NEED_SCROLLFRAME) &&
3629 display->IsScrollableOverflow()) {
3630 BuildScrollFrame(aState, content, styleContext, newFrame,
3631 geometricParent, frameToAddToList);
3632 } else {
3633 InitAndRestoreFrame(aState, content, geometricParent, newFrame);
3634 // See whether we need to create a view
3635 nsContainerFrame::CreateViewForFrame(newFrame, false);
3636 frameToAddToList = newFrame;
3637 }
3638
3639 // Use frameToAddToList as the primary frame. In the non-scrollframe case
3640 // they're equal, but in the scrollframe case newFrame is the scrolled
3641 // frame, while frameToAddToList is the scrollframe (and should be the
3642 // primary frame).
3643 primaryFrame = frameToAddToList;
3644
3645 // If we need to create a block formatting context to wrap our
3646 // kids, do it now.
3647 const nsStyleDisplay* maybeAbsoluteContainingBlockDisplay = display;
3648 nsIFrame* maybeAbsoluteContainingBlock = newFrame;
3649 nsIFrame* possiblyLeafFrame = newFrame;
3650 if (bits & FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS) {
3651 nsRefPtr<nsStyleContext> blockContext;
3652 blockContext =
3653 mPresShell->StyleSet()->ResolveAnonymousBoxStyle(*data->mAnonBoxPseudo,
3654 styleContext);
3655 nsIFrame* blockFrame =
3656 NS_NewBlockFormattingContext(mPresShell, blockContext);
3657
3658 InitAndRestoreFrame(aState, content, newFrame, blockFrame);
3659
3660 SetInitialSingleChild(newFrame, blockFrame);
3661
3662 // Now figure out whether newFrame or blockFrame should be the
3663 // absolute container. It should be the latter if it's
3664 // positioned, otherwise the former.
3665 const nsStyleDisplay* blockDisplay = blockContext->StyleDisplay();
3666 if (blockDisplay->IsPositioned(blockFrame)) {
3667 maybeAbsoluteContainingBlockDisplay = blockDisplay;
3668 maybeAbsoluteContainingBlock = blockFrame;
3669 }
3670
3671 // Our kids should go into the blockFrame
3672 newFrame = blockFrame;
3673 }
3674
3675 aState.AddChild(frameToAddToList, aFrameItems, content, styleContext,
3676 aParentFrame, allowOutOfFlow, allowOutOfFlow, isPopup);
3677
3678 #ifdef MOZ_XUL
3679 // Icky XUL stuff, sadly
3680
3681 if (aItem.mIsRootPopupgroup) {
3682 NS_ASSERTION(nsIRootBox::GetRootBox(mPresShell) &&
3683 nsIRootBox::GetRootBox(mPresShell)->GetPopupSetFrame() ==
3684 newFrame,
3685 "Unexpected PopupSetFrame");
3686 aState.mPopupItems.containingBlock = newFrame;
3687 aState.mHavePendingPopupgroup = false;
3688 }
3689 #endif /* MOZ_XUL */
3690
3691 // Process the child content if requested
3692 nsFrameItems childItems;
3693 nsFrameConstructorSaveState absoluteSaveState;
3694
3695 if (bits & FCDATA_FORCE_NULL_ABSPOS_CONTAINER) {
3696 aState.PushAbsoluteContainingBlock(nullptr, nullptr, absoluteSaveState);
3697 } else if (!(bits & FCDATA_SKIP_ABSPOS_PUSH)) {
3698 nsIFrame* cb = maybeAbsoluteContainingBlock;
3699 cb->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
3700 // This check is identical to nsStyleDisplay::IsPositioned except without
3701 // the assertion that the style display and frame match. When constructing
3702 // scroll frames we intentionally use the style display for the outer, but
3703 // make the inner the containing block.
3704 if ((maybeAbsoluteContainingBlockDisplay->IsAbsolutelyPositionedStyle() ||
3705 maybeAbsoluteContainingBlockDisplay->IsRelativelyPositionedStyle() ||
3706 (maybeAbsoluteContainingBlockDisplay->HasTransformStyle() &&
3707 cb->IsFrameOfType(nsIFrame::eSupportsCSSTransforms)) ||
3708 maybeAbsoluteContainingBlockDisplay->HasPerspectiveStyle()) &&
3709 !cb->IsSVGText()) {
3710 aState.PushAbsoluteContainingBlock(cb, cb, absoluteSaveState);
3711 }
3712 }
3713
3714 if (!aItem.mAnonChildren.IsEmpty()) {
3715 NS_ASSERTION(!(bits & FCDATA_USE_CHILD_ITEMS),
3716 "We should not have both anonymous and non-anonymous "
3717 "children in a given FrameConstructorItem");
3718 AddFCItemsForAnonymousContent(aState, newFrame, aItem.mAnonChildren,
3719 aItem.mChildItems);
3720 bits |= FCDATA_USE_CHILD_ITEMS;
3721 }
3722
3723 if (bits & FCDATA_USE_CHILD_ITEMS) {
3724 nsFrameConstructorSaveState floatSaveState;
3725
3726 if (ShouldSuppressFloatingOfDescendants(newFrame)) {
3727 aState.PushFloatContainingBlock(nullptr, floatSaveState);
3728 } else if (newFrame->IsFloatContainingBlock()) {
3729 aState.PushFloatContainingBlock(newFrame, floatSaveState);
3730 }
3731 ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame,
3732 childItems);
3733 } else {
3734 // Process the child frames.
3735 ProcessChildren(aState, content, styleContext, newFrame,
3736 !(bits & FCDATA_DISALLOW_GENERATED_CONTENT),
3737 childItems,
3738 (bits & FCDATA_ALLOW_BLOCK_STYLES) != 0,
3739 aItem.mPendingBinding, possiblyLeafFrame);
3740 }
3741
3742 #ifdef MOZ_XUL
3743 // More icky XUL stuff
3744 if (aItem.mNameSpaceID == kNameSpaceID_XUL &&
3745 (aItem.mTag == nsGkAtoms::treechildren || // trees always need titletips
3746 content->HasAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext) ||
3747 content->HasAttr(kNameSpaceID_None, nsGkAtoms::tooltip))) {
3748 nsIRootBox* rootBox = nsIRootBox::GetRootBox(mPresShell);
3749 if (rootBox) {
3750 rootBox->AddTooltipSupport(content);
3751 }
3752 }
3753 #endif
3754
3755 if (bits & FCDATA_WRAP_KIDS_IN_BLOCKS) {
3756 nsFrameItems newItems;
3757 nsFrameItems currentBlockItems;
3758 nsIFrame* f;
3759 while ((f = childItems.FirstChild()) != nullptr) {
3760 bool wrapFrame = IsInlineFrame(f) || IsFramePartOfIBSplit(f);
3761 if (!wrapFrame) {
3762 FlushAccumulatedBlock(aState, content, newFrame,
3763 currentBlockItems, newItems);
3764 }
3765
3766 childItems.RemoveFrame(f);
3767 if (wrapFrame) {
3768 currentBlockItems.AddChild(f);
3769 } else {
3770 newItems.AddChild(f);
3771 }
3772 }
3773 FlushAccumulatedBlock(aState, content, newFrame,
3774 currentBlockItems, newItems);
3775
3776 if (childItems.NotEmpty()) {
3777 // an error must have occurred, delete unprocessed frames
3778 childItems.DestroyFrames();
3779 }
3780
3781 childItems = newItems;
3782 }
3783
3784 // Set the frame's initial child list
3785 // Note that MathML depends on this being called even if
3786 // childItems is empty!
3787 newFrame->SetInitialChildList(kPrincipalList, childItems);
3788 }
3789
3790 NS_ASSERTION(newFrame->IsFrameOfType(nsIFrame::eLineParticipant) ==
3791 ((bits & FCDATA_IS_LINE_PARTICIPANT) != 0),
3792 "Incorrectly set FCDATA_IS_LINE_PARTICIPANT bits");
3793
3794 if (aItem.mIsAnonymousContentCreatorContent) {
3795 primaryFrame->AddStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT);
3796 }
3797
3798 // Even if mCreatingExtraFrames is set, we may need to SetPrimaryFrame for
3799 // generated content that doesn't have one yet. Note that we have to examine
3800 // the frame bit, because by this point mIsGeneratedContent has been cleared
3801 // on aItem.
3802 if ((!aState.mCreatingExtraFrames ||
3803 ((primaryFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT) &&
3804 !aItem.mContent->GetPrimaryFrame())) &&
3805 !(bits & FCDATA_SKIP_FRAMESET)) {
3806 aItem.mContent->SetPrimaryFrame(primaryFrame);
3807 }
3808 }
3809
3810 // after the node has been constructed and initialized create any
3811 // anonymous content a node needs.
3812 nsresult
3813 nsCSSFrameConstructor::CreateAnonymousFrames(nsFrameConstructorState& aState,
3814 nsIContent* aParent,
3815 nsIFrame* aParentFrame,
3816 PendingBinding* aPendingBinding,
3817 nsFrameItems& aChildItems)
3818 {
3819 nsAutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> newAnonymousItems;
3820 nsresult rv = GetAnonymousContent(aParent, aParentFrame, newAnonymousItems);
3821 NS_ENSURE_SUCCESS(rv, rv);
3822
3823 uint32_t count = newAnonymousItems.Length();
3824 if (count == 0) {
3825 return NS_OK;
3826 }
3827
3828 nsFrameConstructorState::PendingBindingAutoPusher pusher(aState,
3829 aPendingBinding);
3830 TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext);
3831 if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
3832 ancestorPusher.PushAncestorAndStyleScope(aParent->AsElement());
3833 } else {
3834 ancestorPusher.PushStyleScope(aParent->AsElement());
3835 }
3836
3837 nsIAnonymousContentCreator* creator = do_QueryFrame(aParentFrame);
3838 NS_ASSERTION(creator,
3839 "How can that happen if we have nodes to construct frames for?");
3840
3841 for (uint32_t i=0; i < count; i++) {
3842 nsIContent* content = newAnonymousItems[i].mContent;
3843 NS_ASSERTION(content, "null anonymous content?");
3844 NS_ASSERTION(!newAnonymousItems[i].mStyleContext, "Unexpected style context");
3845 NS_ASSERTION(newAnonymousItems[i].mChildren.IsEmpty(),
3846 "This method is not currently used with frames that implement "
3847 "nsIAnonymousContentCreator::CreateAnonymousContent to "
3848 "output a list where the items have their own children");
3849
3850 nsIFrame* newFrame = creator->CreateFrameFor(content);
3851 if (newFrame) {
3852 NS_ASSERTION(content->GetPrimaryFrame(),
3853 "Content must have a primary frame now");
3854 newFrame->AddStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT);
3855 aChildItems.AddChild(newFrame);
3856 } else {
3857 FrameConstructionItemList items;
3858 {
3859 // Skip flex item style-fixup during our AddFrameConstructionItems() call:
3860 TreeMatchContext::AutoFlexItemStyleFixupSkipper
3861 flexItemStyleFixupSkipper(aState.mTreeMatchContext);
3862
3863 AddFrameConstructionItems(aState, content, true, aParentFrame, items);
3864 }
3865 ConstructFramesFromItemList(aState, items, aParentFrame, aChildItems);
3866 }
3867 }
3868
3869 return NS_OK;
3870 }
3871
3872 static void
3873 SetFlagsOnSubtree(nsIContent *aNode, uintptr_t aFlagsToSet)
3874 {
3875 #ifdef DEBUG
3876 // Make sure that the node passed to us doesn't have any XBL children
3877 {
3878 FlattenedChildIterator iter(aNode);
3879 NS_ASSERTION(!iter.XBLInvolved() || !iter.GetNextChild(),
3880 "The node should not have any XBL children");
3881 }
3882 #endif
3883
3884 // Set the flag on the node itself
3885 aNode->SetFlags(aFlagsToSet);
3886
3887 // Set the flag on all of its children recursively
3888 uint32_t count;
3889 nsIContent * const *children = aNode->GetChildArray(&count);
3890
3891 for (uint32_t index = 0; index < count; ++index) {
3892 SetFlagsOnSubtree(children[index], aFlagsToSet);
3893 }
3894 }
3895
3896 /**
3897 * This function takes a tree of nsIAnonymousContentCreator::ContentInfo
3898 * objects where the nsIContent nodes have just been created, and appends the
3899 * nsIContent children in the tree to their parent. The leaf nsIContent objects
3900 * are appended first to minimize the number of notifications that are sent
3901 * out (i.e. by appending as many descendants as posible while their parent is
3902 * not yet in the document tree).
3903 *
3904 * This function is used simply as a convenience so that implementations of
3905 * nsIAnonymousContentCreator::CreateAnonymousContent don't all have to have
3906 * their own code to connect the elements that they create.
3907 */
3908 static void
3909 ConnectAnonymousTreeDescendants(nsIContent* aParent,
3910 nsTArray<nsIAnonymousContentCreator::ContentInfo>& aContent)
3911 {
3912 uint32_t count = aContent.Length();
3913 for (uint32_t i=0; i < count; i++) {
3914 nsIContent* content = aContent[i].mContent;
3915 NS_ASSERTION(content, "null anonymous content?");
3916
3917 ConnectAnonymousTreeDescendants(content, aContent[i].mChildren);
3918
3919 aParent->AppendChildTo(content, false);
3920 }
3921 }
3922
3923 nsresult
3924 nsCSSFrameConstructor::GetAnonymousContent(nsIContent* aParent,
3925 nsIFrame* aParentFrame,
3926 nsTArray<nsIAnonymousContentCreator::ContentInfo>& aContent)
3927 {
3928 nsIAnonymousContentCreator* creator = do_QueryFrame(aParentFrame);
3929 if (!creator)
3930 return NS_OK;
3931
3932 nsresult rv = creator->CreateAnonymousContent(aContent);
3933 NS_ENSURE_SUCCESS(rv, rv);
3934
3935 uint32_t count = aContent.Length();
3936 for (uint32_t i=0; i < count; i++) {
3937 // get our child's content and set its parent to our content
3938 nsIContent* content = aContent[i].mContent;
3939 NS_ASSERTION(content, "null anonymous content?");
3940
3941 // least-surprise CSS binding until we do the SVG specified
3942 // cascading rules for <svg:use> - bug 265894
3943 if (aParentFrame->GetType() == nsGkAtoms::svgUseFrame) {
3944 content->SetFlags(NODE_IS_ANONYMOUS_ROOT);
3945 } else {
3946 content->SetIsNativeAnonymousRoot();
3947 }
3948
3949 ConnectAnonymousTreeDescendants(content, aContent[i].mChildren);
3950
3951 bool anonContentIsEditable = content->HasFlag(NODE_IS_EDITABLE);
3952 rv = content->BindToTree(mDocument, aParent, aParent, true);
3953 // If the anonymous content creator requested that the content should be
3954 // editable, honor its request.
3955 // We need to set the flag on the whole subtree, because existing
3956 // children's flags have already been set as part of the BindToTree operation.
3957 if (anonContentIsEditable) {
3958 NS_ASSERTION(aParentFrame->GetType() == nsGkAtoms::textInputFrame,
3959 "We only expect this for anonymous content under a text control frame");
3960 SetFlagsOnSubtree(content, NODE_IS_EDITABLE);
3961 }
3962 if (NS_FAILED(rv)) {
3963 content->UnbindFromTree();
3964 return rv;
3965 }
3966 }
3967
3968 return NS_OK;
3969 }
3970
3971 static
3972 bool IsXULDisplayType(const nsStyleDisplay* aDisplay)
3973 {
3974 return (aDisplay->mDisplay == NS_STYLE_DISPLAY_INLINE_BOX ||
3975 #ifdef MOZ_XUL
3976 aDisplay->mDisplay == NS_STYLE_DISPLAY_INLINE_XUL_GRID ||
3977 aDisplay->mDisplay == NS_STYLE_DISPLAY_INLINE_STACK ||
3978 #endif
3979 aDisplay->mDisplay == NS_STYLE_DISPLAY_BOX
3980 #ifdef MOZ_XUL
3981 || aDisplay->mDisplay == NS_STYLE_DISPLAY_XUL_GRID ||
3982 aDisplay->mDisplay == NS_STYLE_DISPLAY_STACK ||
3983 aDisplay->mDisplay == NS_STYLE_DISPLAY_XUL_GRID_GROUP ||
3984 aDisplay->mDisplay == NS_STYLE_DISPLAY_XUL_GRID_LINE ||
3985 aDisplay->mDisplay == NS_STYLE_DISPLAY_DECK ||
3986 aDisplay->mDisplay == NS_STYLE_DISPLAY_POPUP ||
3987 aDisplay->mDisplay == NS_STYLE_DISPLAY_GROUPBOX
3988 #endif
3989 );
3990 }
3991
3992
3993 // XUL frames are not allowed to be out of flow.
3994 #define SIMPLE_XUL_FCDATA(_func) \
3995 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH, \
3996 _func)
3997 #define SCROLLABLE_XUL_FCDATA(_func) \
3998 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH | \
3999 FCDATA_MAY_NEED_SCROLLFRAME, _func)
4000 // .. but we allow some XUL frames to be _containers_ for out-of-flow content
4001 // (This is the same as SCROLLABLE_XUL_FCDATA, but w/o FCDATA_SKIP_ABSPOS_PUSH)
4002 #define SCROLLABLE_ABSPOS_CONTAINER_XUL_FCDATA(_func) \
4003 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | \
4004 FCDATA_MAY_NEED_SCROLLFRAME, _func)
4005
4006 #define SIMPLE_XUL_CREATE(_tag, _func) \
4007 { &nsGkAtoms::_tag, SIMPLE_XUL_FCDATA(_func) }
4008 #define SCROLLABLE_XUL_CREATE(_tag, _func) \
4009 { &nsGkAtoms::_tag, SCROLLABLE_XUL_FCDATA(_func) }
4010 #define SIMPLE_XUL_INT_CREATE(_int, _func) \
4011 { _int, SIMPLE_XUL_FCDATA(_func) }
4012 #define SCROLLABLE_XUL_INT_CREATE(_int, _func) \
4013 { _int, SCROLLABLE_XUL_FCDATA(_func) }
4014 #define SCROLLABLE_ABSPOS_CONTAINER_XUL_INT_CREATE(_int, _func) \
4015 { _int, SCROLLABLE_ABSPOS_CONTAINER_XUL_FCDATA(_func) }
4016
4017 static
4018 nsIFrame* NS_NewGridBoxFrame(nsIPresShell* aPresShell,
4019 nsStyleContext* aStyleContext)
4020 {
4021 nsCOMPtr<nsBoxLayout> layout;
4022 NS_NewGridLayout2(aPresShell, getter_AddRefs(layout));
4023 return NS_NewBoxFrame(aPresShell, aStyleContext, false, layout);
4024 }
4025
4026 /* static */
4027 const nsCSSFrameConstructor::FrameConstructionData*
4028 nsCSSFrameConstructor::FindXULTagData(Element* aElement,
4029 nsIAtom* aTag,
4030 int32_t aNameSpaceID,
4031 nsStyleContext* aStyleContext)
4032 {
4033 if (aNameSpaceID != kNameSpaceID_XUL) {
4034 return nullptr;
4035 }
4036
4037 static const FrameConstructionDataByTag sXULTagData[] = {
4038 #ifdef MOZ_XUL
4039 SCROLLABLE_XUL_CREATE(button, NS_NewButtonBoxFrame),
4040 SCROLLABLE_XUL_CREATE(checkbox, NS_NewButtonBoxFrame),
4041 SCROLLABLE_XUL_CREATE(radio, NS_NewButtonBoxFrame),
4042 SCROLLABLE_XUL_CREATE(autorepeatbutton, NS_NewAutoRepeatBoxFrame),
4043 SCROLLABLE_XUL_CREATE(titlebar, NS_NewTitleBarFrame),
4044 SCROLLABLE_XUL_CREATE(resizer, NS_NewResizerFrame),
4045 SIMPLE_XUL_CREATE(image, NS_NewImageBoxFrame),
4046 SIMPLE_XUL_CREATE(spring, NS_NewLeafBoxFrame),
4047 SIMPLE_XUL_CREATE(spacer, NS_NewLeafBoxFrame),
4048 SIMPLE_XUL_CREATE(treechildren, NS_NewTreeBodyFrame),
4049 SIMPLE_XUL_CREATE(treecol, NS_NewTreeColFrame),
4050 SIMPLE_XUL_CREATE(text, NS_NewTextBoxFrame),
4051 SIMPLE_TAG_CHAIN(label, nsCSSFrameConstructor::FindXULLabelData),
4052 SIMPLE_TAG_CHAIN(description, nsCSSFrameConstructor::FindXULDescriptionData),
4053 SIMPLE_XUL_CREATE(menu, NS_NewMenuFrame),
4054 SIMPLE_XUL_CREATE(menubutton, NS_NewMenuFrame),
4055 SIMPLE_XUL_CREATE(menuitem, NS_NewMenuItemFrame),
4056 #ifdef XP_MACOSX
4057 SIMPLE_TAG_CHAIN(menubar, nsCSSFrameConstructor::FindXULMenubarData),
4058 #else
4059 SIMPLE_XUL_CREATE(menubar, NS_NewMenuBarFrame),
4060 #endif /* XP_MACOSX */
4061 SIMPLE_TAG_CHAIN(popupgroup, nsCSSFrameConstructor::FindPopupGroupData),
4062 SIMPLE_XUL_CREATE(iframe, NS_NewSubDocumentFrame),
4063 SIMPLE_XUL_CREATE(editor, NS_NewSubDocumentFrame),
4064 SIMPLE_XUL_CREATE(browser, NS_NewSubDocumentFrame),
4065 SIMPLE_XUL_CREATE(progressmeter, NS_NewProgressMeterFrame),
4066 SIMPLE_XUL_CREATE(splitter, NS_NewSplitterFrame),
4067 SIMPLE_TAG_CHAIN(listboxbody,
4068 nsCSSFrameConstructor::FindXULListBoxBodyData),
4069 SIMPLE_TAG_CHAIN(listitem, nsCSSFrameConstructor::FindXULListItemData),
4070 #endif /* MOZ_XUL */
4071 SIMPLE_XUL_CREATE(slider, NS_NewSliderFrame),
4072 SIMPLE_XUL_CREATE(scrollbar, NS_NewScrollbarFrame),
4073 SIMPLE_XUL_CREATE(scrollbarbutton, NS_NewScrollbarButtonFrame)
4074 };
4075
4076 return FindDataByTag(aTag, aElement, aStyleContext, sXULTagData,
4077 ArrayLength(sXULTagData));
4078 }
4079
4080 #ifdef MOZ_XUL
4081 /* static */
4082 const nsCSSFrameConstructor::FrameConstructionData*
4083 nsCSSFrameConstructor::FindPopupGroupData(Element* aElement,
4084 nsStyleContext* /* unused */)
4085 {
4086 if (!aElement->IsRootOfNativeAnonymousSubtree()) {
4087 return nullptr;
4088 }
4089
4090 static const FrameConstructionData sPopupSetData =
4091 SIMPLE_XUL_FCDATA(NS_NewPopupSetFrame);
4092 return &sPopupSetData;
4093 }
4094
4095 /* static */
4096 const nsCSSFrameConstructor::FrameConstructionData
4097 nsCSSFrameConstructor::sXULTextBoxData = SIMPLE_XUL_FCDATA(NS_NewTextBoxFrame);
4098
4099 /* static */
4100 const nsCSSFrameConstructor::FrameConstructionData*
4101 nsCSSFrameConstructor::FindXULLabelData(Element* aElement,
4102 nsStyleContext* /* unused */)
4103 {
4104 if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
4105 return &sXULTextBoxData;
4106 }
4107
4108 static const FrameConstructionData sLabelData =
4109 SIMPLE_XUL_FCDATA(NS_NewXULLabelFrame);
4110 return &sLabelData;
4111 }
4112
4113 static nsIFrame*
4114 NS_NewXULDescriptionFrame(nsIPresShell* aPresShell, nsStyleContext *aContext)
4115 {
4116 // XXXbz do we really need to set those flags? If the parent is not
4117 // a block we'll get them anyway, and if it is, do we want them?
4118 return NS_NewBlockFrame(aPresShell, aContext,
4119 NS_BLOCK_FLOAT_MGR | NS_BLOCK_MARGIN_ROOT);
4120 }
4121
4122 /* static */
4123 const nsCSSFrameConstructor::FrameConstructionData*
4124 nsCSSFrameConstructor::FindXULDescriptionData(Element* aElement,
4125 nsStyleContext* /* unused */)
4126 {
4127 if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
4128 return &sXULTextBoxData;
4129 }
4130
4131 static const FrameConstructionData sDescriptionData =
4132 SIMPLE_XUL_FCDATA(NS_NewXULDescriptionFrame);
4133 return &sDescriptionData;
4134 }
4135
4136 #ifdef XP_MACOSX
4137 /* static */
4138 const nsCSSFrameConstructor::FrameConstructionData*
4139 nsCSSFrameConstructor::FindXULMenubarData(Element* aElement,
4140 nsStyleContext* aStyleContext)
4141 {
4142 nsCOMPtr<nsIDocShell> treeItem =
4143 aStyleContext->PresContext()->GetDocShell();
4144 if (treeItem && nsIDocShellTreeItem::typeChrome == treeItem->ItemType()) {
4145 nsCOMPtr<nsIDocShellTreeItem> parent;
4146 treeItem->GetParent(getter_AddRefs(parent));
4147 if (!parent) {
4148 // This is the root. Suppress the menubar, since on Mac
4149 // window menus are not attached to the window.
4150 static const FrameConstructionData sSuppressData = SUPPRESS_FCDATA();
4151 return &sSuppressData;
4152 }
4153 }
4154
4155 static const FrameConstructionData sMenubarData =
4156 SIMPLE_XUL_FCDATA(NS_NewMenuBarFrame);
4157 return &sMenubarData;
4158 }
4159 #endif /* XP_MACOSX */
4160
4161 /* static */
4162 const nsCSSFrameConstructor::FrameConstructionData*
4163 nsCSSFrameConstructor::FindXULListBoxBodyData(Element* aElement,
4164 nsStyleContext* aStyleContext)
4165 {
4166 if (aStyleContext->StyleDisplay()->mDisplay !=
4167 NS_STYLE_DISPLAY_XUL_GRID_GROUP) {
4168 return nullptr;
4169 }
4170
4171 static const FrameConstructionData sListBoxBodyData =
4172 SCROLLABLE_XUL_FCDATA(NS_NewListBoxBodyFrame);
4173 return &sListBoxBodyData;
4174 }
4175
4176 /* static */
4177 const nsCSSFrameConstructor::FrameConstructionData*
4178 nsCSSFrameConstructor::FindXULListItemData(Element* aElement,
4179 nsStyleContext* aStyleContext)
4180 {
4181 if (aStyleContext->StyleDisplay()->mDisplay !=
4182 NS_STYLE_DISPLAY_XUL_GRID_LINE) {
4183 return nullptr;
4184 }
4185
4186 static const FrameConstructionData sListItemData =
4187 SCROLLABLE_XUL_FCDATA(NS_NewListItemFrame);
4188 return &sListItemData;
4189 }
4190
4191 #endif /* MOZ_XUL */
4192
4193 /* static */
4194 const nsCSSFrameConstructor::FrameConstructionData*
4195 nsCSSFrameConstructor::FindXULDisplayData(const nsStyleDisplay* aDisplay,
4196 Element* aElement,
4197 nsStyleContext* aStyleContext)
4198 {
4199 static const FrameConstructionDataByInt sXULDisplayData[] = {
4200 SCROLLABLE_ABSPOS_CONTAINER_XUL_INT_CREATE(NS_STYLE_DISPLAY_INLINE_BOX,
4201 NS_NewBoxFrame),
4202 SCROLLABLE_ABSPOS_CONTAINER_XUL_INT_CREATE(NS_STYLE_DISPLAY_BOX,
4203 NS_NewBoxFrame),
4204 #ifdef MOZ_XUL
4205 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_INLINE_XUL_GRID, NS_NewGridBoxFrame),
4206 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_XUL_GRID, NS_NewGridBoxFrame),
4207 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_XUL_GRID_GROUP,
4208 NS_NewGridRowGroupFrame),
4209 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_XUL_GRID_LINE,
4210 NS_NewGridRowLeafFrame),
4211 SIMPLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_DECK, NS_NewDeckFrame),
4212 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_GROUPBOX, NS_NewGroupBoxFrame),
4213 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_INLINE_STACK, NS_NewStackFrame),
4214 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_STACK, NS_NewStackFrame),
4215 { NS_STYLE_DISPLAY_POPUP,
4216 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_IS_POPUP |
4217 FCDATA_SKIP_ABSPOS_PUSH, NS_NewMenuPopupFrame) }
4218 #endif /* MOZ_XUL */
4219 };
4220
4221 // Processing by display here:
4222 return FindDataByInt(aDisplay->mDisplay, aElement, aStyleContext,
4223 sXULDisplayData, ArrayLength(sXULDisplayData));
4224 }
4225
4226 already_AddRefed<nsStyleContext>
4227 nsCSSFrameConstructor::BeginBuildingScrollFrame(nsFrameConstructorState& aState,
4228 nsIContent* aContent,
4229 nsStyleContext* aContentStyle,
4230 nsIFrame* aParentFrame,
4231 nsIAtom* aScrolledPseudo,
4232 bool aIsRoot,
4233 nsIFrame*& aNewFrame)
4234 {
4235 nsIFrame* gfxScrollFrame = aNewFrame;
4236
4237 nsFrameItems anonymousItems;
4238
4239 nsRefPtr<nsStyleContext> contentStyle = aContentStyle;
4240
4241 if (!gfxScrollFrame) {
4242 // Build a XULScrollFrame when the child is a box, otherwise an
4243 // HTMLScrollFrame
4244 // XXXbz this is the lone remaining consumer of IsXULDisplayType.
4245 // I wonder whether we can eliminate that somehow.
4246 const nsStyleDisplay* displayStyle = aContentStyle->StyleDisplay();
4247 if (IsXULDisplayType(displayStyle)) {
4248 gfxScrollFrame = NS_NewXULScrollFrame(mPresShell, contentStyle, aIsRoot,
4249 displayStyle->mDisplay == NS_STYLE_DISPLAY_STACK ||
4250 displayStyle->mDisplay == NS_STYLE_DISPLAY_INLINE_STACK);
4251 } else {
4252 gfxScrollFrame = NS_NewHTMLScrollFrame(mPresShell, contentStyle, aIsRoot);
4253 }
4254
4255 InitAndRestoreFrame(aState, aContent, aParentFrame, gfxScrollFrame);
4256 }
4257
4258 // if there are any anonymous children for the scroll frame, create
4259 // frames for them.
4260 // Pass a null pending binding: we don't care how constructors for any of
4261 // this anonymous content order with anything else. It's never been
4262 // consistent anyway.
4263 CreateAnonymousFrames(aState, aContent, gfxScrollFrame, nullptr,
4264 anonymousItems);
4265
4266 aNewFrame = gfxScrollFrame;
4267
4268 // we used the style that was passed in. So resolve another one.
4269 nsStyleSet *styleSet = mPresShell->StyleSet();
4270 nsRefPtr<nsStyleContext> scrolledChildStyle =
4271 styleSet->ResolveAnonymousBoxStyle(aScrolledPseudo, contentStyle);
4272
4273 if (gfxScrollFrame) {
4274 gfxScrollFrame->SetInitialChildList(kPrincipalList, anonymousItems);
4275 }
4276
4277 return scrolledChildStyle.forget();
4278 }
4279
4280 void
4281 nsCSSFrameConstructor::FinishBuildingScrollFrame(nsIFrame* aScrollFrame,
4282 nsIFrame* aScrolledFrame)
4283 {
4284 nsFrameList scrolled(aScrolledFrame, aScrolledFrame);
4285 aScrollFrame->AppendFrames(kPrincipalList, scrolled);
4286 }
4287
4288
4289 /**
4290 * Called to wrap a gfx scrollframe around a frame. The hierarchy will look like this
4291 *
4292 * ------- for gfx scrollbars ------
4293 *
4294 *
4295 * ScrollFrame
4296 * ^
4297 * |
4298 * Frame (scrolled frame you passed in)
4299 *
4300 *
4301 *-----------------------------------
4302 * LEGEND:
4303 *
4304 * ScrollFrame: This is a frame that manages gfx cross platform frame based scrollbars.
4305 *
4306 * @param aContent the content node of the child to wrap.
4307 * @param aScrolledFrame The frame of the content to wrap. This should not be
4308 * Initialized. This method will initialize it with a scrolled pseudo
4309 * and no nsIContent. The content will be attached to the scrollframe
4310 * returned.
4311 * @param aContentStyle the style context that has already been resolved for the content being passed in.
4312 *
4313 * @param aParentFrame The parent to attach the scroll frame to
4314 *
4315 * @param aNewFrame The new scrollframe or gfx scrollframe that we create. It will contain the
4316 * scrolled frame you passed in. (returned)
4317 * If this is not null, we'll just use it
4318 * @param aScrolledContentStyle the style that was resolved for the scrolled frame. (returned)
4319 */
4320 nsresult
4321 nsCSSFrameConstructor::BuildScrollFrame(nsFrameConstructorState& aState,
4322 nsIContent* aContent,
4323 nsStyleContext* aContentStyle,
4324 nsIFrame* aScrolledFrame,
4325 nsIFrame* aParentFrame,
4326 nsIFrame*& aNewFrame)
4327 {
4328 nsRefPtr<nsStyleContext> scrolledContentStyle =
4329 BeginBuildingScrollFrame(aState, aContent, aContentStyle, aParentFrame,
4330 nsCSSAnonBoxes::scrolledContent,
4331 false, aNewFrame);
4332
4333 aScrolledFrame->SetStyleContextWithoutNotification(scrolledContentStyle);
4334 InitAndRestoreFrame(aState, aContent, aNewFrame, aScrolledFrame);
4335
4336 FinishBuildingScrollFrame(aNewFrame, aScrolledFrame);
4337 return NS_OK;
4338 }
4339
4340 const nsCSSFrameConstructor::FrameConstructionData*
4341 nsCSSFrameConstructor::FindDisplayData(const nsStyleDisplay* aDisplay,
4342 Element* aElement,
4343 nsIFrame* aParentFrame,
4344 nsStyleContext* aStyleContext)
4345 {
4346 PR_STATIC_ASSERT(eParentTypeCount < (1 << (32 - FCDATA_PARENT_TYPE_OFFSET)));
4347
4348 // The style system ensures that floated and positioned frames are
4349 // block-level.
4350 NS_ASSERTION(!(aDisplay->IsFloatingStyle() ||
4351 aDisplay->IsAbsolutelyPositionedStyle()) ||
4352 aDisplay->IsBlockOutsideStyle(),
4353 "Style system did not apply CSS2.1 section 9.7 fixups");
4354
4355 // If this is "body", try propagating its scroll style to the viewport
4356 // Note that we need to do this even if the body is NOT scrollable;
4357 // it might have dynamically changed from scrollable to not scrollable,
4358 // and that might need to be propagated.
4359 // XXXbz is this the right place to do this? If this code moves,
4360 // make this function static.
4361 bool propagatedScrollToViewport = false;
4362 if (aElement->IsHTML(nsGkAtoms::body)) {
4363 propagatedScrollToViewport =
4364 PropagateScrollToViewport() == aElement;
4365 }
4366
4367 NS_ASSERTION(!propagatedScrollToViewport ||
4368 !mPresShell->GetPresContext()->IsPaginated(),
4369 "Shouldn't propagate scroll in paginated contexts");
4370
4371 // If the frame is a block-level frame and is scrollable, then wrap it in a
4372 // scroll frame.
4373 // XXX Ignore tables for the time being
4374 // XXXbz it would be nice to combine this with the other block
4375 // case... Think about how do do this?
4376 if (aDisplay->IsBlockInsideStyle() &&
4377 aDisplay->IsScrollableOverflow() &&
4378 !propagatedScrollToViewport) {
4379 // Except we don't want to do that for paginated contexts for
4380 // frames that are block-outside and aren't frames for native
4381 // anonymous stuff.
4382 if (mPresShell->GetPresContext()->IsPaginated() &&
4383 aDisplay->IsBlockOutsideStyle() &&
4384 !aElement->IsInNativeAnonymousSubtree()) {
4385 static const FrameConstructionData sForcedNonScrollableBlockData =
4386 FULL_CTOR_FCDATA(FCDATA_FORCED_NON_SCROLLABLE_BLOCK,
4387 &nsCSSFrameConstructor::ConstructNonScrollableBlock);
4388 return &sForcedNonScrollableBlockData;
4389 }
4390
4391 static const FrameConstructionData sScrollableBlockData =
4392 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructScrollableBlock);
4393 return &sScrollableBlockData;
4394 }
4395
4396 // Handle various non-scrollable blocks
4397 if (aDisplay->IsBlockInsideStyle()) {
4398 static const FrameConstructionData sNonScrollableBlockData =
4399 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructNonScrollableBlock);
4400 return &sNonScrollableBlockData;
4401 }
4402
4403 static const FrameConstructionDataByInt sDisplayData[] = {
4404 // To keep the hash table small don't add inline frames (they're
4405 // typically things like FONT and B), because we can quickly
4406 // find them if we need to.
4407 // XXXbz the "quickly" part is a bald-faced lie!
4408 { NS_STYLE_DISPLAY_INLINE,
4409 FULL_CTOR_FCDATA(FCDATA_IS_INLINE | FCDATA_IS_LINE_PARTICIPANT,
4410 &nsCSSFrameConstructor::ConstructInline) },
4411 { NS_STYLE_DISPLAY_FLEX,
4412 FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame) },
4413 { NS_STYLE_DISPLAY_INLINE_FLEX,
4414 FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame) },
4415 { NS_STYLE_DISPLAY_GRID,
4416 FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewGridContainerFrame) },
4417 { NS_STYLE_DISPLAY_INLINE_GRID,
4418 FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewGridContainerFrame) },
4419 { NS_STYLE_DISPLAY_TABLE,
4420 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructTable) },
4421 { NS_STYLE_DISPLAY_INLINE_TABLE,
4422 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructTable) },
4423 // NOTE: In the unlikely event that we add another table-part here that has
4424 // a desired-parent-type (& hence triggers table fixup), we'll need to also
4425 // update the flexbox chunk in nsStyleContext::ApplyStyleFixups().
4426 { NS_STYLE_DISPLAY_TABLE_CAPTION,
4427 FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_ALLOW_BLOCK_STYLES |
4428 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH |
4429 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
4430 NS_NewTableCaptionFrame) },
4431 { NS_STYLE_DISPLAY_TABLE_ROW_GROUP,
4432 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
4433 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
4434 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup) },
4435 { NS_STYLE_DISPLAY_TABLE_HEADER_GROUP,
4436 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
4437 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
4438 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup) },
4439 { NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP,
4440 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
4441 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
4442 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup) },
4443 { NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP,
4444 FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW |
4445 FCDATA_SKIP_ABSPOS_PUSH |
4446 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
4447 NS_NewTableColGroupFrame) },
4448 { NS_STYLE_DISPLAY_TABLE_COLUMN,
4449 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
4450 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeColGroup),
4451 &nsCSSFrameConstructor::ConstructTableCol) },
4452 { NS_STYLE_DISPLAY_TABLE_ROW,
4453 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
4454 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup),
4455 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup) },
4456 { NS_STYLE_DISPLAY_TABLE_CELL,
4457 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
4458 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow),
4459 &nsCSSFrameConstructor::ConstructTableCell) }
4460 };
4461
4462 return FindDataByInt(aDisplay->mDisplay,
4463 aElement, aStyleContext, sDisplayData,
4464 ArrayLength(sDisplayData));
4465 }
4466
4467 nsIFrame*
4468 nsCSSFrameConstructor::ConstructScrollableBlock(nsFrameConstructorState& aState,
4469 FrameConstructionItem& aItem,
4470 nsIFrame* aParentFrame,
4471 const nsStyleDisplay* aDisplay,
4472 nsFrameItems& aFrameItems)
4473 {
4474 nsIContent* const content = aItem.mContent;
4475 nsStyleContext* const styleContext = aItem.mStyleContext;
4476
4477 nsIFrame* newFrame = nullptr;
4478 nsRefPtr<nsStyleContext> scrolledContentStyle
4479 = BeginBuildingScrollFrame(aState, content, styleContext,
4480 aState.GetGeometricParent(aDisplay, aParentFrame),
4481 nsCSSAnonBoxes::scrolledContent,
4482 false, newFrame);
4483
4484 // Create our block frame
4485 // pass a temporary stylecontext, the correct one will be set later
4486 nsIFrame* scrolledFrame =
4487 NS_NewBlockFormattingContext(mPresShell, styleContext);
4488
4489 // Make sure to AddChild before we call ConstructBlock so that we
4490 // end up before our descendants in fixed-pos lists as needed.
4491 aState.AddChild(newFrame, aFrameItems, content, styleContext, aParentFrame);
4492
4493 nsFrameItems blockItem;
4494 ConstructBlock(aState, scrolledContentStyle->StyleDisplay(), content,
4495 newFrame, newFrame, scrolledContentStyle,
4496 &scrolledFrame, blockItem,
4497 aDisplay->IsPositioned(newFrame) ? newFrame : nullptr,
4498 aItem.mPendingBinding);
4499
4500 NS_ASSERTION(blockItem.FirstChild() == scrolledFrame,
4501 "Scrollframe's frameItems should be exactly the scrolled frame");
4502 FinishBuildingScrollFrame(newFrame, scrolledFrame);
4503
4504 return newFrame;
4505 }
4506
4507 nsIFrame*
4508 nsCSSFrameConstructor::ConstructNonScrollableBlock(nsFrameConstructorState& aState,
4509 FrameConstructionItem& aItem,
4510 nsIFrame* aParentFrame,
4511 const nsStyleDisplay* aDisplay,
4512 nsFrameItems& aFrameItems)
4513 {
4514 nsStyleContext* const styleContext = aItem.mStyleContext;
4515
4516 // We want a block formatting context root in paginated contexts for
4517 // every block that would be scrollable in a non-paginated context.
4518 // We mark our blocks with a bit here if this condition is true, so
4519 // we can check it later in nsFrame::ApplyPaginatedOverflowClipping.
4520 bool clipPaginatedOverflow =
4521 (aItem.mFCData->mBits & FCDATA_FORCED_NON_SCROLLABLE_BLOCK) != 0;
4522 nsIFrame* newFrame;
4523 if ((aDisplay->IsAbsolutelyPositionedStyle() ||
4524 aDisplay->IsFloatingStyle() ||
4525 NS_STYLE_DISPLAY_INLINE_BLOCK == aDisplay->mDisplay ||
4526 clipPaginatedOverflow) &&
4527 !aParentFrame->IsSVGText()) {
4528 newFrame = NS_NewBlockFormattingContext(mPresShell, styleContext);
4529 if (clipPaginatedOverflow) {
4530 newFrame->AddStateBits(NS_BLOCK_CLIP_PAGINATED_OVERFLOW);
4531 }
4532 } else {
4533 newFrame = NS_NewBlockFrame(mPresShell, styleContext);
4534 }
4535
4536 ConstructBlock(aState, aDisplay, aItem.mContent,
4537 aState.GetGeometricParent(aDisplay, aParentFrame),
4538 aParentFrame, styleContext, &newFrame,
4539 aFrameItems,
4540 aDisplay->IsPositioned(newFrame) ? newFrame : nullptr,
4541 aItem.mPendingBinding);
4542 return newFrame;
4543 }
4544
4545
4546 void
4547 nsCSSFrameConstructor::InitAndRestoreFrame(const nsFrameConstructorState& aState,
4548 nsIContent* aContent,
4549 nsIFrame* aParentFrame,
4550 nsIFrame* aNewFrame,
4551 bool aAllowCounters)
4552 {
4553 NS_PRECONDITION(mUpdateCount != 0,
4554 "Should be in an update while creating frames");
4555
4556 MOZ_ASSERT(aNewFrame, "Null frame cannot be initialized");
4557
4558 // Initialize the frame
4559 aNewFrame->Init(aContent, aParentFrame, nullptr);
4560 aNewFrame->AddStateBits(aState.mAdditionalStateBits);
4561
4562 if (aState.mFrameState) {
4563 // Restore frame state for just the newly created frame.
4564 RestoreFrameStateFor(aNewFrame, aState.mFrameState);
4565 }
4566
4567 if (aAllowCounters &&
4568 mCounterManager.AddCounterResetsAndIncrements(aNewFrame)) {
4569 CountersDirty();
4570 }
4571 }
4572
4573 already_AddRefed<nsStyleContext>
4574 nsCSSFrameConstructor::ResolveStyleContext(nsIFrame* aParentFrame,
4575 nsIContent* aContent,
4576 nsFrameConstructorState* aState)
4577 {
4578 nsStyleContext* parentStyleContext = nullptr;
4579 NS_ASSERTION(aContent->GetParent(), "Must have parent here");
4580
4581 aParentFrame = nsFrame::CorrectStyleParentFrame(aParentFrame, nullptr);
4582
4583 if (aParentFrame) {
4584 // Resolve the style context based on the content object and the parent
4585 // style context
4586 parentStyleContext = aParentFrame->StyleContext();
4587 } else {
4588 // Perhaps aParentFrame is a canvasFrame and we're replicating
4589 // fixed-pos frames.
4590 // XXX should we create a way to tell ConstructFrame which style
4591 // context to use, and pass it the style context for the
4592 // previous page's fixed-pos frame?
4593 }
4594
4595 return ResolveStyleContext(parentStyleContext, aContent, aState);
4596 }
4597
4598 already_AddRefed<nsStyleContext>
4599 nsCSSFrameConstructor::ResolveStyleContext(nsStyleContext* aParentStyleContext,
4600 nsIContent* aContent,
4601 nsFrameConstructorState* aState)
4602 {
4603 nsStyleSet *styleSet = mPresShell->StyleSet();
4604 aContent->OwnerDoc()->FlushPendingLinkUpdates();
4605
4606 if (aContent->IsElement()) {
4607 if (aState) {
4608 return styleSet->ResolveStyleFor(aContent->AsElement(),
4609 aParentStyleContext,
4610 aState->mTreeMatchContext);
4611 }
4612 return styleSet->ResolveStyleFor(aContent->AsElement(), aParentStyleContext);
4613
4614 }
4615
4616 NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
4617 "shouldn't waste time creating style contexts for "
4618 "comments and processing instructions");
4619
4620 return styleSet->ResolveStyleForNonElement(aParentStyleContext);
4621 }
4622
4623 // MathML Mod - RBS
4624 void
4625 nsCSSFrameConstructor::FlushAccumulatedBlock(nsFrameConstructorState& aState,
4626 nsIContent* aContent,
4627 nsIFrame* aParentFrame,
4628 nsFrameItems& aBlockItems,
4629 nsFrameItems& aNewItems)
4630 {
4631 if (aBlockItems.IsEmpty()) {
4632 // Nothing to do
4633 return;
4634 }
4635
4636 nsIAtom* anonPseudo = nsCSSAnonBoxes::mozMathMLAnonymousBlock;
4637
4638 nsStyleContext* parentContext =
4639 nsFrame::CorrectStyleParentFrame(aParentFrame,
4640 anonPseudo)->StyleContext();
4641 nsStyleSet* styleSet = mPresShell->StyleSet();
4642 nsRefPtr<nsStyleContext> blockContext;
4643 blockContext = styleSet->
4644 ResolveAnonymousBoxStyle(anonPseudo, parentContext);
4645
4646
4647 // then, create a block frame that will wrap the child frames. Make it a
4648 // MathML frame so that Get(Absolute/Float)ContainingBlockFor know that this
4649 // is not a suitable block.
4650 nsIFrame* blockFrame =
4651 NS_NewMathMLmathBlockFrame(mPresShell, blockContext,
4652 NS_BLOCK_FLOAT_MGR | NS_BLOCK_MARGIN_ROOT);
4653
4654 InitAndRestoreFrame(aState, aContent, aParentFrame, blockFrame);
4655 ReparentFrames(this, blockFrame, aBlockItems);
4656 // abs-pos and floats are disabled in MathML children so we don't have to
4657 // worry about messing up those.
4658 blockFrame->SetInitialChildList(kPrincipalList, aBlockItems);
4659 NS_ASSERTION(aBlockItems.IsEmpty(), "What happened?");
4660 aBlockItems.Clear();
4661 aNewItems.AddChild(blockFrame);
4662 }
4663
4664 // Only <math> elements can be floated or positioned. All other MathML
4665 // should be in-flow.
4666 #define SIMPLE_MATHML_CREATE(_tag, _func) \
4667 { &nsGkAtoms::_tag, \
4668 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | \
4669 FCDATA_FORCE_NULL_ABSPOS_CONTAINER | \
4670 FCDATA_WRAP_KIDS_IN_BLOCKS, _func) }
4671
4672 /* static */
4673 const nsCSSFrameConstructor::FrameConstructionData*
4674 nsCSSFrameConstructor::FindMathMLData(Element* aElement,
4675 nsIAtom* aTag,
4676 int32_t aNameSpaceID,
4677 nsStyleContext* aStyleContext)
4678 {
4679 // Make sure that we remain confined in the MathML world
4680 if (aNameSpaceID != kNameSpaceID_MathML)
4681 return nullptr;
4682
4683 // Handle <math> specially, because it sometimes produces inlines
4684 if (aTag == nsGkAtoms::math) {
4685 // This needs to match the test in EnsureBlockDisplay in
4686 // nsRuleNode.cpp. Though the behavior here for the display:table
4687 // case is pretty weird...
4688 if (aStyleContext->StyleDisplay()->IsBlockOutsideStyle()) {
4689 static const FrameConstructionData sBlockMathData =
4690 FCDATA_DECL(FCDATA_FORCE_NULL_ABSPOS_CONTAINER |
4691 FCDATA_WRAP_KIDS_IN_BLOCKS,
4692 NS_CreateNewMathMLmathBlockFrame);
4693 return &sBlockMathData;
4694 }
4695
4696 static const FrameConstructionData sInlineMathData =
4697 FCDATA_DECL(FCDATA_FORCE_NULL_ABSPOS_CONTAINER |
4698 FCDATA_IS_LINE_PARTICIPANT |
4699 FCDATA_WRAP_KIDS_IN_BLOCKS,
4700 NS_NewMathMLmathInlineFrame);
4701 return &sInlineMathData;
4702 }
4703
4704
4705 static const FrameConstructionDataByTag sMathMLData[] = {
4706 SIMPLE_MATHML_CREATE(annotation_, NS_NewMathMLTokenFrame),
4707 SIMPLE_MATHML_CREATE(annotation_xml_, NS_NewMathMLmrowFrame),
4708 SIMPLE_MATHML_CREATE(mi_, NS_NewMathMLTokenFrame),
4709 SIMPLE_MATHML_CREATE(mn_, NS_NewMathMLTokenFrame),
4710 SIMPLE_MATHML_CREATE(ms_, NS_NewMathMLTokenFrame),
4711 SIMPLE_MATHML_CREATE(mtext_, NS_NewMathMLTokenFrame),
4712 SIMPLE_MATHML_CREATE(mo_, NS_NewMathMLmoFrame),
4713 SIMPLE_MATHML_CREATE(mfrac_, NS_NewMathMLmfracFrame),
4714 SIMPLE_MATHML_CREATE(msup_, NS_NewMathMLmmultiscriptsFrame),
4715 SIMPLE_MATHML_CREATE(msub_, NS_NewMathMLmmultiscriptsFrame),
4716 SIMPLE_MATHML_CREATE(msubsup_, NS_NewMathMLmmultiscriptsFrame),
4717 SIMPLE_MATHML_CREATE(munder_, NS_NewMathMLmunderoverFrame),
4718 SIMPLE_MATHML_CREATE(mover_, NS_NewMathMLmunderoverFrame),
4719 SIMPLE_MATHML_CREATE(munderover_, NS_NewMathMLmunderoverFrame),
4720 SIMPLE_MATHML_CREATE(mphantom_, NS_NewMathMLmphantomFrame),
4721 SIMPLE_MATHML_CREATE(mpadded_, NS_NewMathMLmpaddedFrame),
4722 SIMPLE_MATHML_CREATE(mspace_, NS_NewMathMLmspaceFrame),
4723 SIMPLE_MATHML_CREATE(none, NS_NewMathMLmspaceFrame),
4724 SIMPLE_MATHML_CREATE(mprescripts_, NS_NewMathMLmspaceFrame),
4725 SIMPLE_MATHML_CREATE(mfenced_, NS_NewMathMLmfencedFrame),
4726 SIMPLE_MATHML_CREATE(mmultiscripts_, NS_NewMathMLmmultiscriptsFrame),
4727 SIMPLE_MATHML_CREATE(mstyle_, NS_NewMathMLmrowFrame),
4728 SIMPLE_MATHML_CREATE(msqrt_, NS_NewMathMLmsqrtFrame),
4729 SIMPLE_MATHML_CREATE(mroot_, NS_NewMathMLmrootFrame),
4730 SIMPLE_MATHML_CREATE(maction_, NS_NewMathMLmactionFrame),
4731 SIMPLE_MATHML_CREATE(mrow_, NS_NewMathMLmrowFrame),
4732 SIMPLE_MATHML_CREATE(merror_, NS_NewMathMLmrowFrame),
4733 SIMPLE_MATHML_CREATE(menclose_, NS_NewMathMLmencloseFrame),
4734 SIMPLE_MATHML_CREATE(semantics_, NS_NewMathMLsemanticsFrame)
4735 };
4736
4737 return FindDataByTag(aTag, aElement, aStyleContext, sMathMLData,
4738 ArrayLength(sMathMLData));
4739 }
4740
4741
4742 nsIFrame*
4743 nsCSSFrameConstructor::ConstructFrameWithAnonymousChild(
4744 nsFrameConstructorState& aState,
4745 FrameConstructionItem& aItem,
4746 nsIFrame* aParentFrame,
4747 const nsStyleDisplay* aDisplay,
4748 nsFrameItems& aFrameItems,
4749 FrameCreationFunc aConstructor,
4750 FrameCreationFunc aInnerConstructor,
4751 nsICSSAnonBoxPseudo* aInnerPseudo,
4752 bool aCandidateRootFrame)
4753 {
4754 nsIContent* const content = aItem.mContent;
4755 nsStyleContext* const styleContext = aItem.mStyleContext;
4756
4757 // Create the outer frame:
4758 nsIFrame* newFrame = aConstructor(mPresShell, styleContext);
4759
4760 InitAndRestoreFrame(aState, content,
4761 aCandidateRootFrame ?
4762 aState.GetGeometricParent(styleContext->StyleDisplay(),
4763 aParentFrame) :
4764 aParentFrame,
4765 newFrame);
4766
4767 // Create the pseudo SC for the anonymous wrapper child as a child of the SC:
4768 nsRefPtr<nsStyleContext> scForAnon;
4769 scForAnon = mPresShell->StyleSet()->
4770 ResolveAnonymousBoxStyle(aInnerPseudo, styleContext);
4771
4772 // Create the anonymous inner wrapper frame
4773 nsIFrame* innerFrame = aInnerConstructor(mPresShell, scForAnon);
4774
4775 InitAndRestoreFrame(aState, content, newFrame, innerFrame);
4776
4777 // Put the newly created frames into the right child list
4778 SetInitialSingleChild(newFrame, innerFrame);
4779
4780 aState.AddChild(newFrame, aFrameItems, content, styleContext, aParentFrame,
4781 aCandidateRootFrame, aCandidateRootFrame);
4782
4783 if (!mRootElementFrame && aCandidateRootFrame) {
4784 // The frame we're constructing will be the root element frame.
4785 // Set mRootElementFrame before processing children.
4786 mRootElementFrame = newFrame;
4787 }
4788
4789 nsFrameItems childItems;
4790
4791 // Process children
4792 NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
4793 "nsIAnonymousContentCreator::CreateAnonymousContent should not "
4794 "be implemented for frames for which we explicitly create an "
4795 "anonymous child to wrap its child frames");
4796 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
4797 ConstructFramesFromItemList(aState, aItem.mChildItems,
4798 innerFrame, childItems);
4799 } else {
4800 ProcessChildren(aState, content, styleContext, innerFrame,
4801 true, childItems, false, aItem.mPendingBinding);
4802 }
4803
4804 // Set the inner wrapper frame's initial primary list
4805 innerFrame->SetInitialChildList(kPrincipalList, childItems);
4806
4807 return newFrame;
4808 }
4809
4810 nsIFrame*
4811 nsCSSFrameConstructor::ConstructOuterSVG(nsFrameConstructorState& aState,
4812 FrameConstructionItem& aItem,
4813 nsIFrame* aParentFrame,
4814 const nsStyleDisplay* aDisplay,
4815 nsFrameItems& aFrameItems)
4816 {
4817 return ConstructFrameWithAnonymousChild(
4818 aState, aItem, aParentFrame, aDisplay, aFrameItems,
4819 NS_NewSVGOuterSVGFrame, NS_NewSVGOuterSVGAnonChildFrame,
4820 nsCSSAnonBoxes::mozSVGOuterSVGAnonChild, true);
4821 }
4822
4823 nsIFrame*
4824 nsCSSFrameConstructor::ConstructMarker(nsFrameConstructorState& aState,
4825 FrameConstructionItem& aItem,
4826 nsIFrame* aParentFrame,
4827 const nsStyleDisplay* aDisplay,
4828 nsFrameItems& aFrameItems)
4829 {
4830 return ConstructFrameWithAnonymousChild(
4831 aState, aItem, aParentFrame, aDisplay, aFrameItems,
4832 NS_NewSVGMarkerFrame, NS_NewSVGMarkerAnonChildFrame,
4833 nsCSSAnonBoxes::mozSVGMarkerAnonChild, false);
4834 }
4835
4836 // Only outer <svg> elements can be floated or positioned. All other SVG
4837 // should be in-flow.
4838 #define SIMPLE_SVG_FCDATA(_func) \
4839 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | \
4840 FCDATA_SKIP_ABSPOS_PUSH | \
4841 FCDATA_DISALLOW_GENERATED_CONTENT, _func)
4842 #define SIMPLE_SVG_CREATE(_tag, _func) \
4843 { &nsGkAtoms::_tag, SIMPLE_SVG_FCDATA(_func) }
4844
4845 static bool
4846 IsFilterPrimitiveChildTag(const nsIAtom* aTag)
4847 {
4848 return aTag == nsGkAtoms::feDistantLight ||
4849 aTag == nsGkAtoms::fePointLight ||
4850 aTag == nsGkAtoms::feSpotLight ||
4851 aTag == nsGkAtoms::feFuncR ||
4852 aTag == nsGkAtoms::feFuncG ||
4853 aTag == nsGkAtoms::feFuncB ||
4854 aTag == nsGkAtoms::feFuncA ||
4855 aTag == nsGkAtoms::feMergeNode;
4856 }
4857
4858 /* static */
4859 const nsCSSFrameConstructor::FrameConstructionData*
4860 nsCSSFrameConstructor::FindSVGData(Element* aElement,
4861 nsIAtom* aTag,
4862 int32_t aNameSpaceID,
4863 nsIFrame* aParentFrame,
4864 bool aIsWithinSVGText,
4865 bool aAllowsTextPathChild,
4866 nsStyleContext* aStyleContext)
4867 {
4868 if (aNameSpaceID != kNameSpaceID_SVG) {
4869 return nullptr;
4870 }
4871
4872 static const FrameConstructionData sSuppressData = SUPPRESS_FCDATA();
4873 static const FrameConstructionData sContainerData =
4874 SIMPLE_SVG_FCDATA(NS_NewSVGContainerFrame);
4875
4876 bool parentIsSVG = aIsWithinSVGText;
4877 nsIContent* parentContent =
4878 aParentFrame ? aParentFrame->GetContent() : nullptr;
4879 // XXXbz should this really be based on the XBL-resolved tag of the parent
4880 // frame's content? Should it not be based on the type of the parent frame
4881 // (e.g. whether it's an SVG frame)?
4882 if (parentContent) {
4883 int32_t parentNSID;
4884 nsIAtom* parentTag =
4885 parentContent->OwnerDoc()->BindingManager()->
4886 ResolveTag(parentContent, &parentNSID);
4887
4888 // It's not clear whether the SVG spec intends to allow any SVG
4889 // content within svg:foreignObject at all (SVG 1.1, section
4890 // 23.2), but if it does, it better be svg:svg. So given that
4891 // we're allowing it, treat it as a non-SVG parent.
4892 parentIsSVG = parentNSID == kNameSpaceID_SVG &&
4893 parentTag != nsGkAtoms::foreignObject;
4894 }
4895
4896 if ((aTag != nsGkAtoms::svg && !parentIsSVG) ||
4897 (aTag == nsGkAtoms::desc || aTag == nsGkAtoms::title)) {
4898 // Sections 5.1 and G.4 of SVG 1.1 say that SVG elements other than
4899 // svg:svg not contained within svg:svg are incorrect, although they
4900 // don't seem to specify error handling. Ignore them, since many of
4901 // our frame classes can't deal. It *may* be that the document
4902 // should at that point be considered in error according to F.2, but
4903 // it's hard to tell.
4904 //
4905 // Style mutation can't change this situation, so don't bother
4906 // adding to the undisplayed content map.
4907 //
4908 // We don't currently handle any UI for desc/title
4909 return &sSuppressData;
4910 }
4911
4912 // We don't need frames for animation elements
4913 if (aElement->IsNodeOfType(nsINode::eANIMATION)) {
4914 return &sSuppressData;
4915 }
4916
4917 if (aTag == nsGkAtoms::svg && !parentIsSVG) {
4918 // We need outer <svg> elements to have an nsSVGOuterSVGFrame regardless
4919 // of whether they fail conditional processing attributes, since various
4920 // SVG frames assume that one exists. We handle the non-rendering
4921 // of failing outer <svg> element contents like <switch> statements,
4922 // and do the PassesConditionalProcessingTests call in
4923 // nsSVGOuterSVGFrame::Init.
4924 static const FrameConstructionData sOuterSVGData =
4925 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructOuterSVG);
4926 return &sOuterSVGData;
4927 }
4928
4929 if (aTag == nsGkAtoms::marker) {
4930 static const FrameConstructionData sMarkerSVGData =
4931 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructMarker);
4932 return &sMarkerSVGData;
4933 }
4934
4935 nsCOMPtr<SVGTests> tests(do_QueryInterface(aElement));
4936 if (tests && !tests->PassesConditionalProcessingTests()) {
4937 // Elements with failing conditional processing attributes never get
4938 // rendered. Note that this is not where we select which frame in a
4939 // <switch> to render! That happens in nsSVGSwitchFrame::PaintSVG.
4940 return &sContainerData;
4941 }
4942
4943 // Prevent bad frame types being children of filters or parents of filter
4944 // primitives. If aParentFrame is null, we know that the frame that will
4945 // be created will be an nsInlineFrame, so it can never be a filter.
4946 bool parentIsFilter = aParentFrame &&
4947 aParentFrame->GetType() == nsGkAtoms::svgFilterFrame;
4948 bool filterPrimitive = aElement->IsNodeOfType(nsINode::eFILTER);
4949 if ((parentIsFilter && !filterPrimitive) ||
4950 (!parentIsFilter && filterPrimitive)) {
4951 return &sSuppressData;
4952 }
4953
4954 // Prevent bad frame types being children of filter primitives or parents of
4955 // filter primitive children. If aParentFrame is null, we know that the frame
4956 // that will be created will be an nsInlineFrame, so it can never be a filter
4957 // primitive.
4958 bool parentIsFEContainerFrame = aParentFrame &&
4959 aParentFrame->GetType() == nsGkAtoms::svgFEContainerFrame;
4960 if ((parentIsFEContainerFrame && !IsFilterPrimitiveChildTag(aTag)) ||
4961 (!parentIsFEContainerFrame && IsFilterPrimitiveChildTag(aTag))) {
4962 return &sSuppressData;
4963 }
4964
4965 // Special cases for text/tspan/textPath, because the kind of frame
4966 // they get depends on the parent frame. We ignore 'a' elements when
4967 // determining the parent, however.
4968 if (aIsWithinSVGText) {
4969 // If aIsWithinSVGText is true, then we know that the "SVG text uses
4970 // CSS frames" pref was true when this SVG fragment was first constructed.
4971
4972 // We don't use ConstructInline because we want different behavior
4973 // for generated content.
4974 static const FrameConstructionData sTSpanData =
4975 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW |
4976 FCDATA_SKIP_ABSPOS_PUSH |
4977 FCDATA_DISALLOW_GENERATED_CONTENT |
4978 FCDATA_IS_LINE_PARTICIPANT |
4979 FCDATA_IS_INLINE |
4980 FCDATA_USE_CHILD_ITEMS,
4981 NS_NewInlineFrame);
4982 if (aTag == nsGkAtoms::textPath) {
4983 if (aAllowsTextPathChild) {
4984 return &sTSpanData;
4985 }
4986 } else if (aTag == nsGkAtoms::tspan ||
4987 aTag == nsGkAtoms::altGlyph ||
4988 aTag == nsGkAtoms::a) {
4989 return &sTSpanData;
4990 }
4991 return &sSuppressData;
4992 } else if (aTag == nsGkAtoms::text) {
4993 static const FrameConstructionData sTextData =
4994 FCDATA_WITH_WRAPPING_BLOCK(FCDATA_DISALLOW_OUT_OF_FLOW |
4995 FCDATA_ALLOW_BLOCK_STYLES,
4996 NS_NewSVGTextFrame,
4997 nsCSSAnonBoxes::mozSVGText);
4998 return &sTextData;
4999 } else if (aTag == nsGkAtoms::tspan ||
5000 aTag == nsGkAtoms::altGlyph ||
5001 aTag == nsGkAtoms::textPath) {
5002 return &sSuppressData;
5003 }
5004
5005 static const FrameConstructionDataByTag sSVGData[] = {
5006 SIMPLE_SVG_CREATE(svg, NS_NewSVGInnerSVGFrame),
5007 SIMPLE_SVG_CREATE(g, NS_NewSVGGFrame),
5008 SIMPLE_SVG_CREATE(svgSwitch, NS_NewSVGSwitchFrame),
5009 SIMPLE_SVG_CREATE(polygon, NS_NewSVGPathGeometryFrame),
5010 SIMPLE_SVG_CREATE(polyline, NS_NewSVGPathGeometryFrame),
5011 SIMPLE_SVG_CREATE(circle, NS_NewSVGPathGeometryFrame),
5012 SIMPLE_SVG_CREATE(ellipse, NS_NewSVGPathGeometryFrame),
5013 SIMPLE_SVG_CREATE(line, NS_NewSVGPathGeometryFrame),
5014 SIMPLE_SVG_CREATE(rect, NS_NewSVGPathGeometryFrame),
5015 SIMPLE_SVG_CREATE(path, NS_NewSVGPathGeometryFrame),
5016 SIMPLE_SVG_CREATE(defs, NS_NewSVGContainerFrame),
5017 SIMPLE_SVG_CREATE(generic_, NS_NewSVGGenericContainerFrame),
5018 { &nsGkAtoms::foreignObject,
5019 FCDATA_WITH_WRAPPING_BLOCK(FCDATA_DISALLOW_OUT_OF_FLOW,
5020 NS_NewSVGForeignObjectFrame,
5021 nsCSSAnonBoxes::mozSVGForeignContent) },
5022 SIMPLE_SVG_CREATE(a, NS_NewSVGAFrame),
5023 SIMPLE_SVG_CREATE(linearGradient, NS_NewSVGLinearGradientFrame),
5024 SIMPLE_SVG_CREATE(radialGradient, NS_NewSVGRadialGradientFrame),
5025 SIMPLE_SVG_CREATE(stop, NS_NewSVGStopFrame),
5026 SIMPLE_SVG_CREATE(use, NS_NewSVGUseFrame),
5027 SIMPLE_SVG_CREATE(view, NS_NewSVGViewFrame),
5028 SIMPLE_SVG_CREATE(image, NS_NewSVGImageFrame),
5029 SIMPLE_SVG_CREATE(clipPath, NS_NewSVGClipPathFrame),
5030 SIMPLE_SVG_CREATE(filter, NS_NewSVGFilterFrame),
5031 SIMPLE_SVG_CREATE(pattern, NS_NewSVGPatternFrame),
5032 SIMPLE_SVG_CREATE(mask, NS_NewSVGMaskFrame),
5033 SIMPLE_SVG_CREATE(feDistantLight, NS_NewSVGFEUnstyledLeafFrame),
5034 SIMPLE_SVG_CREATE(fePointLight, NS_NewSVGFEUnstyledLeafFrame),
5035 SIMPLE_SVG_CREATE(feSpotLight, NS_NewSVGFEUnstyledLeafFrame),
5036 SIMPLE_SVG_CREATE(feBlend, NS_NewSVGFELeafFrame),
5037 SIMPLE_SVG_CREATE(feColorMatrix, NS_NewSVGFELeafFrame),
5038 SIMPLE_SVG_CREATE(feFuncR, NS_NewSVGFEUnstyledLeafFrame),
5039 SIMPLE_SVG_CREATE(feFuncG, NS_NewSVGFEUnstyledLeafFrame),
5040 SIMPLE_SVG_CREATE(feFuncB, NS_NewSVGFEUnstyledLeafFrame),
5041 SIMPLE_SVG_CREATE(feFuncA, NS_NewSVGFEUnstyledLeafFrame),
5042 SIMPLE_SVG_CREATE(feComposite, NS_NewSVGFELeafFrame),
5043 SIMPLE_SVG_CREATE(feComponentTransfer, NS_NewSVGFEContainerFrame),
5044 SIMPLE_SVG_CREATE(feConvolveMatrix, NS_NewSVGFELeafFrame),
5045 SIMPLE_SVG_CREATE(feDiffuseLighting, NS_NewSVGFEContainerFrame),
5046 SIMPLE_SVG_CREATE(feDisplacementMap, NS_NewSVGFELeafFrame),
5047 SIMPLE_SVG_CREATE(feDropShadow, NS_NewSVGFELeafFrame),
5048 SIMPLE_SVG_CREATE(feFlood, NS_NewSVGFELeafFrame),
5049 SIMPLE_SVG_CREATE(feGaussianBlur, NS_NewSVGFELeafFrame),
5050 SIMPLE_SVG_CREATE(feImage, NS_NewSVGFEImageFrame),
5051 SIMPLE_SVG_CREATE(feMerge, NS_NewSVGFEContainerFrame),
5052 SIMPLE_SVG_CREATE(feMergeNode, NS_NewSVGFEUnstyledLeafFrame),
5053 SIMPLE_SVG_CREATE(feMorphology, NS_NewSVGFELeafFrame),
5054 SIMPLE_SVG_CREATE(feOffset, NS_NewSVGFELeafFrame),
5055 SIMPLE_SVG_CREATE(feSpecularLighting, NS_NewSVGFEContainerFrame),
5056 SIMPLE_SVG_CREATE(feTile, NS_NewSVGFELeafFrame),
5057 SIMPLE_SVG_CREATE(feTurbulence, NS_NewSVGFELeafFrame)
5058 };
5059
5060 const FrameConstructionData* data =
5061 FindDataByTag(aTag, aElement, aStyleContext, sSVGData,
5062 ArrayLength(sSVGData));
5063
5064 if (!data) {
5065 data = &sContainerData;
5066 }
5067
5068 return data;
5069 }
5070
5071 void
5072 nsCSSFrameConstructor::AddPageBreakItem(nsIContent* aContent,
5073 nsStyleContext* aMainStyleContext,
5074 FrameConstructionItemList& aItems)
5075 {
5076 // Use the same parent style context that |aMainStyleContext| has, since
5077 // that's easier to re-resolve and it doesn't matter in practice.
5078 // (Getting different parents can result in framechange hints, e.g.,
5079 // for user-modify.)
5080 nsRefPtr<nsStyleContext> pseudoStyle =
5081 mPresShell->StyleSet()->
5082 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::pageBreak,
5083 aMainStyleContext->GetParent());
5084
5085 NS_ASSERTION(pseudoStyle->StyleDisplay()->mDisplay ==
5086 NS_STYLE_DISPLAY_BLOCK, "Unexpected display");
5087
5088 static const FrameConstructionData sPageBreakData =
5089 FCDATA_DECL(FCDATA_SKIP_FRAMESET, NS_NewPageBreakFrame);
5090
5091 // Lie about the tag and namespace so we don't trigger anything
5092 // interesting during frame construction.
5093 aItems.AppendItem(&sPageBreakData, aContent, nsCSSAnonBoxes::pageBreak,
5094 kNameSpaceID_None, nullptr, pseudoStyle.forget(),
5095 true, nullptr);
5096 }
5097
5098 void
5099 nsCSSFrameConstructor::AddFrameConstructionItems(nsFrameConstructorState& aState,
5100 nsIContent* aContent,
5101 bool aSuppressWhiteSpaceOptimizations,
5102 nsIFrame* aParentFrame,
5103 FrameConstructionItemList& aItems)
5104 {
5105 aContent->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
5106 if (aContent->IsElement()) {
5107 // We can't just remove our pending restyle flags, since we may
5108 // have restyle-later-siblings set on us. But we _can_ remove the
5109 // "is possible restyle root" flags, and need to. Otherwise we can
5110 // end up with stale such flags (e.g. if we used to have a
5111 // display:none parent when our last restyle was posted and
5112 // processed and now no longer do).
5113 aContent->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS &
5114 ~ELEMENT_PENDING_RESTYLE_FLAGS);
5115 }
5116
5117 // XXX the GetContent() != aContent check is needed due to bug 135040.
5118 // Remove it once that's fixed.
5119 if (aContent->GetPrimaryFrame() &&
5120 aContent->GetPrimaryFrame()->GetContent() == aContent &&
5121 !aState.mCreatingExtraFrames) {
5122 NS_ERROR("asked to create frame construction item for a node that already "
5123 "has a frame");
5124 return;
5125 }
5126
5127 // don't create a whitespace frame if aParent doesn't want it
5128 if (!NeedFrameFor(aState, aParentFrame, aContent)) {
5129 return;
5130 }
5131
5132 // never create frames for comments or PIs
5133 if (aContent->IsNodeOfType(nsINode::eCOMMENT) ||
5134 aContent->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION))
5135 return;
5136
5137 nsRefPtr<nsStyleContext> styleContext;
5138 styleContext = ResolveStyleContext(aParentFrame, aContent, &aState);
5139
5140 uint32_t flags = ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK;
5141 if (aParentFrame->IsSVGText()) {
5142 flags |= ITEM_IS_WITHIN_SVG_TEXT;
5143 }
5144 if (aParentFrame->GetType() == nsGkAtoms::blockFrame &&
5145 aParentFrame->GetParent() &&
5146 aParentFrame->GetParent()->GetType() == nsGkAtoms::svgTextFrame) {
5147 flags |= ITEM_ALLOWS_TEXT_PATH_CHILD;
5148 }
5149 AddFrameConstructionItemsInternal(aState, aContent, aParentFrame,
5150 aContent->Tag(), aContent->GetNameSpaceID(),
5151 aSuppressWhiteSpaceOptimizations,
5152 styleContext,
5153 flags, nullptr,
5154 aItems);
5155 }
5156
5157 /* static */ void
5158 nsCSSFrameConstructor::SetAsUndisplayedContent(FrameConstructionItemList& aList,
5159 nsIContent* aContent,
5160 nsStyleContext* aStyleContext,
5161 bool aIsGeneratedContent)
5162 {
5163 if (aStyleContext->GetPseudo()) {
5164 if (aIsGeneratedContent) {
5165 aContent->UnbindFromTree();
5166 }
5167 return;
5168 }
5169
5170 NS_ASSERTION(!aIsGeneratedContent, "Should have had pseudo type");
5171 aList.AppendUndisplayedItem(aContent, aStyleContext);
5172 }
5173
5174 void
5175 nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState& aState,
5176 nsIContent* aContent,
5177 nsIFrame* aParentFrame,
5178 nsIAtom* aTag,
5179 int32_t aNameSpaceID,
5180 bool aSuppressWhiteSpaceOptimizations,
5181 nsStyleContext* aStyleContext,
5182 uint32_t aFlags,
5183 nsTArray<nsIAnonymousContentCreator::ContentInfo>* aAnonChildren,
5184 FrameConstructionItemList& aItems)
5185 {
5186 NS_PRECONDITION(aContent->IsNodeOfType(nsINode::eTEXT) ||
5187 aContent->IsElement(),
5188 "Shouldn't get anything else here!");
5189
5190 // The following code allows the user to specify the base tag
5191 // of an element using XBL. XUL and HTML objects (like boxes, menus, etc.)
5192 // can then be extended arbitrarily.
5193 const nsStyleDisplay* display = aStyleContext->StyleDisplay();
5194 nsRefPtr<nsStyleContext> styleContext(aStyleContext);
5195 PendingBinding* pendingBinding = nullptr;
5196 if ((aFlags & ITEM_ALLOW_XBL_BASE) && display->mBinding)
5197 {
5198 // Ensure that our XBL bindings are installed.
5199
5200 nsXBLService* xblService = nsXBLService::GetInstance();
5201 if (!xblService)
5202 return;
5203
5204 bool resolveStyle;
5205
5206 nsAutoPtr<PendingBinding> newPendingBinding(new PendingBinding());
5207
5208 nsresult rv = xblService->LoadBindings(aContent, display->mBinding->GetURI(),
5209 display->mBinding->mOriginPrincipal,
5210 getter_AddRefs(newPendingBinding->mBinding),
5211 &resolveStyle);
5212 if (NS_FAILED(rv) && rv != NS_ERROR_XBL_BLOCKED)
5213 return;
5214
5215 if (newPendingBinding->mBinding) {
5216 pendingBinding = newPendingBinding;
5217 // aState takes over owning newPendingBinding
5218 aState.AddPendingBinding(newPendingBinding.forget());
5219 }
5220
5221 if (resolveStyle) {
5222 styleContext =
5223 ResolveStyleContext(styleContext->GetParent(), aContent, &aState);
5224 display = styleContext->StyleDisplay();
5225 aStyleContext = styleContext;
5226 }
5227
5228 aTag = mDocument->BindingManager()->ResolveTag(aContent, &aNameSpaceID);
5229 }
5230
5231 bool isGeneratedContent = ((aFlags & ITEM_IS_GENERATED_CONTENT) != 0);
5232
5233 // Pre-check for display "none" - if we find that, don't create
5234 // any frame at all
5235 if (NS_STYLE_DISPLAY_NONE == display->mDisplay) {
5236 SetAsUndisplayedContent(aItems, aContent, styleContext, isGeneratedContent);
5237 return;
5238 }
5239
5240 bool isText = !aContent->IsElement();
5241
5242 // never create frames for non-option/optgroup kids of <select> and
5243 // non-option kids of <optgroup> inside a <select>.
5244 // XXXbz it's not clear how this should best work with XBL.
5245 nsIContent *parent = aContent->GetParent();
5246 if (parent) {
5247 // Check tag first, since that check will usually fail
5248 nsIAtom* parentTag = parent->Tag();
5249 if ((parentTag == nsGkAtoms::select || parentTag == nsGkAtoms::optgroup) &&
5250 parent->IsHTML() &&
5251 // <option> is ok no matter what
5252 !aContent->IsHTML(nsGkAtoms::option) &&
5253 // <optgroup> is OK in <select> but not in <optgroup>
5254 (!aContent->IsHTML(nsGkAtoms::optgroup) ||
5255 parentTag != nsGkAtoms::select) &&
5256 // Allow native anonymous content no matter what
5257 !aContent->IsRootOfNativeAnonymousSubtree()) {
5258 // No frame for aContent
5259 if (!isText) {
5260 SetAsUndisplayedContent(aItems, aContent, styleContext,
5261 isGeneratedContent);
5262 }
5263 return;
5264 }
5265 }
5266
5267 bool isPopup = false;
5268 // Try to find frame construction data for this content
5269 const FrameConstructionData* data;
5270 if (isText) {
5271 data = FindTextData(aParentFrame);
5272 if (!data) {
5273 // Nothing to do here; suppressed text inside SVG
5274 return;
5275 }
5276 } else {
5277 Element* element = aContent->AsElement();
5278
5279 // Don't create frames for non-SVG element children of SVG elements.
5280 if (aNameSpaceID != kNameSpaceID_SVG &&
5281 ((aParentFrame &&
5282 IsFrameForSVG(aParentFrame) &&
5283 !aParentFrame->IsFrameOfType(nsIFrame::eSVGForeignObject)) ||
5284 (aFlags & ITEM_IS_WITHIN_SVG_TEXT))) {
5285 SetAsUndisplayedContent(aItems, element, styleContext,
5286 isGeneratedContent);
5287 return;
5288 }
5289
5290 data = FindHTMLData(element, aTag, aNameSpaceID, aParentFrame,
5291 styleContext);
5292 if (!data) {
5293 data = FindXULTagData(element, aTag, aNameSpaceID, styleContext);
5294 }
5295 if (!data) {
5296 data = FindMathMLData(element, aTag, aNameSpaceID, styleContext);
5297 }
5298 if (!data) {
5299 data = FindSVGData(element, aTag, aNameSpaceID, aParentFrame,
5300 aFlags & ITEM_IS_WITHIN_SVG_TEXT,
5301 aFlags & ITEM_ALLOWS_TEXT_PATH_CHILD,
5302 styleContext);
5303 }
5304
5305 // Now check for XUL display types
5306 if (!data) {
5307 data = FindXULDisplayData(display, element, styleContext);
5308 }
5309
5310 // And general display types
5311 if (!data) {
5312 data = FindDisplayData(display, element, aParentFrame, styleContext);
5313 }
5314
5315 NS_ASSERTION(data, "Should have frame construction data now");
5316
5317 if (data->mBits & FCDATA_SUPPRESS_FRAME) {
5318 SetAsUndisplayedContent(aItems, element, styleContext, isGeneratedContent);
5319 return;
5320 }
5321
5322 #ifdef MOZ_XUL
5323 if ((data->mBits & FCDATA_IS_POPUP) &&
5324 (!aParentFrame || // Parent is inline
5325 aParentFrame->GetType() != nsGkAtoms::menuFrame)) {
5326 if (!aState.mPopupItems.containingBlock &&
5327 !aState.mHavePendingPopupgroup) {
5328 SetAsUndisplayedContent(aItems, element, styleContext,
5329 isGeneratedContent);
5330 return;
5331 }
5332
5333 isPopup = true;
5334 }
5335 #endif /* MOZ_XUL */
5336 }
5337
5338 uint32_t bits = data->mBits;
5339
5340 // Inside colgroups, suppress everything except columns.
5341 if (aParentFrame &&
5342 aParentFrame->GetType() == nsGkAtoms::tableColGroupFrame &&
5343 (!(bits & FCDATA_IS_TABLE_PART) ||
5344 display->mDisplay != NS_STYLE_DISPLAY_TABLE_COLUMN)) {
5345 SetAsUndisplayedContent(aItems, aContent, styleContext, isGeneratedContent);
5346 return;
5347 }
5348
5349 bool canHavePageBreak =
5350 (aFlags & ITEM_ALLOW_PAGE_BREAK) &&
5351 aState.mPresContext->IsPaginated() &&
5352 !display->IsAbsolutelyPositionedStyle() &&
5353 !(bits & FCDATA_IS_TABLE_PART) &&
5354 !(bits & FCDATA_IS_SVG_TEXT);
5355
5356 if (canHavePageBreak && display->mBreakBefore) {
5357 AddPageBreakItem(aContent, aStyleContext, aItems);
5358 }
5359
5360 FrameConstructionItem* item =
5361 aItems.AppendItem(data, aContent, aTag, aNameSpaceID,
5362 pendingBinding, styleContext.forget(),
5363 aSuppressWhiteSpaceOptimizations, aAnonChildren);
5364 if (!item) {
5365 if (isGeneratedContent) {
5366 aContent->UnbindFromTree();
5367 }
5368 return;
5369 }
5370
5371 item->mIsText = isText;
5372 item->mIsGeneratedContent = isGeneratedContent;
5373 item->mIsAnonymousContentCreatorContent =
5374 aFlags & ITEM_IS_ANONYMOUSCONTENTCREATOR_CONTENT;
5375 if (isGeneratedContent) {
5376 NS_ADDREF(item->mContent);
5377 }
5378 item->mIsRootPopupgroup =
5379 aNameSpaceID == kNameSpaceID_XUL && aTag == nsGkAtoms::popupgroup &&
5380 aContent->IsRootOfNativeAnonymousSubtree();
5381 if (item->mIsRootPopupgroup) {
5382 aState.mHavePendingPopupgroup = true;
5383 }
5384 item->mIsPopup = isPopup;
5385 item->mIsForSVGAElement = aNameSpaceID == kNameSpaceID_SVG &&
5386 aTag == nsGkAtoms::a;
5387
5388 if (canHavePageBreak && display->mBreakAfter) {
5389 AddPageBreakItem(aContent, aStyleContext, aItems);
5390 }
5391
5392 if (bits & FCDATA_IS_INLINE) {
5393 // To correctly set item->mIsAllInline we need to build up our child items
5394 // right now.
5395 BuildInlineChildItems(aState, *item,
5396 aFlags & ITEM_IS_WITHIN_SVG_TEXT,
5397 aFlags & ITEM_ALLOWS_TEXT_PATH_CHILD);
5398 item->mHasInlineEnds = true;
5399 item->mIsBlock = false;
5400 } else {
5401 // Compute a boolean isInline which is guaranteed to be false for blocks
5402 // (but may also be false for some inlines).
5403 bool isInline =
5404 // Table-internal things are inline-outside if and only if they're kids of
5405 // inlines, since they'll trigger construction of inline-table
5406 // pseudos.
5407 ((bits & FCDATA_IS_TABLE_PART) &&
5408 (!aParentFrame || // No aParentFrame means inline
5409 aParentFrame->StyleDisplay()->mDisplay == NS_STYLE_DISPLAY_INLINE)) ||
5410 // Things that are inline-outside but aren't inline frames are inline
5411 display->IsInlineOutsideStyle() ||
5412 // Popups that are certainly out of flow.
5413 isPopup;
5414
5415 // Set mIsAllInline conservatively. It just might be that even an inline
5416 // that has mIsAllInline false doesn't need an {ib} split. So this is just
5417 // an optimization to keep from doing too much work in cases when we can
5418 // show that mIsAllInline is true..
5419 item->mIsAllInline = item->mHasInlineEnds = isInline ||
5420 // Figure out whether we're guaranteed this item will be out of flow.
5421 // This is not a precise test, since one of our ancestor inlines might add
5422 // an absolute containing block (if it's relatively positioned) when there
5423 // wasn't such a containing block before. But it's conservative in the
5424 // sense that anything that will really end up as an in-flow non-inline
5425 // will test false here. In other words, if this test is true we're
5426 // guaranteed to be inline; if it's false we don't know what we'll end up
5427 // as.
5428 //
5429 // If we make this test precise, we can remove some of the code dealing
5430 // with the imprecision in ConstructInline and adjust the comments on
5431 // mIsAllInline and mIsBlock in the header. And probably remove mIsBlock
5432 // altogether, since then it will always be equal to !mHasInlineEnds.
5433 (!(bits & FCDATA_DISALLOW_OUT_OF_FLOW) &&
5434 aState.GetGeometricParent(display, nullptr));
5435
5436 // Set mIsBlock conservatively. It's OK to set it false for some real
5437 // blocks, but not OK to set it true for things that aren't blocks. Since
5438 // isOutOfFlow might be false even in cases when the frame will end up
5439 // out-of-flow, we can't use it here. But we _can_ say that the frame will
5440 // for sure end up in-flow if it's not floated or absolutely positioned.
5441 item->mIsBlock = !isInline &&
5442 !display->IsAbsolutelyPositionedStyle() &&
5443 !display->IsFloatingStyle() &&
5444 !(bits & FCDATA_IS_SVG_TEXT);
5445 }
5446
5447 if (item->mIsAllInline) {
5448 aItems.InlineItemAdded();
5449 } else if (item->mIsBlock) {
5450 aItems.BlockItemAdded();
5451 }
5452
5453 // Our item should be treated as a line participant if we have the relevant
5454 // bit and are going to be in-flow. Note that this really only matters if
5455 // our ancestor is a box or some such, so the fact that we might have an
5456 // inline ancestor that might become a containing block is not relevant here.
5457 if ((bits & FCDATA_IS_LINE_PARTICIPANT) &&
5458 ((bits & FCDATA_DISALLOW_OUT_OF_FLOW) ||
5459 !aState.GetGeometricParent(display, nullptr))) {
5460 item->mIsLineParticipant = true;
5461 aItems.LineParticipantItemAdded();
5462 }
5463 }
5464
5465 static void
5466 DestroyContent(void* aPropertyValue)
5467 {
5468 nsIContent* content = static_cast<nsIContent*>(aPropertyValue);
5469 content->UnbindFromTree();
5470 NS_RELEASE(content);
5471 }
5472
5473 NS_DECLARE_FRAME_PROPERTY(BeforeProperty, DestroyContent)
5474 NS_DECLARE_FRAME_PROPERTY(AfterProperty, DestroyContent)
5475
5476 static const FramePropertyDescriptor*
5477 GenConPseudoToProperty(nsIAtom* aPseudo)
5478 {
5479 NS_ASSERTION(aPseudo == nsCSSPseudoElements::before ||
5480 aPseudo == nsCSSPseudoElements::after,
5481 "Bad gen-con pseudo");
5482 return aPseudo == nsCSSPseudoElements::before ? BeforeProperty()
5483 : AfterProperty();
5484 }
5485
5486 /**
5487 * Return true if the frame construction item pointed to by aIter will
5488 * create a frame adjacent to a line boundary in the frame tree, and that
5489 * line boundary is induced by a content node adjacent to the frame's
5490 * content node in the content tree. The latter condition is necessary so
5491 * that ContentAppended/ContentInserted/ContentRemoved can easily find any
5492 * text nodes that were suppressed here.
5493 */
5494 bool
5495 nsCSSFrameConstructor::AtLineBoundary(FCItemIterator& aIter)
5496 {
5497 if (aIter.item().mSuppressWhiteSpaceOptimizations) {
5498 return false;
5499 }
5500
5501 if (aIter.AtStart()) {
5502 if (aIter.List()->HasLineBoundaryAtStart() &&
5503 !aIter.item().mContent->GetPreviousSibling())
5504 return true;
5505 } else {
5506 FCItemIterator prev = aIter;
5507 prev.Prev();
5508 if (prev.item().IsLineBoundary() &&
5509 !prev.item().mSuppressWhiteSpaceOptimizations &&
5510 aIter.item().mContent->GetPreviousSibling() == prev.item().mContent)
5511 return true;
5512 }
5513
5514 FCItemIterator next = aIter;
5515 next.Next();
5516 if (next.IsDone()) {
5517 if (aIter.List()->HasLineBoundaryAtEnd() &&
5518 !aIter.item().mContent->GetNextSibling())
5519 return true;
5520 } else {
5521 if (next.item().IsLineBoundary() &&
5522 !next.item().mSuppressWhiteSpaceOptimizations &&
5523 aIter.item().mContent->GetNextSibling() == next.item().mContent)
5524 return true;
5525 }
5526
5527 return false;
5528 }
5529
5530 void
5531 nsCSSFrameConstructor::ConstructFramesFromItem(nsFrameConstructorState& aState,
5532 FCItemIterator& aIter,
5533 nsIFrame* aParentFrame,
5534 nsFrameItems& aFrameItems)
5535 {
5536 nsIFrame* adjParentFrame = aParentFrame;
5537 FrameConstructionItem& item = aIter.item();
5538 nsStyleContext* styleContext = item.mStyleContext;
5539 AdjustParentFrame(adjParentFrame, item.mFCData, styleContext);
5540
5541 if (item.mIsText) {
5542 // If this is collapsible whitespace next to a line boundary,
5543 // don't create a frame. item.IsWhitespace() also sets the
5544 // NS_CREATE_FRAME_IF_NON_WHITESPACE flag in the text node. (If we
5545 // end up creating a frame, nsTextFrame::Init will clear the flag.)
5546 // We don't do this for generated content, because some generated
5547 // text content is empty text nodes that are about to be initialized.
5548 // (We check mAdditionalStateBits because only the generated content
5549 // container's frame construction item is marked with
5550 // mIsGeneratedContent, and we might not have an aParentFrame.)
5551 // We don't do it for content that may have XBL anonymous siblings,
5552 // because they make it difficult to correctly create the frame
5553 // due to dynamic changes.
5554 // We don't do it for SVG text, since we might need to position and
5555 // measure the white space glyphs due to x/y/dx/dy attributes.
5556 if (AtLineBoundary(aIter) &&
5557 !styleContext->StyleText()->WhiteSpaceOrNewlineIsSignificant() &&
5558 aIter.List()->ParentHasNoXBLChildren() &&
5559 !(aState.mAdditionalStateBits & NS_FRAME_GENERATED_CONTENT) &&
5560 (item.mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) &&
5561 !(item.mFCData->mBits & FCDATA_IS_SVG_TEXT) &&
5562 !mAlwaysCreateFramesForIgnorableWhitespace &&
5563 item.IsWhitespace(aState))
5564 return;
5565
5566 ConstructTextFrame(item.mFCData, aState, item.mContent,
5567 adjParentFrame, styleContext,
5568 aFrameItems);
5569 return;
5570 }
5571
5572 // Start background loads during frame construction so that we're
5573 // guaranteed that they will be started before onload fires.
5574 styleContext->StartBackgroundImageLoads();
5575
5576 nsFrameState savedStateBits = aState.mAdditionalStateBits;
5577 if (item.mIsGeneratedContent) {
5578 // Ensure that frames created here are all tagged with
5579 // NS_FRAME_GENERATED_CONTENT.
5580 aState.mAdditionalStateBits |= NS_FRAME_GENERATED_CONTENT;
5581
5582 // Note that we're not necessarily setting this property on the primary
5583 // frame for the content for which this is generated content. We might be
5584 // setting it on a table pseudo-frame inserted under that instead. That's
5585 // OK, though; we just need to do the property set so that the content will
5586 // get cleaned up when the frame is destroyed.
5587 aParentFrame->Properties().Set(GenConPseudoToProperty(styleContext->GetPseudo()),
5588 item.mContent);
5589
5590 // Now that we've passed ownership of item.mContent to the frame, unset
5591 // our generated content flag so we don't release or unbind it ourselves.
5592 item.mIsGeneratedContent = false;
5593 }
5594
5595 // XXXbz maybe just inline ConstructFrameFromItemInternal here or something?
5596 ConstructFrameFromItemInternal(item, aState, adjParentFrame, aFrameItems);
5597
5598 aState.mAdditionalStateBits = savedStateBits;
5599 }
5600
5601
5602 inline bool
5603 IsRootBoxFrame(nsIFrame *aFrame)
5604 {
5605 return (aFrame->GetType() == nsGkAtoms::rootFrame);
5606 }
5607
5608 nsresult
5609 nsCSSFrameConstructor::ReconstructDocElementHierarchy()
5610 {
5611 Element* rootElement = mDocument->GetRootElement();
5612 if (!rootElement) {
5613 /* nothing to do */
5614 return NS_OK;
5615 }
5616 return RecreateFramesForContent(rootElement, false);
5617 }
5618
5619 nsIFrame*
5620 nsCSSFrameConstructor::GetFrameFor(nsIContent* aContent)
5621 {
5622 // Get the primary frame associated with the content
5623 nsIFrame* frame = aContent->GetPrimaryFrame();
5624
5625 if (!frame)
5626 return nullptr;
5627
5628 // If the content of the frame is not the desired content then this is not
5629 // really a frame for the desired content.
5630 // XXX This check is needed due to bug 135040. Remove it once that's fixed.
5631 if (frame->GetContent() != aContent) {
5632 return nullptr;
5633 }
5634
5635 nsIFrame* insertionFrame = frame->GetContentInsertionFrame();
5636
5637 NS_ASSERTION(insertionFrame == frame || !frame->IsLeaf(),
5638 "The insertion frame is the primary frame or the primary frame isn't a leaf");
5639
5640 return insertionFrame;
5641 }
5642
5643 nsIFrame*
5644 nsCSSFrameConstructor::GetAbsoluteContainingBlock(nsIFrame* aFrame,
5645 ContainingBlockType aType)
5646 {
5647 NS_PRECONDITION(nullptr != mRootElementFrame, "no root element frame");
5648
5649 // Starting with aFrame, look for a frame that is absolutely positioned or
5650 // relatively positioned (and transformed, if aType is FIXED)
5651 for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
5652 if (frame->IsFrameOfType(nsIFrame::eMathML)) {
5653 // If it's mathml, bail out -- no absolute positioning out from inside
5654 // mathml frames. Note that we don't make this part of the loop
5655 // condition because of the stuff at the end of this method...
5656 return nullptr;
5657 }
5658
5659 // If the frame is positioned, we will probably return it as the containing
5660 // block (see the exceptions below). Otherwise, we'll start looking at the
5661 // parent frame, unless we're dealing with a scrollframe.
5662 // Scrollframes are special since they're not positioned, but their
5663 // scrolledframe might be. So, we need to check this special case to return
5664 // the correct containing block (the scrolledframe) in that case.
5665 // If we're looking for a fixed-pos containing block and the frame is
5666 // not transformed, skip it.
5667 if (!frame->IsPositioned() ||
5668 (aType == FIXED_POS &&
5669 !frame->StyleDisplay()->HasTransform(frame) &&
5670 !frame->StyleDisplay()->HasPerspectiveStyle())) {
5671 continue;
5672 }
5673 nsIFrame* absPosCBCandidate = frame;
5674 nsIAtom* type = absPosCBCandidate->GetType();
5675 if (type == nsGkAtoms::fieldSetFrame) {
5676 absPosCBCandidate = static_cast<nsFieldSetFrame*>(absPosCBCandidate)->GetInner();
5677 if (!absPosCBCandidate) {
5678 continue;
5679 }
5680 type = absPosCBCandidate->GetType();
5681 }
5682 if (type == nsGkAtoms::scrollFrame) {
5683 nsIScrollableFrame* scrollFrame = do_QueryFrame(absPosCBCandidate);
5684 absPosCBCandidate = scrollFrame->GetScrolledFrame();
5685 if (!absPosCBCandidate) {
5686 continue;
5687 }
5688 type = absPosCBCandidate->GetType();
5689 }
5690 // Only first continuations can be containing blocks.
5691 absPosCBCandidate = absPosCBCandidate->FirstContinuation();
5692 // Is the frame really an absolute container?
5693 if (!absPosCBCandidate->IsAbsoluteContainer()) {
5694 continue;
5695 }
5696
5697 // For tables, skip the inner frame and consider the outer table frame.
5698 if (type == nsGkAtoms::tableFrame) {
5699 continue;
5700 }
5701 // For outer table frames, we can just return absPosCBCandidate.
5702 return absPosCBCandidate;
5703 }
5704
5705 // It is possible for the search for the containing block to fail, because
5706 // no absolute container can be found in the parent chain. In those cases,
5707 // we fall back to the document element's containing block.
5708 if (aType == FIXED_POS) {
5709 return mFixedContainingBlock;
5710 }
5711 return mHasRootAbsPosContainingBlock ? mDocElementContainingBlock : nullptr;
5712 }
5713
5714 nsIFrame*
5715 nsCSSFrameConstructor::GetFloatContainingBlock(nsIFrame* aFrame)
5716 {
5717 // Starting with aFrame, look for a frame that is a float containing block.
5718 // IF we hit a mathml frame, bail out; we don't allow floating out of mathml
5719 // frames, because they don't seem to be able to deal.
5720 // The logic here needs to match the logic in ProcessChildren()
5721 for (nsIFrame* containingBlock = aFrame;
5722 containingBlock &&
5723 !ShouldSuppressFloatingOfDescendants(containingBlock);
5724 containingBlock = containingBlock->GetParent()) {
5725 if (containingBlock->IsFloatContainingBlock()) {
5726 return containingBlock;
5727 }
5728 }
5729
5730 // If we didn't find a containing block, then there just isn't
5731 // one.... return null
5732 return nullptr;
5733 }
5734
5735 /**
5736 * This function will check whether aContainer has :after generated content.
5737 * If so, appending to it should actually insert. The return value is the
5738 * parent to use for newly-appended content. *aAfterFrame points to the :after
5739 * frame before which appended content should go, if there is one.
5740 */
5741 static nsIFrame*
5742 AdjustAppendParentForAfterContent(nsPresContext* aPresContext,
5743 nsIContent* aContainer,
5744 nsIFrame* aParentFrame,
5745 nsIFrame** aAfterFrame)
5746 {
5747 // See if the parent has an :after pseudo-element. Check for the presence
5748 // of style first, since nsLayoutUtils::GetAfterFrame is sorta expensive.
5749 nsStyleContext* parentStyle = aParentFrame->StyleContext();
5750 if (nsLayoutUtils::HasPseudoStyle(aContainer, parentStyle,
5751 nsCSSPseudoElements::ePseudo_after,
5752 aPresContext)) {
5753 // Ensure that the :after frame is on the principal child list.
5754 aParentFrame->DrainSelfOverflowList();
5755
5756 nsIFrame* afterFrame = nsLayoutUtils::GetAfterFrame(aParentFrame);
5757 if (afterFrame) {
5758 *aAfterFrame = afterFrame;
5759 return afterFrame->GetParent();
5760 }
5761 }
5762
5763 *aAfterFrame = nullptr;
5764
5765 if (IsFramePartOfIBSplit(aParentFrame)) {
5766 // We might be in a situation where the last part of the {ib} split was
5767 // empty. Since we have no ::after pseudo-element, we do in fact want to be
5768 // appending to that last part, so advance to it if needed. Note that here
5769 // aParentFrame is the result of a GetLastIBSplitSibling call, so must be
5770 // either the last or next to last ib-split sibling.
5771 nsIFrame* trailingInline = GetIBSplitSibling(aParentFrame);
5772 if (trailingInline) {
5773 aParentFrame = trailingInline;
5774 }
5775
5776 // Always make sure to look at the last continuation of the frame
5777 // for the {ib} case, even if that continuation is empty. We
5778 // don't do this for the non-ib-split-frame case, since in the
5779 // other cases appending to the last nonempty continuation is fine
5780 // and in fact not doing that can confuse code that doesn't know
5781 // to pull kids from continuations other than its next one.
5782 aParentFrame = aParentFrame->LastContinuation();
5783 }
5784
5785 return aParentFrame;
5786 }
5787
5788 /**
5789 * This function will get the previous sibling to use for an append operation.
5790 * it takes a parent frame (must not be null) and its :after frame (may be
5791 * null).
5792 */
5793 static nsIFrame*
5794 FindAppendPrevSibling(nsIFrame* aParentFrame, nsIFrame* aAfterFrame)
5795 {
5796 if (aAfterFrame) {
5797 NS_ASSERTION(aAfterFrame->GetParent() == aParentFrame, "Wrong parent");
5798 NS_ASSERTION(aAfterFrame->GetPrevSibling() ||
5799 aParentFrame->GetFirstPrincipalChild() == aAfterFrame,
5800 ":after frame must be on the principal child list here");
5801 return aAfterFrame->GetPrevSibling();
5802 }
5803
5804 aParentFrame->DrainSelfOverflowList();
5805
5806 return aParentFrame->GetLastChild(kPrincipalList);
5807 }
5808
5809 /**
5810 * This function will get the next sibling for a frame insert operation given
5811 * the parent and previous sibling. aPrevSibling may be null.
5812 */
5813 static nsIFrame*
5814 GetInsertNextSibling(nsIFrame* aParentFrame, nsIFrame* aPrevSibling)
5815 {
5816 if (aPrevSibling) {
5817 return aPrevSibling->GetNextSibling();
5818 }
5819
5820 return aParentFrame->GetFirstPrincipalChild();
5821 }
5822
5823 /**
5824 * This function is called by ContentAppended() and ContentInserted() when
5825 * appending flowed frames to a parent's principal child list. It handles the
5826 * case where the parent is the trailing inline of an {ib} split.
5827 */
5828 nsresult
5829 nsCSSFrameConstructor::AppendFramesToParent(nsFrameConstructorState& aState,
5830 nsIFrame* aParentFrame,
5831 nsFrameItems& aFrameList,
5832 nsIFrame* aPrevSibling,
5833 bool aIsRecursiveCall)
5834 {
5835 NS_PRECONDITION(!IsFramePartOfIBSplit(aParentFrame) ||
5836 !GetIBSplitSibling(aParentFrame) ||
5837 !GetIBSplitSibling(aParentFrame)->GetFirstPrincipalChild(),
5838 "aParentFrame has a ib-split sibling with kids?");
5839 NS_PRECONDITION(!aPrevSibling || aPrevSibling->GetParent() == aParentFrame,
5840 "Parent and prevsibling don't match");
5841
5842 nsIFrame* nextSibling = ::GetInsertNextSibling(aParentFrame, aPrevSibling);
5843
5844 NS_ASSERTION(nextSibling ||
5845 !aParentFrame->GetNextContinuation() ||
5846 !aParentFrame->GetNextContinuation()->GetFirstPrincipalChild() ||
5847 aIsRecursiveCall,
5848 "aParentFrame has later continuations with kids?");
5849 NS_ASSERTION(nextSibling ||
5850 !IsFramePartOfIBSplit(aParentFrame) ||
5851 (IsInlineFrame(aParentFrame) &&
5852 !GetIBSplitSibling(aParentFrame) &&
5853 !aParentFrame->GetNextContinuation()) ||
5854 aIsRecursiveCall,
5855 "aParentFrame is not last?");
5856
5857 // If we're inserting a list of frames at the end of the trailing inline
5858 // of an {ib} split, we may need to create additional {ib} siblings to parent
5859 // them.
5860 if (!nextSibling && IsFramePartOfIBSplit(aParentFrame)) {
5861 // When we get here, our frame list might start with a block. If it does
5862 // so, and aParentFrame is an inline, and it and all its previous
5863 // continuations have no siblings, then put the initial blocks from the
5864 // frame list into the previous block of the {ib} split. Note that we
5865 // didn't want to stop at the block part of the split when figuring out
5866 // initial parent, because that could screw up float parenting; it's easier
5867 // to do this little fixup here instead.
5868 if (aFrameList.NotEmpty() && !aFrameList.FirstChild()->IsInlineOutside()) {
5869 // See whether our trailing inline is empty
5870 nsIFrame* firstContinuation = aParentFrame->FirstContinuation();
5871 if (firstContinuation->PrincipalChildList().IsEmpty()) {
5872 // Our trailing inline is empty. Collect our starting blocks from
5873 // aFrameList, get the right parent frame for them, and put them in.
5874 nsFrameList::FrameLinkEnumerator firstNonBlockEnumerator =
5875 FindFirstNonBlock(aFrameList);
5876 nsFrameList blockKids = aFrameList.ExtractHead(firstNonBlockEnumerator);
5877 NS_ASSERTION(blockKids.NotEmpty(), "No blocks?");
5878
5879 nsIFrame* prevBlock =
5880 GetIBSplitPrevSibling(firstContinuation)->LastContinuation();
5881 NS_ASSERTION(prevBlock, "Should have previous block here");
5882
5883 MoveChildrenTo(aState.mPresContext, aParentFrame, prevBlock, blockKids);
5884 }
5885 }
5886
5887 // We want to put some of the frames into this inline frame.
5888 nsFrameList::FrameLinkEnumerator firstBlockEnumerator(aFrameList);
5889 FindFirstBlock(firstBlockEnumerator);
5890
5891 nsFrameList inlineKids = aFrameList.ExtractHead(firstBlockEnumerator);
5892 if (!inlineKids.IsEmpty()) {
5893 AppendFrames(aParentFrame, kPrincipalList, inlineKids);
5894 }
5895
5896 if (!aFrameList.IsEmpty()) {
5897 bool positioned = aParentFrame->IsRelativelyPositioned();
5898 nsFrameItems ibSiblings;
5899 CreateIBSiblings(aState, aParentFrame, positioned, aFrameList,
5900 ibSiblings);
5901
5902 // Make sure to trigger reflow of the inline that used to be our
5903 // last one and now isn't anymore, since its GetSkipSides() has
5904 // changed.
5905 mPresShell->FrameNeedsReflow(aParentFrame,
5906 nsIPresShell::eTreeChange,
5907 NS_FRAME_HAS_DIRTY_CHILDREN);
5908
5909 // Recurse so we create new ib siblings as needed for aParentFrame's parent
5910 return AppendFramesToParent(aState, aParentFrame->GetParent(), ibSiblings,
5911 aParentFrame, true);
5912 }
5913
5914 return NS_OK;
5915 }
5916
5917 // Insert the frames after our aPrevSibling
5918 return InsertFrames(aParentFrame, kPrincipalList, aPrevSibling, aFrameList);
5919 }
5920
5921 #define UNSET_DISPLAY 255
5922
5923 // This gets called to see if the frames corresponding to aSibling and aContent
5924 // should be siblings in the frame tree. Although (1) rows and cols, (2) row
5925 // groups and col groups, (3) row groups and captions, (4) legends and content
5926 // inside fieldsets, (5) popups and other kids of the menu are siblings from a
5927 // content perspective, they are not considered siblings in the frame tree.
5928 bool
5929 nsCSSFrameConstructor::IsValidSibling(nsIFrame* aSibling,
5930 nsIContent* aContent,
5931 uint8_t& aDisplay)
5932 {
5933 nsIFrame* parentFrame = aSibling->GetParent();
5934 nsIAtom* parentType = nullptr;
5935 if (parentFrame) {
5936 parentType = parentFrame->GetType();
5937 }
5938
5939 uint8_t siblingDisplay = aSibling->GetDisplay();
5940 if ((NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == siblingDisplay) ||
5941 (NS_STYLE_DISPLAY_TABLE_COLUMN == siblingDisplay) ||
5942 (NS_STYLE_DISPLAY_TABLE_CAPTION == siblingDisplay) ||
5943 (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == siblingDisplay) ||
5944 (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == siblingDisplay) ||
5945 (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == siblingDisplay) ||
5946 nsGkAtoms::menuFrame == parentType) {
5947 // if we haven't already, construct a style context to find the display type of aContent
5948 if (UNSET_DISPLAY == aDisplay) {
5949 nsRefPtr<nsStyleContext> styleContext;
5950 nsIFrame* styleParent = aSibling->GetParentStyleContextFrame();
5951 if (!styleParent) {
5952 NS_NOTREACHED("Shouldn't happen");
5953 return false;
5954 }
5955 // XXXbz when this code is killed, the state argument to
5956 // ResolveStyleContext can be made non-optional.
5957 styleContext = ResolveStyleContext(styleParent, aContent, nullptr);
5958 const nsStyleDisplay* display = styleContext->StyleDisplay();
5959 aDisplay = display->mDisplay;
5960 }
5961 if (nsGkAtoms::menuFrame == parentType) {
5962 return
5963 (NS_STYLE_DISPLAY_POPUP == aDisplay) ==
5964 (NS_STYLE_DISPLAY_POPUP == siblingDisplay);
5965 }
5966 // To have decent performance we want to return false in cases in which
5967 // reordering the two siblings has no effect on display. To ensure
5968 // correctness, we MUST return false in cases where the two siblings have
5969 // the same desired parent type and live on different display lists.
5970 // Specificaly, columns and column groups should only consider columns and
5971 // column groups as valid siblings. Captions should only consider other
5972 // captions. All other things should consider each other as valid
5973 // siblings. The restriction in the |if| above on siblingDisplay is ok,
5974 // because for correctness the only part that really needs to happen is to
5975 // not consider captions, column groups, and row/header/footer groups
5976 // siblings of each other. Treating a column or colgroup as a valid
5977 // sibling of a non-table-related frame will just mean we end up reframing.
5978 if ((siblingDisplay == NS_STYLE_DISPLAY_TABLE_CAPTION) !=
5979 (aDisplay == NS_STYLE_DISPLAY_TABLE_CAPTION)) {
5980 // One's a caption and the other is not. Not valid siblings.
5981 return false;
5982 }
5983
5984 if ((siblingDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP ||
5985 siblingDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN) !=
5986 (aDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP ||
5987 aDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN)) {
5988 // One's a column or column group and the other is not. Not valid
5989 // siblings.
5990 return false;
5991 }
5992 // Fall through; it's possible that the display type was overridden and
5993 // a different sort of frame was constructed, so we may need to return false
5994 // below.
5995 }
5996
5997 if (IsFrameForFieldSet(parentFrame, parentType)) {
5998 // Legends can be sibling of legends but not of other content in the fieldset
5999 nsIAtom* sibType = aSibling->GetContentInsertionFrame()->GetType();
6000 bool legendContent = aContent->IsHTML(nsGkAtoms::legend);
6001
6002 if ((legendContent && (nsGkAtoms::legendFrame != sibType)) ||
6003 (!legendContent && (nsGkAtoms::legendFrame == sibType)))
6004 return false;
6005 }
6006
6007 return true;
6008 }
6009
6010 nsIFrame*
6011 nsCSSFrameConstructor::FindFrameForContentSibling(nsIContent* aContent,
6012 nsIContent* aTargetContent,
6013 uint8_t& aTargetContentDisplay,
6014 bool aPrevSibling)
6015 {
6016 nsIFrame* sibling = aContent->GetPrimaryFrame();
6017 if (!sibling || sibling->GetContent() != aContent) {
6018 // XXX the GetContent() != aContent check is needed due to bug 135040.
6019 // Remove it once that's fixed.
6020 return nullptr;
6021 }
6022
6023 // If the frame is out-of-flow, GetPrimaryFrame() will have returned the
6024 // out-of-flow frame; we want the placeholder.
6025 if (sibling->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
6026 nsIFrame* placeholderFrame = GetPlaceholderFrameFor(sibling);
6027 NS_ASSERTION(placeholderFrame, "no placeholder for out-of-flow frame");
6028 sibling = placeholderFrame;
6029 }
6030
6031 // The frame we have now should never be a continuation
6032 NS_ASSERTION(!sibling->GetPrevContinuation(), "How did that happen?");
6033
6034 if (aPrevSibling) {
6035 // The frame may be a ib-split frame (a split inline frame that
6036 // contains a block). Get the last part of that split.
6037 if (IsFramePartOfIBSplit(sibling)) {
6038 sibling = GetLastIBSplitSibling(sibling, true);
6039 }
6040
6041 // The frame may have a continuation. If so, we want the last
6042 // non-overflow-container continuation as our previous sibling.
6043 sibling = sibling->GetTailContinuation();
6044 }
6045
6046 if (aTargetContent &&
6047 !IsValidSibling(sibling, aTargetContent, aTargetContentDisplay)) {
6048 sibling = nullptr;
6049 }
6050
6051 return sibling;
6052 }
6053
6054 nsIFrame*
6055 nsCSSFrameConstructor::FindPreviousSibling(FlattenedChildIterator aIter,
6056 uint8_t& aTargetContentDisplay)
6057 {
6058 nsIContent* child = aIter.Get();
6059
6060 // Note: not all content objects are associated with a frame (e.g., if it's
6061 // `display: none') so keep looking until we find a previous frame
6062 while (nsIContent* sibling = aIter.GetPreviousChild()) {
6063 nsIFrame* prevSibling =
6064 FindFrameForContentSibling(sibling, child, aTargetContentDisplay, true);
6065
6066 if (prevSibling) {
6067 // Found a previous sibling, we're done!
6068 return prevSibling;
6069 }
6070 }
6071
6072 return nullptr;
6073 }
6074
6075 nsIFrame*
6076 nsCSSFrameConstructor::FindNextSibling(FlattenedChildIterator aIter,
6077 uint8_t& aTargetContentDisplay)
6078 {
6079 nsIContent* child = aIter.Get();
6080
6081 while (nsIContent* sibling = aIter.GetNextChild()) {
6082 nsIFrame* nextSibling =
6083 FindFrameForContentSibling(sibling, child, aTargetContentDisplay, false);
6084
6085 if (nextSibling) {
6086 // We found a next sibling, we're done!
6087 return nextSibling;
6088 }
6089 }
6090
6091 return nullptr;
6092 }
6093
6094 // For fieldsets, returns the area frame, if the child is not a legend.
6095 static nsIFrame*
6096 GetAdjustedParentFrame(nsIFrame* aParentFrame,
6097 nsIAtom* aParentFrameType,
6098 nsIContent* aChildContent)
6099 {
6100 NS_PRECONDITION(nsGkAtoms::tableOuterFrame != aParentFrameType,
6101 "Shouldn't be happening!");
6102
6103 nsIFrame* newParent = nullptr;
6104
6105 if (nsGkAtoms::fieldSetFrame == aParentFrameType) {
6106 // If the parent is a fieldSet, use the fieldSet's area frame as the
6107 // parent unless the new content is a legend.
6108 if (!aChildContent->IsHTML(nsGkAtoms::legend)) {
6109 newParent = GetFieldSetBlockFrame(aParentFrame);
6110 }
6111 }
6112 return (newParent) ? newParent : aParentFrame;
6113 }
6114
6115 nsIFrame*
6116 nsCSSFrameConstructor::GetInsertionPrevSibling(nsIFrame*& aParentFrame,
6117 nsIContent* aContainer,
6118 nsIContent* aChild,
6119 bool* aIsAppend,
6120 bool* aIsRangeInsertSafe,
6121 nsIContent* aStartSkipChild,
6122 nsIContent* aEndSkipChild)
6123 {
6124 *aIsAppend = false;
6125
6126 // Find the frame that precedes the insertion point. Walk backwards
6127 // from the parent frame to get the parent content, because if an
6128 // XBL insertion point is involved, we'll need to use _that_ to find
6129 // the preceding frame.
6130
6131 NS_PRECONDITION(aParentFrame, "Must have parent frame to start with");
6132 nsIContent* container = aParentFrame->GetContent();
6133
6134 FlattenedChildIterator iter(container);
6135 bool xblCase = iter.XBLInvolved() || container != aContainer;
6136 if (xblCase || !aChild->IsRootOfAnonymousSubtree()) {
6137 // The check for IsRootOfAnonymousSubtree() is because editor is
6138 // severely broken and calls us directly for native anonymous
6139 // nodes that it creates.
6140 if (aStartSkipChild) {
6141 iter.Seek(aStartSkipChild);
6142 } else {
6143 iter.Seek(aChild);
6144 }
6145 } else {
6146 // Prime the iterator for the call to FindPreviousSibling.
6147 iter.GetNextChild();
6148 NS_WARNING("Someone passed native anonymous content directly into frame "
6149 "construction. Stop doing that!");
6150 }
6151
6152 // Note that FindPreviousSibling is passed the iterator by value, so that
6153 // the later usage of the iterator starts from the same place.
6154 uint8_t childDisplay = UNSET_DISPLAY;
6155 nsIFrame* prevSibling = FindPreviousSibling(iter, childDisplay);
6156
6157 // Now, find the geometric parent so that we can handle
6158 // continuations properly. Use the prev sibling if we have it;
6159 // otherwise use the next sibling.
6160 if (prevSibling) {
6161 aParentFrame = prevSibling->GetParent()->GetContentInsertionFrame();
6162 }
6163 else {
6164 // If there is no previous sibling, then find the frame that follows
6165 if (aEndSkipChild) {
6166 iter.Seek(aEndSkipChild);
6167 iter.GetPreviousChild();
6168 }
6169 nsIFrame* nextSibling = FindNextSibling(iter, childDisplay);
6170
6171 if (nextSibling) {
6172 aParentFrame = nextSibling->GetParent()->GetContentInsertionFrame();
6173 }
6174 else {
6175 // No previous or next sibling, so treat this like an appended frame.
6176 *aIsAppend = true;
6177 if (IsFramePartOfIBSplit(aParentFrame)) {
6178 // Since we're appending, we'll walk to the last anonymous frame
6179 // that was created for the broken inline frame. But don't walk
6180 // to the trailing inline if it's empty; stop at the block.
6181 aParentFrame = GetLastIBSplitSibling(aParentFrame, false);
6182 }
6183 // Get continuation that parents the last child. This MUST be done
6184 // before the AdjustAppendParentForAfterContent call.
6185 aParentFrame = nsLayoutUtils::LastContinuationWithChild(aParentFrame);
6186 // Deal with fieldsets
6187 aParentFrame = ::GetAdjustedParentFrame(aParentFrame,
6188 aParentFrame->GetType(),
6189 aChild);
6190 nsIFrame* appendAfterFrame;
6191 aParentFrame =
6192 ::AdjustAppendParentForAfterContent(mPresShell->GetPresContext(),
6193 container, aParentFrame,
6194 &appendAfterFrame);
6195 prevSibling = ::FindAppendPrevSibling(aParentFrame, appendAfterFrame);
6196 }
6197 }
6198
6199 *aIsRangeInsertSafe = (childDisplay == UNSET_DISPLAY);
6200 return prevSibling;
6201 }
6202
6203 static bool
6204 IsSpecialFramesetChild(nsIContent* aContent)
6205 {
6206 // IMPORTANT: This must match the conditions in nsHTMLFramesetFrame::Init.
6207 return aContent->IsHTML() &&
6208 (aContent->Tag() == nsGkAtoms::frameset ||
6209 aContent->Tag() == nsGkAtoms::frame);
6210 }
6211
6212 static void
6213 InvalidateCanvasIfNeeded(nsIPresShell* presShell, nsIContent* node);
6214
6215 #ifdef MOZ_XUL
6216
6217 static
6218 bool
6219 IsXULListBox(nsIContent* aContainer)
6220 {
6221 return (aContainer->IsXUL() && aContainer->Tag() == nsGkAtoms::listbox);
6222 }
6223
6224 static
6225 nsListBoxBodyFrame*
6226 MaybeGetListBoxBodyFrame(nsIContent* aContainer, nsIContent* aChild)
6227 {
6228 if (!aContainer)
6229 return nullptr;
6230
6231 if (IsXULListBox(aContainer) &&
6232 aChild->IsXUL() && aChild->Tag() == nsGkAtoms::listitem) {
6233 nsCOMPtr<nsIDOMXULElement> xulElement = do_QueryInterface(aContainer);
6234 nsCOMPtr<nsIBoxObject> boxObject;
6235 xulElement->GetBoxObject(getter_AddRefs(boxObject));
6236 nsCOMPtr<nsPIListBoxObject> listBoxObject = do_QueryInterface(boxObject);
6237 if (listBoxObject) {
6238 return listBoxObject->GetListBoxBody(false);
6239 }
6240 }
6241
6242 return nullptr;
6243 }
6244 #endif
6245
6246 void
6247 nsCSSFrameConstructor::AddTextItemIfNeeded(nsFrameConstructorState& aState,
6248 nsIFrame* aParentFrame,
6249 nsIContent* aPossibleTextContent,
6250 FrameConstructionItemList& aItems)
6251 {
6252 NS_PRECONDITION(aPossibleTextContent, "Must have node");
6253 if (!aPossibleTextContent->IsNodeOfType(nsINode::eTEXT) ||
6254 !aPossibleTextContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE)) {
6255 // Not text, or not suppressed due to being all-whitespace (if it
6256 // were being suppressed, it would have the
6257 // NS_CREATE_FRAME_IF_NON_WHITESPACE flag)
6258 return;
6259 }
6260 NS_ASSERTION(!aPossibleTextContent->GetPrimaryFrame(),
6261 "Text node has a frame and NS_CREATE_FRAME_IF_NON_WHITESPACE");
6262 AddFrameConstructionItems(aState, aPossibleTextContent, false,
6263 aParentFrame, aItems);
6264 }
6265
6266 void
6267 nsCSSFrameConstructor::ReframeTextIfNeeded(nsIContent* aParentContent,
6268 nsIContent* aContent)
6269 {
6270 if (!aContent->IsNodeOfType(nsINode::eTEXT) ||
6271 !aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE)) {
6272 // Not text, or not suppressed due to being all-whitespace (if it
6273 // were being suppressed, it would have the
6274 // NS_CREATE_FRAME_IF_NON_WHITESPACE flag)
6275 return;
6276 }
6277 NS_ASSERTION(!aContent->GetPrimaryFrame(),
6278 "Text node has a frame and NS_CREATE_FRAME_IF_NON_WHITESPACE");
6279 ContentInserted(aParentContent, aContent, nullptr, false);
6280 }
6281
6282 // For inserts aChild should be valid, for appends it should be null.
6283 // Returns true if this operation can be lazy, false if not.
6284 bool
6285 nsCSSFrameConstructor::MaybeConstructLazily(Operation aOperation,
6286 nsIContent* aContainer,
6287 nsIContent* aChild)
6288 {
6289 if (mPresShell->GetPresContext()->IsChrome() || !aContainer ||
6290 aContainer->IsInNativeAnonymousSubtree() || aContainer->IsXUL()) {
6291 return false;
6292 }
6293
6294 if (aOperation == CONTENTINSERT) {
6295 if (aChild->IsRootOfAnonymousSubtree() ||
6296 aChild->HasFlag(NODE_IS_IN_SHADOW_TREE) ||
6297 aChild->IsEditable() || aChild->IsXUL()) {
6298 return false;
6299 }
6300 } else { // CONTENTAPPEND
6301 NS_ASSERTION(aOperation == CONTENTAPPEND,
6302 "operation should be either insert or append");
6303 for (nsIContent* child = aChild; child; child = child->GetNextSibling()) {
6304 NS_ASSERTION(!child->IsRootOfAnonymousSubtree(),
6305 "Should be coming through the CONTENTAPPEND case");
6306 if (child->IsXUL() || child->IsEditable()) {
6307 return false;
6308 }
6309 }
6310 }
6311
6312 // We can construct lazily; just need to set suitable bits in the content
6313 // tree.
6314
6315 // Walk up the tree setting the NODE_DESCENDANTS_NEED_FRAMES bit as we go.
6316 nsIContent* content = aContainer;
6317 #ifdef DEBUG
6318 // If we hit a node with no primary frame, or the NODE_NEEDS_FRAME bit set
6319 // we want to assert, but leaf frames that process their own children and may
6320 // ignore anonymous children (eg framesets) make this complicated. So we set
6321 // these two booleans if we encounter these situations and unset them if we
6322 // hit a node with a leaf frame.
6323 bool noPrimaryFrame = false;
6324 bool needsFrameBitSet = false;
6325 #endif
6326 while (content &&
6327 !content->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
6328 #ifdef DEBUG
6329 if (content->GetPrimaryFrame() && content->GetPrimaryFrame()->IsLeaf()) {
6330 noPrimaryFrame = needsFrameBitSet = false;
6331 }
6332 if (!noPrimaryFrame && !content->GetPrimaryFrame()) {
6333 noPrimaryFrame = true;
6334 }
6335 if (!needsFrameBitSet && content->HasFlag(NODE_NEEDS_FRAME)) {
6336 needsFrameBitSet = true;
6337 }
6338 #endif
6339 content->SetFlags(NODE_DESCENDANTS_NEED_FRAMES);
6340 content = content->GetFlattenedTreeParent();
6341 }
6342 #ifdef DEBUG
6343 if (content && content->GetPrimaryFrame() &&
6344 content->GetPrimaryFrame()->IsLeaf()) {
6345 noPrimaryFrame = needsFrameBitSet = false;
6346 }
6347 NS_ASSERTION(!noPrimaryFrame, "Ancestors of nodes with frames to be "
6348 "constructed lazily should have frames");
6349 NS_ASSERTION(!needsFrameBitSet, "Ancestors of nodes with frames to be "
6350 "constructed lazily should not have NEEDS_FRAME bit set");
6351 #endif
6352
6353 // Set NODE_NEEDS_FRAME on the new nodes.
6354 if (aOperation == CONTENTINSERT) {
6355 NS_ASSERTION(!aChild->GetPrimaryFrame() ||
6356 aChild->GetPrimaryFrame()->GetContent() != aChild,
6357 //XXX the aChild->GetPrimaryFrame()->GetContent() != aChild
6358 // check is needed due to bug 135040. Remove it once that's
6359 // fixed.
6360 "setting NEEDS_FRAME on a node that already has a frame?");
6361 aChild->SetFlags(NODE_NEEDS_FRAME);
6362 } else { // CONTENTAPPEND
6363 for (nsIContent* child = aChild; child; child = child->GetNextSibling()) {
6364 NS_ASSERTION(!child->GetPrimaryFrame() ||
6365 child->GetPrimaryFrame()->GetContent() != child,
6366 //XXX the child->GetPrimaryFrame()->GetContent() != child
6367 // check is needed due to bug 135040. Remove it once that's
6368 // fixed.
6369 "setting NEEDS_FRAME on a node that already has a frame?");
6370 child->SetFlags(NODE_NEEDS_FRAME);
6371 }
6372 }
6373
6374 RestyleManager()->PostRestyleEventForLazyConstruction();
6375 return true;
6376 }
6377
6378 void
6379 nsCSSFrameConstructor::CreateNeededFrames(nsIContent* aContent)
6380 {
6381 NS_ASSERTION(!aContent->HasFlag(NODE_NEEDS_FRAME),
6382 "shouldn't get here with a content node that has needs frame bit set");
6383 NS_ASSERTION(aContent->HasFlag(NODE_DESCENDANTS_NEED_FRAMES),
6384 "should only get here with a content node that has descendants needing frames");
6385
6386 aContent->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES);
6387
6388 // We could either descend first (on nodes that don't have NODE_NEEDS_FRAME
6389 // set) or issue content notifications for our kids first. In absence of
6390 // anything definitive either way we'll go with the latter.
6391
6392 // It might be better to use GetChildArray and scan it completely first and
6393 // then issue all notifications. (We have to scan it completely first because
6394 // constructing frames can set attributes, which can change the storage of
6395 // child lists).
6396
6397 // Scan the children of aContent to see what operations (if any) we need to
6398 // perform.
6399 uint32_t childCount = aContent->GetChildCount();
6400 bool inRun = false;
6401 nsIContent* firstChildInRun = nullptr;
6402 for (uint32_t i = 0; i < childCount; i++) {
6403 nsIContent* child = aContent->GetChildAt(i);
6404 if (child->HasFlag(NODE_NEEDS_FRAME)) {
6405 NS_ASSERTION(!child->GetPrimaryFrame() ||
6406 child->GetPrimaryFrame()->GetContent() != child,
6407 //XXX the child->GetPrimaryFrame()->GetContent() != child
6408 // check is needed due to bug 135040. Remove it once that's
6409 // fixed.
6410 "NEEDS_FRAME set on a node that already has a frame?");
6411 if (!inRun) {
6412 inRun = true;
6413 firstChildInRun = child;
6414 }
6415 } else {
6416 if (inRun) {
6417 inRun = false;
6418 // generate a ContentRangeInserted for [startOfRun,i)
6419 ContentRangeInserted(aContent, firstChildInRun, child, nullptr,
6420 false);
6421 }
6422 }
6423 }
6424 if (inRun) {
6425 ContentAppended(aContent, firstChildInRun, false);
6426 }
6427
6428 // Now descend.
6429 FlattenedChildIterator iter(aContent);
6430 for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
6431 if (child->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
6432 CreateNeededFrames(child);
6433 }
6434 }
6435 }
6436
6437 void nsCSSFrameConstructor::CreateNeededFrames()
6438 {
6439 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
6440 "Someone forgot a script blocker");
6441
6442 Element* rootElement = mDocument->GetRootElement();
6443 NS_ASSERTION(!rootElement || !rootElement->HasFlag(NODE_NEEDS_FRAME),
6444 "root element should not have frame created lazily");
6445 if (rootElement && rootElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
6446 BeginUpdate();
6447 CreateNeededFrames(rootElement);
6448 EndUpdate();
6449 }
6450 }
6451
6452 void
6453 nsCSSFrameConstructor::IssueSingleInsertNofications(nsIContent* aContainer,
6454 nsIContent* aStartChild,
6455 nsIContent* aEndChild,
6456 bool aAllowLazyConstruction)
6457 {
6458 for (nsIContent* child = aStartChild;
6459 child != aEndChild;
6460 child = child->GetNextSibling()) {
6461 if ((child->GetPrimaryFrame() ||
6462 GetUndisplayedContent(child))
6463 #ifdef MOZ_XUL
6464 // Except listboxes suck, so do NOT skip anything here if
6465 // we plan to notify a listbox.
6466 && !MaybeGetListBoxBodyFrame(aContainer, child)
6467 #endif
6468 ) {
6469 // Already have a frame or undisplayed entry for this content; a
6470 // previous ContentInserted in this loop must have reconstructed
6471 // its insertion parent. Skip it.
6472 continue;
6473 }
6474 // Call ContentInserted with this node.
6475 ContentInserted(aContainer, child, mTempFrameTreeState,
6476 aAllowLazyConstruction);
6477 }
6478 }
6479
6480 nsIFrame*
6481 nsCSSFrameConstructor::GetRangeInsertionPoint(nsIContent* aContainer,
6482 nsIContent* aStartChild,
6483 nsIContent* aEndChild,
6484 bool aAllowLazyConstruction)
6485 {
6486 // See if we have an XBL insertion point. If so, then that's our
6487 // real parent frame; if not, then the frame hasn't been built yet
6488 // and we just bail.
6489 bool multiple = false;
6490 nsIFrame* insertionPoint = GetInsertionPoint(aContainer, nullptr, &multiple);
6491 if (!insertionPoint && !multiple)
6492 return nullptr; // Don't build the frames.
6493
6494 bool hasInsertion = false;
6495 if (!multiple) {
6496 // XXXbz XBL2/sXBL issue
6497 nsIDocument* document = aStartChild->GetDocument();
6498 // XXXbz how would |document| be null here?
6499 if (document && aStartChild->GetXBLInsertionParent()) {
6500 hasInsertion = true;
6501 }
6502 }
6503
6504 if (multiple || hasInsertion) {
6505 // We have an insertion point. There are some additional tests we need to do
6506 // in order to ensure that an append is a safe operation.
6507 uint32_t childCount = 0;
6508
6509 if (!multiple) {
6510 // We may need to make multiple ContentInserted calls instead. A
6511 // reasonable heuristic to employ (in order to maintain good performance)
6512 // is to find out if the insertion point's content node contains any
6513 // explicit children. If it does not, then it is highly likely that
6514 // an append is occurring. (Note it is not definite, and there are insane
6515 // cases we will not deal with by employing this heuristic, but it beats
6516 // always falling back to multiple ContentInserted calls).
6517 //
6518 // In the multiple insertion point case, we know we're going to need to do
6519 // multiple ContentInserted calls anyway.
6520 // XXXndeakin This test doesn't work in the new world. Or rather, it works, but
6521 // it's slow
6522 childCount = insertionPoint->GetContent()->GetChildCount();
6523 }
6524
6525 // If we have multiple insertion points or if we have an insertion point
6526 // and the operation is not a true append or if the insertion point already
6527 // has explicit children, then we must fall back.
6528 if (multiple || aEndChild != nullptr || childCount > 0) {
6529 // Now comes the fun part. For each inserted child, make a
6530 // ContentInserted call as if it had just gotten inserted and
6531 // let ContentInserted handle the mess.
6532 IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
6533 aAllowLazyConstruction);
6534 return nullptr;
6535 }
6536 }
6537
6538 return insertionPoint;
6539 }
6540
6541 bool
6542 nsCSSFrameConstructor::MaybeRecreateForFrameset(nsIFrame* aParentFrame,
6543 nsIContent* aStartChild,
6544 nsIContent* aEndChild)
6545 {
6546 if (aParentFrame->GetType() == nsGkAtoms::frameSetFrame) {
6547 // Check whether we have any kids we care about.
6548 for (nsIContent* cur = aStartChild;
6549 cur != aEndChild;
6550 cur = cur->GetNextSibling()) {
6551 if (IsSpecialFramesetChild(cur)) {
6552 // Just reframe the parent, since framesets are weird like that.
6553 RecreateFramesForContent(aParentFrame->GetContent(), false);
6554 return true;
6555 }
6556 }
6557 }
6558 return false;
6559 }
6560
6561 nsresult
6562 nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
6563 nsIContent* aFirstNewContent,
6564 bool aAllowLazyConstruction)
6565 {
6566 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
6567 NS_PRECONDITION(mUpdateCount != 0,
6568 "Should be in an update while creating frames");
6569
6570 #ifdef DEBUG
6571 if (gNoisyContentUpdates) {
6572 printf("nsCSSFrameConstructor::ContentAppended container=%p "
6573 "first-child=%p lazy=%d\n",
6574 static_cast<void*>(aContainer), aFirstNewContent,
6575 aAllowLazyConstruction);
6576 if (gReallyNoisyContentUpdates && aContainer) {
6577 aContainer->List(stdout, 0);
6578 }
6579 }
6580 #endif
6581
6582 #ifdef DEBUG
6583 for (nsIContent* child = aFirstNewContent;
6584 child;
6585 child = child->GetNextSibling()) {
6586 // XXX the GetContent() != child check is needed due to bug 135040.
6587 // Remove it once that's fixed.
6588 NS_ASSERTION(!child->GetPrimaryFrame() ||
6589 child->GetPrimaryFrame()->GetContent() != child,
6590 "asked to construct a frame for a node that already has a frame");
6591 }
6592 #endif
6593
6594 #ifdef MOZ_XUL
6595 if (aContainer) {
6596 int32_t namespaceID;
6597 nsIAtom* tag =
6598 mDocument->BindingManager()->ResolveTag(aContainer, &namespaceID);
6599
6600 // Just ignore tree tags, anyway we don't create any frames for them.
6601 if (tag == nsGkAtoms::treechildren ||
6602 tag == nsGkAtoms::treeitem ||
6603 tag == nsGkAtoms::treerow)
6604 return NS_OK;
6605
6606 }
6607 #endif // MOZ_XUL
6608
6609 if (aContainer && aContainer->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
6610 // Recreate frames if content is appended into a ShadowRoot
6611 // because children of ShadowRoot are rendered in place of children
6612 // of the host.
6613 nsIContent* bindingParent = aContainer->GetBindingParent();
6614 LAYOUT_PHASE_TEMP_EXIT();
6615 nsresult rv = RecreateFramesForContent(bindingParent, false);
6616 LAYOUT_PHASE_TEMP_REENTER();
6617 return rv;
6618 }
6619
6620 // Get the frame associated with the content
6621 nsIFrame* parentFrame = GetFrameFor(aContainer);
6622
6623 // See comment in ContentRangeInserted for why this is necessary.
6624 if (!parentFrame && !aContainer->IsActiveChildrenElement()) {
6625 return NS_OK;
6626 }
6627
6628 if (aAllowLazyConstruction &&
6629 MaybeConstructLazily(CONTENTAPPEND, aContainer, aFirstNewContent)) {
6630 return NS_OK;
6631 }
6632
6633 LAYOUT_PHASE_TEMP_EXIT();
6634 parentFrame = GetRangeInsertionPoint(aContainer,
6635 aFirstNewContent, nullptr,
6636 aAllowLazyConstruction);
6637 LAYOUT_PHASE_TEMP_REENTER();
6638 if (!parentFrame) {
6639 return NS_OK;
6640 }
6641
6642 LAYOUT_PHASE_TEMP_EXIT();
6643 if (MaybeRecreateForFrameset(parentFrame, aFirstNewContent, nullptr)) {
6644 LAYOUT_PHASE_TEMP_REENTER();
6645 return NS_OK;
6646 }
6647 LAYOUT_PHASE_TEMP_REENTER();
6648
6649 if (parentFrame->IsLeaf()) {
6650 // Nothing to do here; we shouldn't be constructing kids of leaves
6651 // Clear lazy bits so we don't try to construct again.
6652 ClearLazyBits(aFirstNewContent, nullptr);
6653 return NS_OK;
6654 }
6655
6656 if (parentFrame->IsFrameOfType(nsIFrame::eMathML)) {
6657 LAYOUT_PHASE_TEMP_EXIT();
6658 nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), false);
6659 LAYOUT_PHASE_TEMP_REENTER();
6660 return rv;
6661 }
6662
6663 // If the frame we are manipulating is a ib-split frame (that is, one
6664 // that's been created as a result of a block-in-inline situation) then we
6665 // need to append to the last ib-split sibling, not to the frame itself.
6666 bool parentIBSplit = IsFramePartOfIBSplit(parentFrame);
6667 if (parentIBSplit) {
6668 #ifdef DEBUG
6669 if (gNoisyContentUpdates) {
6670 printf("nsCSSFrameConstructor::ContentAppended: parentFrame=");
6671 nsFrame::ListTag(stdout, parentFrame);
6672 printf(" is ib-split\n");
6673 }
6674 #endif
6675
6676 // Since we're appending, we'll walk to the last anonymous frame
6677 // that was created for the broken inline frame. But don't walk
6678 // to the trailing inline if it's empty; stop at the block.
6679 parentFrame = GetLastIBSplitSibling(parentFrame, false);
6680 }
6681
6682 // Get continuation that parents the last child. This MUST be done
6683 // before the AdjustAppendParentForAfterContent call.
6684 parentFrame = nsLayoutUtils::LastContinuationWithChild(parentFrame);
6685
6686 // We should never get here with fieldsets, since they have multiple
6687 // insertion points.
6688 NS_ASSERTION(parentFrame->GetType() != nsGkAtoms::fieldSetFrame,
6689 "Unexpected parent");
6690
6691 // Deal with possible :after generated content on the parent
6692 nsIFrame* parentAfterFrame;
6693 parentFrame =
6694 ::AdjustAppendParentForAfterContent(mPresShell->GetPresContext(),
6695 aContainer, parentFrame,
6696 &parentAfterFrame);
6697
6698 // Create some new frames
6699 nsFrameConstructorState state(mPresShell,
6700 GetAbsoluteContainingBlock(parentFrame, FIXED_POS),
6701 GetAbsoluteContainingBlock(parentFrame, ABS_POS),
6702 GetFloatContainingBlock(parentFrame));
6703 state.mTreeMatchContext.InitAncestors(aContainer->AsElement());
6704
6705 // See if the containing block has :first-letter style applied.
6706 bool haveFirstLetterStyle = false, haveFirstLineStyle = false;
6707 nsIFrame* containingBlock = state.mFloatedItems.containingBlock;
6708 if (containingBlock) {
6709 haveFirstLetterStyle = HasFirstLetterStyle(containingBlock);
6710 haveFirstLineStyle =
6711 ShouldHaveFirstLineStyle(containingBlock->GetContent(),
6712 containingBlock->StyleContext());
6713 }
6714
6715 if (haveFirstLetterStyle) {
6716 // Before we get going, remove the current letter frames
6717 RemoveLetterFrames(state.mPresContext, state.mPresShell,
6718 containingBlock);
6719 }
6720
6721 nsIAtom* frameType = parentFrame->GetType();
6722
6723 FlattenedChildIterator iter(aContainer);
6724 bool haveNoXBLChildren = (!iter.XBLInvolved() || !iter.GetNextChild());
6725 FrameConstructionItemList items;
6726 if (aFirstNewContent->GetPreviousSibling() &&
6727 GetParentType(frameType) == eTypeBlock &&
6728 haveNoXBLChildren) {
6729 // If there's a text node in the normal content list just before the new
6730 // items, and it has no frame, make a frame construction item for it. If it
6731 // doesn't need a frame, ConstructFramesFromItemList below won't give it
6732 // one. No need to do all this if our parent type is not block, though,
6733 // since WipeContainingBlock already handles that situation.
6734 //
6735 // Because we're appending, we don't need to worry about any text
6736 // after the appended content; there can only be XBL anonymous content
6737 // (text in an XBL binding is not suppressed) or generated content
6738 // (and bare text nodes are not generated). Native anonymous content
6739 // generated by frames never participates in inline layout.
6740 AddTextItemIfNeeded(state, parentFrame,
6741 aFirstNewContent->GetPreviousSibling(), items);
6742 }
6743 for (nsIContent* child = aFirstNewContent;
6744 child;
6745 child = child->GetNextSibling()) {
6746 AddFrameConstructionItems(state, child, false, parentFrame, items);
6747 }
6748
6749 nsIFrame* prevSibling = ::FindAppendPrevSibling(parentFrame, parentAfterFrame);
6750
6751 // Perform special check for diddling around with the frames in
6752 // a ib-split inline frame.
6753 // If we're appending before :after content, then we're not really
6754 // appending, so let WipeContainingBlock know that.
6755 LAYOUT_PHASE_TEMP_EXIT();
6756 if (WipeContainingBlock(state, containingBlock, parentFrame, items,
6757 true, prevSibling)) {
6758 LAYOUT_PHASE_TEMP_REENTER();
6759 return NS_OK;
6760 }
6761 LAYOUT_PHASE_TEMP_REENTER();
6762
6763 // If the parent is a block frame, and we're not in a special case
6764 // where frames can be moved around, determine if the list is for the
6765 // start or end of the block.
6766 if (nsLayoutUtils::GetAsBlock(parentFrame) && !haveFirstLetterStyle &&
6767 !haveFirstLineStyle && !parentIBSplit) {
6768 items.SetLineBoundaryAtStart(!prevSibling ||
6769 !prevSibling->IsInlineOutside() ||
6770 prevSibling->GetType() == nsGkAtoms::brFrame);
6771 // :after content can't be <br> so no need to check it
6772 items.SetLineBoundaryAtEnd(!parentAfterFrame ||
6773 !parentAfterFrame->IsInlineOutside());
6774 }
6775 // To suppress whitespace-only text frames, we have to verify that
6776 // our container's DOM child list matches its flattened tree child list.
6777 items.SetParentHasNoXBLChildren(haveNoXBLChildren);
6778
6779 nsFrameItems frameItems;
6780 ConstructFramesFromItemList(state, items, parentFrame, frameItems);
6781
6782 for (nsIContent* child = aFirstNewContent;
6783 child;
6784 child = child->GetNextSibling()) {
6785 // Invalidate now instead of before the WipeContainingBlock call, just in
6786 // case we do wipe; in that case we don't need to do this walk at all.
6787 // XXXbz does that matter? Would it make more sense to save some virtual
6788 // GetChildAt calls instead and do this during construction of our
6789 // FrameConstructionItemList?
6790 InvalidateCanvasIfNeeded(mPresShell, child);
6791 }
6792
6793 // if the container is a table and a caption was appended, it needs to be put
6794 // in the outer table frame's additional child list.
6795 nsFrameItems captionItems;
6796 if (nsGkAtoms::tableFrame == frameType) {
6797 // Pull out the captions. Note that we don't want to do that as we go,
6798 // because processing a single caption can add a whole bunch of things to
6799 // the frame items due to pseudoframe processing. So we'd have to pull
6800 // captions from a list anyway; might as well do that here.
6801 // XXXbz this is no longer true; we could pull captions directly out of the
6802 // FrameConstructionItemList now.
6803 PullOutCaptionFrames(frameItems, captionItems);
6804 }
6805
6806 if (haveFirstLineStyle && parentFrame == containingBlock) {
6807 // It's possible that some of the new frames go into a
6808 // first-line frame. Look at them and see...
6809 AppendFirstLineFrames(state, containingBlock->GetContent(),
6810 containingBlock, frameItems);
6811 }
6812
6813 // Notify the parent frame passing it the list of new frames
6814 // Append the flowed frames to the principal child list; captions
6815 // need special treatment
6816 if (captionItems.NotEmpty()) { // append the caption to the outer table
6817 NS_ASSERTION(nsGkAtoms::tableFrame == frameType, "how did that happen?");
6818 nsIFrame* outerTable = parentFrame->GetParent();
6819 AppendFrames(outerTable, nsIFrame::kCaptionList, captionItems);
6820 }
6821
6822 if (frameItems.NotEmpty()) { // append the in-flow kids
6823 AppendFramesToParent(state, parentFrame, frameItems, prevSibling);
6824 }
6825
6826 // Recover first-letter frames
6827 if (haveFirstLetterStyle) {
6828 RecoverLetterFrames(containingBlock);
6829 }
6830
6831 #ifdef DEBUG
6832 if (gReallyNoisyContentUpdates) {
6833 printf("nsCSSFrameConstructor::ContentAppended: resulting frame model:\n");
6834 parentFrame->List(stdout, 0);
6835 }
6836 #endif
6837
6838 #ifdef ACCESSIBILITY
6839 nsAccessibilityService* accService = nsIPresShell::AccService();
6840 if (accService) {
6841 accService->ContentRangeInserted(mPresShell, aContainer,
6842 aFirstNewContent, nullptr);
6843 }
6844 #endif
6845
6846 return NS_OK;
6847 }
6848
6849 #ifdef MOZ_XUL
6850
6851 enum content_operation
6852 {
6853 CONTENT_INSERTED,
6854 CONTENT_REMOVED
6855 };
6856
6857 // Helper function to lookup the listbox body frame and send a notification
6858 // for insertion or removal of content
6859 static
6860 bool NotifyListBoxBody(nsPresContext* aPresContext,
6861 nsIContent* aContainer,
6862 nsIContent* aChild,
6863 // Only used for the removed notification
6864 nsIContent* aOldNextSibling,
6865 nsIDocument* aDocument,
6866 nsIFrame* aChildFrame,
6867 content_operation aOperation)
6868 {
6869 nsListBoxBodyFrame* listBoxBodyFrame =
6870 MaybeGetListBoxBodyFrame(aContainer, aChild);
6871 if (listBoxBodyFrame) {
6872 if (aOperation == CONTENT_REMOVED) {
6873 // Except if we have an aChildFrame and its parent is not the right
6874 // thing, then we don't do this. Pseudo frames are so much fun....
6875 if (!aChildFrame || aChildFrame->GetParent() == listBoxBodyFrame) {
6876 listBoxBodyFrame->OnContentRemoved(aPresContext, aContainer,
6877 aChildFrame, aOldNextSibling);
6878 return true;
6879 }
6880 } else {
6881 listBoxBodyFrame->OnContentInserted(aPresContext, aChild);
6882 return true;
6883 }
6884 }
6885
6886 return false;
6887 }
6888 #endif // MOZ_XUL
6889
6890 nsresult
6891 nsCSSFrameConstructor::ContentInserted(nsIContent* aContainer,
6892 nsIContent* aChild,
6893 nsILayoutHistoryState* aFrameState,
6894 bool aAllowLazyConstruction)
6895 {
6896 return ContentRangeInserted(aContainer,
6897 aChild,
6898 aChild->GetNextSibling(),
6899 aFrameState,
6900 aAllowLazyConstruction);
6901 }
6902
6903 // ContentRangeInserted handles creating frames for a range of nodes that
6904 // aren't at the end of their childlist. ContentRangeInserted isn't a real
6905 // content notification, but rather it handles regular ContentInserted calls
6906 // for a single node as well as the lazy construction of frames for a range of
6907 // nodes when called from CreateNeededFrames. For a range of nodes to be
6908 // suitable to have its frames constructed all at once they must meet the same
6909 // conditions that ContentAppended imposes (GetRangeInsertionPoint checks
6910 // these), plus more. Namely when finding the insertion prevsibling we must not
6911 // need to consult something specific to any one node in the range, so that the
6912 // insertion prevsibling would be the same for each node in the range. So we
6913 // pass the first node in the range to GetInsertionPrevSibling, and if
6914 // IsValidSibling (the only place GetInsertionPrevSibling might look at the
6915 // passed in node itself) needs to resolve style on the node we record this and
6916 // return that this range needs to be split up and inserted separately. Table
6917 // captions need extra attention as we need to determine where to insert them
6918 // in the caption list, while skipping any nodes in the range being inserted
6919 // (because when we treat the caption frames the other nodes have had their
6920 // frames constructed but not yet inserted into the frame tree).
6921 nsresult
6922 nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
6923 nsIContent* aStartChild,
6924 nsIContent* aEndChild,
6925 nsILayoutHistoryState* aFrameState,
6926 bool aAllowLazyConstruction)
6927 {
6928 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
6929 NS_PRECONDITION(mUpdateCount != 0,
6930 "Should be in an update while creating frames");
6931
6932 NS_PRECONDITION(aStartChild, "must always pass a child");
6933
6934 // XXXldb Do we need to re-resolve style to handle the CSS2 + combinator and
6935 // the :empty pseudo-class?
6936 #ifdef DEBUG
6937 if (gNoisyContentUpdates) {
6938 printf("nsCSSFrameConstructor::ContentRangeInserted container=%p "
6939 "start-child=%p end-child=%p lazy=%d\n",
6940 static_cast<void*>(aContainer),
6941 static_cast<void*>(aStartChild), static_cast<void*>(aEndChild),
6942 aAllowLazyConstruction);
6943 if (gReallyNoisyContentUpdates) {
6944 if (aContainer) {
6945 aContainer->List(stdout,0);
6946 } else {
6947 aStartChild->List(stdout, 0);
6948 }
6949 }
6950 }
6951 #endif
6952
6953 #ifdef DEBUG
6954 for (nsIContent* child = aStartChild;
6955 child != aEndChild;
6956 child = child->GetNextSibling()) {
6957 // XXX the GetContent() != child check is needed due to bug 135040.
6958 // Remove it once that's fixed.
6959 NS_ASSERTION(!child->GetPrimaryFrame() ||
6960 child->GetPrimaryFrame()->GetContent() != child,
6961 "asked to construct a frame for a node that already has a frame");
6962 }
6963 #endif
6964
6965 bool isSingleInsert = (aStartChild->GetNextSibling() == aEndChild);
6966 NS_ASSERTION(isSingleInsert || !aAllowLazyConstruction,
6967 "range insert shouldn't be lazy");
6968 NS_ASSERTION(isSingleInsert || aEndChild,
6969 "range should not include all nodes after aStartChild");
6970
6971 #ifdef MOZ_XUL
6972 if (aContainer && IsXULListBox(aContainer)) {
6973 if (isSingleInsert) {
6974 if (NotifyListBoxBody(mPresShell->GetPresContext(), aContainer,
6975 // The insert case in NotifyListBoxBody
6976 // doesn't use "old next sibling".
6977 aStartChild, nullptr,
6978 mDocument, nullptr, CONTENT_INSERTED)) {
6979 return NS_OK;
6980 }
6981 } else {
6982 // We don't handle a range insert to a listbox parent, issue single
6983 // ContertInserted calls for each node inserted.
6984 LAYOUT_PHASE_TEMP_EXIT();
6985 IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
6986 aAllowLazyConstruction);
6987 LAYOUT_PHASE_TEMP_REENTER();
6988 return NS_OK;
6989 }
6990 }
6991 #endif // MOZ_XUL
6992
6993 // If we have a null parent, then this must be the document element being
6994 // inserted, or some other child of the document in the DOM (might be a PI,
6995 // say).
6996 if (! aContainer) {
6997 NS_ASSERTION(isSingleInsert,
6998 "root node insertion should be a single insertion");
6999 Element *docElement = mDocument->GetRootElement();
7000
7001 if (aStartChild != docElement) {
7002 // Not the root element; just bail out
7003 return NS_OK;
7004 }
7005
7006 NS_PRECONDITION(nullptr == mRootElementFrame,
7007 "root element frame already created");
7008
7009 // Create frames for the document element and its child elements
7010 nsIFrame* docElementFrame =
7011 ConstructDocElementFrame(docElement, aFrameState);
7012
7013 if (docElementFrame) {
7014 InvalidateCanvasIfNeeded(mPresShell, aStartChild);
7015 #ifdef DEBUG
7016 if (gReallyNoisyContentUpdates) {
7017 printf("nsCSSFrameConstructor::ContentRangeInserted: resulting frame "
7018 "model:\n");
7019 mFixedContainingBlock->List(stdout, 0);
7020 }
7021 #endif
7022 }
7023
7024 if (aFrameState) {
7025 // Restore frame state for the root scroll frame if there is one
7026 nsIFrame* rootScrollFrame = mPresShell->GetRootScrollFrame();
7027 if (rootScrollFrame) {
7028 RestoreFrameStateFor(rootScrollFrame, aFrameState);
7029 }
7030 }
7031
7032 #ifdef ACCESSIBILITY
7033 nsAccessibilityService* accService = nsIPresShell::AccService();
7034 if (accService) {
7035 accService->ContentRangeInserted(mPresShell, aContainer,
7036 aStartChild, aEndChild);
7037 }
7038 #endif
7039
7040 return NS_OK;
7041 }
7042
7043 if (aContainer->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
7044 // Recreate frames if content is inserted into a ShadowRoot
7045 // because children of ShadowRoot are rendered in place of
7046 // the children of the host.
7047 nsIContent* bindingParent = aContainer->GetBindingParent();
7048 LAYOUT_PHASE_TEMP_EXIT();
7049 nsresult rv = RecreateFramesForContent(bindingParent, false);
7050 LAYOUT_PHASE_TEMP_REENTER();
7051 return rv;
7052 }
7053
7054 nsIFrame* parentFrame = GetFrameFor(aContainer);
7055 // The xbl:children element won't have a frame, but default content can have the children as
7056 // a parent. While its uncommon to change the structure of the default content itself, a label,
7057 // for example, can be reframed by having its value attribute set or removed.
7058 if (!parentFrame && !aContainer->IsActiveChildrenElement()) {
7059 return NS_OK;
7060 }
7061
7062 // Otherwise, we've got parent content. Find its frame.
7063 NS_ASSERTION(!parentFrame || parentFrame->GetContent() == aContainer, "New XBL code is possibly wrong!");
7064
7065 if (aAllowLazyConstruction &&
7066 MaybeConstructLazily(CONTENTINSERT, aContainer, aStartChild)) {
7067 return NS_OK;
7068 }
7069
7070 if (isSingleInsert) {
7071 // See if we have an XBL insertion point. If so, then that's our
7072 // real parent frame; if not, then the frame hasn't been built yet
7073 // and we just bail.
7074 parentFrame = GetInsertionPoint(aContainer, aStartChild);
7075 } else {
7076 // Get our insertion point. If we need to issue single ContentInserted's
7077 // GetRangeInsertionPoint will take care of that for us.
7078 LAYOUT_PHASE_TEMP_EXIT();
7079 parentFrame = GetRangeInsertionPoint(aContainer, aStartChild, aEndChild,
7080 aAllowLazyConstruction);
7081 LAYOUT_PHASE_TEMP_REENTER();
7082 }
7083
7084 if (!parentFrame) {
7085 return NS_OK;
7086 }
7087
7088 bool isAppend, isRangeInsertSafe;
7089 nsIFrame* prevSibling =
7090 GetInsertionPrevSibling(parentFrame, aContainer, aStartChild,
7091 &isAppend, &isRangeInsertSafe);
7092
7093 // check if range insert is safe
7094 if (!isSingleInsert && !isRangeInsertSafe) {
7095 // must fall back to a single ContertInserted for each child in the range
7096 LAYOUT_PHASE_TEMP_EXIT();
7097 IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
7098 aAllowLazyConstruction);
7099 LAYOUT_PHASE_TEMP_REENTER();
7100 return NS_OK;
7101 }
7102
7103 nsIContent* container = parentFrame->GetContent();
7104
7105 nsIAtom* frameType = parentFrame->GetType();
7106 LAYOUT_PHASE_TEMP_EXIT();
7107 if (MaybeRecreateForFrameset(parentFrame, aStartChild, aEndChild)) {
7108 LAYOUT_PHASE_TEMP_REENTER();
7109 return NS_OK;
7110 }
7111 LAYOUT_PHASE_TEMP_REENTER();
7112
7113 // We should only get here with fieldsets when doing a single insert, because
7114 // fieldsets have multiple insertion points.
7115 NS_ASSERTION(isSingleInsert || frameType != nsGkAtoms::fieldSetFrame,
7116 "Unexpected parent");
7117 if (IsFrameForFieldSet(parentFrame, frameType) &&
7118 aStartChild->Tag() == nsGkAtoms::legend) {
7119 // Just reframe the parent, since figuring out whether this
7120 // should be the new legend and then handling it is too complex.
7121 // We could do a little better here --- check if the fieldset already
7122 // has a legend which occurs earlier in its child list than this node,
7123 // and if so, proceed. But we'd have to extend nsFieldSetFrame
7124 // to locate this legend in the inserted frames and extract it.
7125 LAYOUT_PHASE_TEMP_EXIT();
7126 nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), false);
7127 LAYOUT_PHASE_TEMP_REENTER();
7128 return rv;
7129 }
7130
7131 // Don't construct kids of leaves
7132 if (parentFrame->IsLeaf()) {
7133 // Clear lazy bits so we don't try to construct again.
7134 ClearLazyBits(aStartChild, aEndChild);
7135 return NS_OK;
7136 }
7137
7138 if (parentFrame->IsFrameOfType(nsIFrame::eMathML)) {
7139 LAYOUT_PHASE_TEMP_EXIT();
7140 nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), false);
7141 LAYOUT_PHASE_TEMP_REENTER();
7142 return rv;
7143 }
7144
7145 nsFrameConstructorState state(mPresShell,
7146 GetAbsoluteContainingBlock(parentFrame, FIXED_POS),
7147 GetAbsoluteContainingBlock(parentFrame, ABS_POS),
7148 GetFloatContainingBlock(parentFrame),
7149 aFrameState);
7150 state.mTreeMatchContext.InitAncestors(aContainer ?
7151 aContainer->AsElement() :
7152 nullptr);
7153
7154 // Recover state for the containing block - we need to know if
7155 // it has :first-letter or :first-line style applied to it. The
7156 // reason we care is that the internal structure in these cases
7157 // is not the normal structure and requires custom updating
7158 // logic.
7159 nsIFrame* containingBlock = state.mFloatedItems.containingBlock;
7160 bool haveFirstLetterStyle = false;
7161 bool haveFirstLineStyle = false;
7162
7163 // In order to shave off some cycles, we only dig up the
7164 // containing block haveFirst* flags if the parent frame where
7165 // the insertion/append is occurring is an inline or block
7166 // container. For other types of containers this isn't relevant.
7167 uint8_t parentDisplay = parentFrame->GetDisplay();
7168
7169 // Examine the parentFrame where the insertion is taking
7170 // place. If it's a certain kind of container then some special
7171 // processing is done.
7172 if ((NS_STYLE_DISPLAY_BLOCK == parentDisplay) ||
7173 (NS_STYLE_DISPLAY_LIST_ITEM == parentDisplay) ||
7174 (NS_STYLE_DISPLAY_INLINE == parentDisplay) ||
7175 (NS_STYLE_DISPLAY_INLINE_BLOCK == parentDisplay)) {
7176 // Recover the special style flags for the containing block
7177 if (containingBlock) {
7178 haveFirstLetterStyle = HasFirstLetterStyle(containingBlock);
7179 haveFirstLineStyle =
7180 ShouldHaveFirstLineStyle(containingBlock->GetContent(),
7181 containingBlock->StyleContext());
7182 }
7183
7184 if (haveFirstLetterStyle) {
7185 // If our current parentFrame is a Letter frame, use its parent as our
7186 // new parent hint
7187 if (parentFrame->GetType() == nsGkAtoms::letterFrame) {
7188 // If parentFrame is out of flow, then we actually want the parent of
7189 // the placeholder frame.
7190 if (parentFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
7191 nsPlaceholderFrame* placeholderFrame =
7192 GetPlaceholderFrameFor(parentFrame);
7193 NS_ASSERTION(placeholderFrame, "No placeholder for out-of-flow?");
7194 parentFrame = placeholderFrame->GetParent();
7195 } else {
7196 parentFrame = parentFrame->GetParent();
7197 }
7198 }
7199
7200 // Remove the old letter frames before doing the insertion
7201 RemoveLetterFrames(state.mPresContext, mPresShell,
7202 state.mFloatedItems.containingBlock);
7203
7204 // Removing the letterframes messes around with the frame tree, removing
7205 // and creating frames. We need to reget our prevsibling, parent frame,
7206 // etc.
7207 prevSibling = GetInsertionPrevSibling(parentFrame, aContainer,
7208 aStartChild, &isAppend,
7209 &isRangeInsertSafe);
7210
7211 // Need check whether a range insert is still safe.
7212 if (!isSingleInsert && !isRangeInsertSafe) {
7213 // Need to recover the letter frames first.
7214 RecoverLetterFrames(state.mFloatedItems.containingBlock);
7215
7216 // must fall back to a single ContertInserted for each child in the range
7217 LAYOUT_PHASE_TEMP_EXIT();
7218 IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
7219 aAllowLazyConstruction);
7220 LAYOUT_PHASE_TEMP_REENTER();
7221 return NS_OK;
7222 }
7223
7224 container = parentFrame->GetContent();
7225 frameType = parentFrame->GetType();
7226 }
7227 }
7228
7229 if (!prevSibling) {
7230 // We're inserting the new frames as the first child. See if the
7231 // parent has a :before pseudo-element
7232 nsIFrame* firstChild = parentFrame->GetFirstPrincipalChild();
7233
7234 if (firstChild &&
7235 nsLayoutUtils::IsGeneratedContentFor(container, firstChild,
7236 nsCSSPseudoElements::before)) {
7237 // Insert the new frames after the last continuation of the :before
7238 prevSibling = firstChild->GetTailContinuation();
7239 parentFrame = prevSibling->GetParent()->GetContentInsertionFrame();
7240 // Don't change isAppend here; we'll can call AppendFrames as needed, and
7241 // the change to our prevSibling doesn't affect that.
7242 }
7243 }
7244
7245 FrameConstructionItemList items;
7246 ParentType parentType = GetParentType(frameType);
7247 FlattenedChildIterator iter(aContainer);
7248 bool haveNoXBLChildren = (!iter.XBLInvolved() || !iter.GetNextChild());
7249 if (aStartChild->GetPreviousSibling() &&
7250 parentType == eTypeBlock && haveNoXBLChildren) {
7251 // If there's a text node in the normal content list just before the
7252 // new nodes, and it has no frame, make a frame construction item for
7253 // it, because it might need a frame now. No need to do this if our
7254 // parent type is not block, though, since WipeContainingBlock
7255 // already handles that sitation.
7256 AddTextItemIfNeeded(state, parentFrame, aStartChild->GetPreviousSibling(),
7257 items);
7258 }
7259
7260 if (isSingleInsert) {
7261 AddFrameConstructionItems(state, aStartChild,
7262 aStartChild->IsRootOfAnonymousSubtree(),
7263 parentFrame, items);
7264 } else {
7265 for (nsIContent* child = aStartChild;
7266 child != aEndChild;
7267 child = child->GetNextSibling()){
7268 AddFrameConstructionItems(state, child, false, parentFrame, items);
7269 }
7270 }
7271
7272 if (aEndChild && parentType == eTypeBlock && haveNoXBLChildren) {
7273 // If there's a text node in the normal content list just after the
7274 // new nodes, and it has no frame, make a frame construction item for
7275 // it, because it might need a frame now. No need to do this if our
7276 // parent type is not block, though, since WipeContainingBlock
7277 // already handles that sitation.
7278 AddTextItemIfNeeded(state, parentFrame, aEndChild, items);
7279 }
7280
7281 // Perform special check for diddling around with the frames in
7282 // a special inline frame.
7283 // If we're appending before :after content, then we're not really
7284 // appending, so let WipeContainingBlock know that.
7285 LAYOUT_PHASE_TEMP_EXIT();
7286 if (WipeContainingBlock(state, containingBlock, parentFrame, items,
7287 isAppend, prevSibling)) {
7288 LAYOUT_PHASE_TEMP_REENTER();
7289 return NS_OK;
7290 }
7291 LAYOUT_PHASE_TEMP_REENTER();
7292
7293 // If the container is a table and a caption will be appended, it needs to be
7294 // put in the outer table frame's additional child list.
7295 // We make no attempt here to set flags to indicate whether the list
7296 // will be at the start or end of a block. It doesn't seem worthwhile.
7297 nsFrameItems frameItems, captionItems;
7298 ConstructFramesFromItemList(state, items, parentFrame, frameItems);
7299
7300 if (frameItems.NotEmpty()) {
7301 for (nsIContent* child = aStartChild;
7302 child != aEndChild;
7303 child = child->GetNextSibling()){
7304 InvalidateCanvasIfNeeded(mPresShell, child);
7305 }
7306
7307 if (nsGkAtoms::tableFrame == frameType ||
7308 nsGkAtoms::tableOuterFrame == frameType) {
7309 PullOutCaptionFrames(frameItems, captionItems);
7310 }
7311 }
7312
7313 // If the parent of our current prevSibling is different from the frame we'll
7314 // actually use as the parent, then the calculated insertion point is now
7315 // invalid and as it is unknown where to insert correctly we append instead
7316 // (bug 341858).
7317 // This can affect our prevSibling and isAppend, but should not have any
7318 // effect on the WipeContainingBlock above, since this should only happen
7319 // when neither parent is a ib-split frame and should not affect whitespace
7320 // handling inside table-related frames (and in fact, can only happen when
7321 // one of the parents is an outer table and one is an inner table or when the
7322 // parent is a fieldset or fieldset content frame). So it won't affect the
7323 // {ib} or XUL box cases in WipeContainingBlock(), and the table pseudo
7324 // handling will only be affected by us maybe thinking we're not inserting
7325 // at the beginning, whereas we really are. That would have made us reframe
7326 // unnecessarily, but that's ok.
7327 // XXXbz we should push our frame construction item code up higher, so we
7328 // know what our items are by the time we start figuring out previous
7329 // siblings
7330 if (prevSibling && frameItems.NotEmpty() &&
7331 frameItems.FirstChild()->GetParent() != prevSibling->GetParent()) {
7332 #ifdef DEBUG
7333 nsIFrame* frame1 = frameItems.FirstChild()->GetParent();
7334 nsIFrame* frame2 = prevSibling->GetParent();
7335 NS_ASSERTION(!IsFramePartOfIBSplit(frame1) &&
7336 !IsFramePartOfIBSplit(frame2),
7337 "Neither should be ib-split");
7338 NS_ASSERTION((frame1->GetType() == nsGkAtoms::tableFrame &&
7339 frame2->GetType() == nsGkAtoms::tableOuterFrame) ||
7340 (frame1->GetType() == nsGkAtoms::tableOuterFrame &&
7341 frame2->GetType() == nsGkAtoms::tableFrame) ||
7342 frame1->GetType() == nsGkAtoms::fieldSetFrame ||
7343 (frame1->GetParent() &&
7344 frame1->GetParent()->GetType() == nsGkAtoms::fieldSetFrame),
7345 "Unexpected frame types");
7346 #endif
7347 isAppend = true;
7348 nsIFrame* appendAfterFrame;
7349 parentFrame =
7350 ::AdjustAppendParentForAfterContent(mPresShell->GetPresContext(),
7351 container,
7352 frameItems.FirstChild()->GetParent(),
7353 &appendAfterFrame);
7354 prevSibling = ::FindAppendPrevSibling(parentFrame, appendAfterFrame);
7355 }
7356
7357 if (haveFirstLineStyle && parentFrame == containingBlock) {
7358 // It's possible that the new frame goes into a first-line
7359 // frame. Look at it and see...
7360 if (isAppend) {
7361 // Use append logic when appending
7362 AppendFirstLineFrames(state, containingBlock->GetContent(),
7363 containingBlock, frameItems);
7364 }
7365 else {
7366 // Use more complicated insert logic when inserting
7367 // XXXbz this method is a no-op, so it's easy for the args being passed
7368 // here to make no sense without anyone noticing... If it ever stops
7369 // being a no-op, vet them carefully!
7370 InsertFirstLineFrames(state, container, containingBlock, &parentFrame,
7371 prevSibling, frameItems);
7372 }
7373 }
7374
7375 // We might have captions; put them into the caption list of the
7376 // outer table frame.
7377 if (captionItems.NotEmpty()) {
7378 NS_ASSERTION(nsGkAtoms::tableFrame == frameType ||
7379 nsGkAtoms::tableOuterFrame == frameType,
7380 "parent for caption is not table?");
7381 // We need to determine where to put the caption items; start with the
7382 // the parent frame that has already been determined and get the insertion
7383 // prevsibling of the first caption item.
7384 nsIFrame* captionParent = parentFrame;
7385 bool captionIsAppend;
7386 nsIFrame* captionPrevSibling = nullptr;
7387
7388 // aIsRangeInsertSafe is ignored on purpose because it is irrelevant here.
7389 bool ignored;
7390 if (isSingleInsert) {
7391 captionPrevSibling =
7392 GetInsertionPrevSibling(captionParent, aContainer, aStartChild,
7393 &captionIsAppend, &ignored);
7394 } else {
7395 nsIContent* firstCaption = captionItems.FirstChild()->GetContent();
7396 // It is very important here that we skip the children in
7397 // [aStartChild,aEndChild) when looking for a
7398 // prevsibling.
7399 captionPrevSibling =
7400 GetInsertionPrevSibling(captionParent, aContainer, firstCaption,
7401 &captionIsAppend, &ignored,
7402 aStartChild, aEndChild);
7403 }
7404
7405 nsIFrame* outerTable = nullptr;
7406 if (GetCaptionAdjustedParent(captionParent, captionItems.FirstChild(),
7407 &outerTable)) {
7408 // If the parent is not an outer table frame we will try to add frames
7409 // to a named child list that the parent does not honour and the frames
7410 // will get lost
7411 NS_ASSERTION(nsGkAtoms::tableOuterFrame == outerTable->GetType(),
7412 "Pseudo frame construction failure; "
7413 "a caption can be only a child of an outer table frame");
7414
7415 // If the parent of our current prevSibling is different from the frame
7416 // we'll actually use as the parent, then the calculated insertion
7417 // point is now invalid (bug 341382).
7418 if (captionPrevSibling &&
7419 captionPrevSibling->GetParent() != outerTable) {
7420 captionPrevSibling = nullptr;
7421 }
7422 if (captionIsAppend) {
7423 AppendFrames(outerTable, nsIFrame::kCaptionList, captionItems);
7424 } else {
7425 InsertFrames(outerTable, nsIFrame::kCaptionList,
7426 captionPrevSibling, captionItems);
7427 }
7428 }
7429 }
7430
7431 if (frameItems.NotEmpty()) {
7432 // Notify the parent frame
7433 if (isAppend) {
7434 AppendFramesToParent(state, parentFrame, frameItems, prevSibling);
7435 } else {
7436 InsertFrames(parentFrame, kPrincipalList, prevSibling, frameItems);
7437 }
7438 }
7439
7440 if (haveFirstLetterStyle) {
7441 // Recover the letter frames for the containing block when
7442 // it has first-letter style.
7443 RecoverLetterFrames(state.mFloatedItems.containingBlock);
7444 }
7445
7446 #ifdef DEBUG
7447 if (gReallyNoisyContentUpdates && parentFrame) {
7448 printf("nsCSSFrameConstructor::ContentRangeInserted: resulting frame model:\n");
7449 parentFrame->List(stdout, 0);
7450 }
7451 #endif
7452
7453 #ifdef ACCESSIBILITY
7454 nsAccessibilityService* accService = nsIPresShell::AccService();
7455 if (accService) {
7456 accService->ContentRangeInserted(mPresShell, aContainer,
7457 aStartChild, aEndChild);
7458 }
7459 #endif
7460
7461 return NS_OK;
7462 }
7463
7464 nsresult
7465 nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
7466 nsIContent* aChild,
7467 nsIContent* aOldNextSibling,
7468 RemoveFlags aFlags,
7469 bool* aDidReconstruct)
7470 {
7471 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
7472 NS_PRECONDITION(mUpdateCount != 0,
7473 "Should be in an update while destroying frames");
7474
7475 *aDidReconstruct = false;
7476
7477 // XXXldb Do we need to re-resolve style to handle the CSS2 + combinator and
7478 // the :empty pseudo-class?
7479
7480 #ifdef DEBUG
7481 if (gNoisyContentUpdates) {
7482 printf("nsCSSFrameConstructor::ContentRemoved container=%p child=%p "
7483 "old-next-sibling=%p\n",
7484 static_cast<void*>(aContainer),
7485 static_cast<void*>(aChild),
7486 static_cast<void*>(aOldNextSibling));
7487 if (gReallyNoisyContentUpdates) {
7488 aContainer->List(stdout, 0);
7489 }
7490 }
7491 #endif
7492
7493 nsPresContext *presContext = mPresShell->GetPresContext();
7494 nsresult rv = NS_OK;
7495
7496 // Find the child frame that maps the content
7497 nsIFrame* childFrame = aChild->GetPrimaryFrame();
7498
7499 if (!childFrame || childFrame->GetContent() != aChild) {
7500 // XXXbz the GetContent() != aChild check is needed due to bug 135040.
7501 // Remove it once that's fixed.
7502 ClearUndisplayedContentIn(aChild, aContainer);
7503 }
7504
7505 #ifdef MOZ_XUL
7506 if (NotifyListBoxBody(presContext, aContainer, aChild, aOldNextSibling,
7507 mDocument, childFrame, CONTENT_REMOVED))
7508 return NS_OK;
7509
7510 #endif // MOZ_XUL
7511
7512 // If we're removing the root, then make sure to remove things starting at
7513 // the viewport's child instead of the primary frame (which might even be
7514 // null if the root had an XBL binding or display:none, even though the
7515 // frames above it got created). We do the adjustment after the childFrame
7516 // check above, because we do want to clear any undisplayed content we might
7517 // have for the root. Detecting removal of a root is a little exciting; in
7518 // particular, having a null aContainer is necessary but NOT sufficient. Due
7519 // to how we process reframes, the content node might not even be in our
7520 // document by now. So explicitly check whether the viewport's first kid's
7521 // content node is aChild.
7522 bool isRoot = false;
7523 if (!aContainer) {
7524 nsIFrame* viewport = GetRootFrame();
7525 if (viewport) {
7526 nsIFrame* firstChild = viewport->GetFirstPrincipalChild();
7527 if (firstChild && firstChild->GetContent() == aChild) {
7528 isRoot = true;
7529 childFrame = firstChild;
7530 NS_ASSERTION(!childFrame->GetNextSibling(), "How did that happen?");
7531 }
7532 }
7533 }
7534
7535 if (aContainer && aContainer->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
7536 // Recreate frames if content is removed from a ShadowRoot
7537 // because it may contain an insertion point which can change
7538 // how the host is rendered.
7539 nsIContent* bindingParent = aContainer->GetBindingParent();
7540 *aDidReconstruct = true;
7541 LAYOUT_PHASE_TEMP_EXIT();
7542 nsresult rv = RecreateFramesForContent(bindingParent, false);
7543 LAYOUT_PHASE_TEMP_REENTER();
7544 return rv;
7545 }
7546
7547 if (childFrame) {
7548 InvalidateCanvasIfNeeded(mPresShell, aChild);
7549
7550 // See whether we need to remove more than just childFrame
7551 LAYOUT_PHASE_TEMP_EXIT();
7552 if (MaybeRecreateContainerForFrameRemoval(childFrame, &rv)) {
7553 LAYOUT_PHASE_TEMP_REENTER();
7554 *aDidReconstruct = true;
7555 return rv;
7556 }
7557 LAYOUT_PHASE_TEMP_REENTER();
7558
7559 // Get the childFrame's parent frame
7560 nsIFrame* parentFrame = childFrame->GetParent();
7561 nsIAtom* parentType = parentFrame->GetType();
7562
7563 if (parentType == nsGkAtoms::frameSetFrame &&
7564 IsSpecialFramesetChild(aChild)) {
7565 // Just reframe the parent, since framesets are weird like that.
7566 *aDidReconstruct = true;
7567 LAYOUT_PHASE_TEMP_EXIT();
7568 nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), false);
7569 LAYOUT_PHASE_TEMP_REENTER();
7570 return rv;
7571 }
7572
7573 // If we're a child of MathML, then we should reframe the MathML content.
7574 // If we're non-MathML, then we would be wrapped in a block so we need to
7575 // check our grandparent in that case.
7576 nsIFrame* possibleMathMLAncestor = parentType == nsGkAtoms::blockFrame ?
7577 parentFrame->GetParent() : parentFrame;
7578 if (possibleMathMLAncestor->IsFrameOfType(nsIFrame::eMathML)) {
7579 *aDidReconstruct = true;
7580 LAYOUT_PHASE_TEMP_EXIT();
7581 nsresult rv = RecreateFramesForContent(possibleMathMLAncestor->GetContent(), false);
7582 LAYOUT_PHASE_TEMP_REENTER();
7583 return rv;
7584 }
7585
7586 // Undo XUL wrapping if it's no longer needed.
7587 // (If we're in the XUL block-wrapping situation, parentFrame is the
7588 // wrapper frame.)
7589 nsIFrame* grandparentFrame = parentFrame->GetParent();
7590 if (grandparentFrame && grandparentFrame->IsBoxFrame() &&
7591 (grandparentFrame->GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK) &&
7592 // check if this frame is the only one needing wrapping
7593 aChild == AnyKidsNeedBlockParent(parentFrame->GetFirstPrincipalChild()) &&
7594 !AnyKidsNeedBlockParent(childFrame->GetNextSibling())) {
7595 *aDidReconstruct = true;
7596 LAYOUT_PHASE_TEMP_EXIT();
7597 nsresult rv = RecreateFramesForContent(grandparentFrame->GetContent(), true);
7598 LAYOUT_PHASE_TEMP_REENTER();
7599 return rv;
7600 }
7601
7602 #ifdef ACCESSIBILITY
7603 nsAccessibilityService* accService = nsIPresShell::AccService();
7604 if (accService) {
7605 accService->ContentRemoved(mPresShell, aContainer, aChild);
7606 }
7607 #endif
7608
7609 // Examine the containing-block for the removed content and see if
7610 // :first-letter style applies.
7611 nsIFrame* inflowChild = childFrame;
7612 if (childFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
7613 inflowChild = GetPlaceholderFrameFor(childFrame);
7614 NS_ASSERTION(inflowChild, "No placeholder for out-of-flow?");
7615 }
7616 nsIFrame* containingBlock =
7617 GetFloatContainingBlock(inflowChild->GetParent());
7618 bool haveFLS = containingBlock && HasFirstLetterStyle(containingBlock);
7619 if (haveFLS) {
7620 // Trap out to special routine that handles adjusting a blocks
7621 // frame tree when first-letter style is present.
7622 #ifdef NOISY_FIRST_LETTER
7623 printf("ContentRemoved: containingBlock=");
7624 nsFrame::ListTag(stdout, containingBlock);
7625 printf(" parentFrame=");
7626 nsFrame::ListTag(stdout, parentFrame);
7627 printf(" childFrame=");
7628 nsFrame::ListTag(stdout, childFrame);
7629 printf("\n");
7630 #endif
7631
7632 // First update the containing blocks structure by removing the
7633 // existing letter frames. This makes the subsequent logic
7634 // simpler.
7635 RemoveLetterFrames(presContext, mPresShell, containingBlock);
7636
7637 // Recover childFrame and parentFrame
7638 childFrame = aChild->GetPrimaryFrame();
7639 if (!childFrame || childFrame->GetContent() != aChild) {
7640 // XXXbz the GetContent() != aChild check is needed due to bug 135040.
7641 // Remove it once that's fixed.
7642 ClearUndisplayedContentIn(aChild, aContainer);
7643 return NS_OK;
7644 }
7645 parentFrame = childFrame->GetParent();
7646 parentType = parentFrame->GetType();
7647
7648 #ifdef NOISY_FIRST_LETTER
7649 printf(" ==> revised parentFrame=");
7650 nsFrame::ListTag(stdout, parentFrame);
7651 printf(" childFrame=");
7652 nsFrame::ListTag(stdout, childFrame);
7653 printf("\n");
7654 #endif
7655 }
7656
7657 #ifdef DEBUG
7658 if (gReallyNoisyContentUpdates) {
7659 printf("nsCSSFrameConstructor::ContentRemoved: childFrame=");
7660 nsFrame::ListTag(stdout, childFrame);
7661 putchar('\n');
7662 parentFrame->List(stdout, 0);
7663 }
7664 #endif
7665
7666
7667 // Notify the parent frame that it should delete the frame
7668 if (childFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
7669 childFrame = GetPlaceholderFrameFor(childFrame);
7670 NS_ASSERTION(childFrame, "Missing placeholder frame for out of flow.");
7671 parentFrame = childFrame->GetParent();
7672 }
7673 rv = RemoveFrame(nsLayoutUtils::GetChildListNameFor(childFrame),
7674 childFrame);
7675 //XXXfr NS_ENSURE_SUCCESS(rv, rv) ?
7676
7677 if (isRoot) {
7678 mRootElementFrame = nullptr;
7679 mRootElementStyleFrame = nullptr;
7680 mDocElementContainingBlock = nullptr;
7681 mPageSequenceFrame = nullptr;
7682 mGfxScrollFrame = nullptr;
7683 mHasRootAbsPosContainingBlock = false;
7684 mFixedContainingBlock = GetRootFrame();
7685 }
7686
7687 if (haveFLS && mRootElementFrame) {
7688 RecoverLetterFrames(containingBlock);
7689 }
7690
7691 // If we're just reconstructing frames for the element, then the
7692 // following ContentInserted notification on the element will
7693 // take care of fixing up any adjacent text nodes. We don't need
7694 // to do this if the table parent type of our parent type is not
7695 // eTypeBlock, though, because in that case the whitespace isn't
7696 // being suppressed due to us anyway.
7697 if (aContainer && !aChild->IsRootOfAnonymousSubtree() &&
7698 aFlags != REMOVE_FOR_RECONSTRUCTION &&
7699 GetParentType(parentType) == eTypeBlock) {
7700 // Adjacent whitespace-only text nodes might have been suppressed if
7701 // this node does not have inline ends. Create frames for them now
7702 // if necessary.
7703 // Reframe any text node just before the node being removed, if there is
7704 // one, and if it's not the last child or the first child. If a whitespace
7705 // textframe was being suppressed and it's now the last child or first
7706 // child then it can stay suppressed since the parent must be a block
7707 // and hence it's adjacent to a block end.
7708 // If aOldNextSibling is null, then the text node before the node being
7709 // removed is the last node, and we don't need to worry about it.
7710 if (aOldNextSibling) {
7711 nsIContent* prevSibling = aOldNextSibling->GetPreviousSibling();
7712 if (prevSibling && prevSibling->GetPreviousSibling()) {
7713 LAYOUT_PHASE_TEMP_EXIT();
7714 ReframeTextIfNeeded(aContainer, prevSibling);
7715 LAYOUT_PHASE_TEMP_REENTER();
7716 }
7717 }
7718 // Reframe any text node just after the node being removed, if there is
7719 // one, and if it's not the last child or the first child.
7720 if (aOldNextSibling && aOldNextSibling->GetNextSibling() &&
7721 aOldNextSibling->GetPreviousSibling()) {
7722 LAYOUT_PHASE_TEMP_EXIT();
7723 ReframeTextIfNeeded(aContainer, aOldNextSibling);
7724 LAYOUT_PHASE_TEMP_REENTER();
7725 }
7726 }
7727
7728 #ifdef DEBUG
7729 if (gReallyNoisyContentUpdates && parentFrame) {
7730 printf("nsCSSFrameConstructor::ContentRemoved: resulting frame model:\n");
7731 parentFrame->List(stdout, 0);
7732 }
7733 #endif
7734 }
7735
7736 return rv;
7737 }
7738
7739 /**
7740 * This method invalidates the canvas when frames are removed or added for a
7741 * node that might have its background propagated to the canvas, i.e., a
7742 * document root node or an HTML BODY which is a child of the root node.
7743 *
7744 * @param aFrame a frame for a content node about to be removed or a frame that
7745 * was just created for a content node that was inserted.
7746 */
7747 static void
7748 InvalidateCanvasIfNeeded(nsIPresShell* presShell, nsIContent* node)
7749 {
7750 NS_PRECONDITION(presShell->GetRootFrame(), "What happened here?");
7751 NS_PRECONDITION(presShell->GetPresContext(), "Say what?");
7752
7753 // Note that both in ContentRemoved and ContentInserted the content node
7754 // will still have the right parent pointer, so looking at that is ok.
7755
7756 nsIContent* parent = node->GetParent();
7757 if (parent) {
7758 // Has a parent; might not be what we want
7759 nsIContent* grandParent = parent->GetParent();
7760 if (grandParent) {
7761 // Has a grandparent, so not what we want
7762 return;
7763 }
7764
7765 // Check whether it's an HTML body
7766 if (node->Tag() != nsGkAtoms::body ||
7767 !node->IsHTML()) {
7768 return;
7769 }
7770 }
7771
7772 // At this point the node has no parent or it's an HTML <body> child of the
7773 // root. We might not need to invalidate in this case (eg we might be in
7774 // XHTML or something), but chances are we want to. Play it safe.
7775 // Invalidate the viewport.
7776
7777 nsIFrame* rootFrame = presShell->GetRootFrame();
7778 rootFrame->InvalidateFrameSubtree();
7779 }
7780
7781 nsIFrame*
7782 nsCSSFrameConstructor::EnsureFrameForTextNode(nsGenericDOMDataNode* aContent)
7783 {
7784 if (aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) &&
7785 !mAlwaysCreateFramesForIgnorableWhitespace) {
7786 // Text frame may have been suppressed. Disable suppression and signal
7787 // that a flush should be performed. We do this on a document-wide
7788 // basis so that pages that repeatedly query metrics for
7789 // collapsed-whitespace text nodes don't trigger pathological behavior.
7790 mAlwaysCreateFramesForIgnorableWhitespace = true;
7791 nsAutoScriptBlocker blocker;
7792 BeginUpdate();
7793 ReconstructDocElementHierarchy();
7794 EndUpdate();
7795 }
7796 return aContent->GetPrimaryFrame();
7797 }
7798
7799 nsresult
7800 nsCSSFrameConstructor::CharacterDataChanged(nsIContent* aContent,
7801 CharacterDataChangeInfo* aInfo)
7802 {
7803 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
7804 nsresult rv = NS_OK;
7805
7806 if ((aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) &&
7807 !aContent->TextIsOnlyWhitespace()) ||
7808 (aContent->HasFlag(NS_REFRAME_IF_WHITESPACE) &&
7809 aContent->TextIsOnlyWhitespace())) {
7810 #ifdef DEBUG
7811 nsIFrame* frame = aContent->GetPrimaryFrame();
7812 NS_ASSERTION(!frame || !frame->IsGeneratedContentFrame(),
7813 "Bit should never be set on generated content");
7814 #endif
7815 LAYOUT_PHASE_TEMP_EXIT();
7816 nsresult rv = RecreateFramesForContent(aContent, false);
7817 LAYOUT_PHASE_TEMP_REENTER();
7818 return rv;
7819 }
7820
7821 // Find the child frame
7822 nsIFrame* frame = aContent->GetPrimaryFrame();
7823
7824 // Notify the first frame that maps the content. It will generate a reflow
7825 // command
7826
7827 // It's possible the frame whose content changed isn't inserted into the
7828 // frame hierarchy yet, or that there is no frame that maps the content
7829 if (nullptr != frame) {
7830 #if 0
7831 NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
7832 ("nsCSSFrameConstructor::CharacterDataChanged: content=%p[%s] subcontent=%p frame=%p",
7833 aContent, ContentTag(aContent, 0),
7834 aSubContent, frame));
7835 #endif
7836
7837 // Special check for text content that is a child of a letter frame. If
7838 // this happens, we should remove the letter frame, do whatever we're
7839 // planning to do with this notification, then put the letter frame back.
7840 // Note that this is basically what RecreateFramesForContent ends up doing;
7841 // the reason we dont' want to call that here is that our text content
7842 // could be native anonymous, in which case RecreateFramesForContent would
7843 // completely barf on it. And recreating the non-anonymous ancestor would
7844 // just lead us to come back into this notification (e.g. if quotes or
7845 // counters are involved), leading to a loop.
7846 nsIFrame* block = GetFloatContainingBlock(frame);
7847 bool haveFirstLetterStyle = false;
7848 if (block) {
7849 // See if the block has first-letter style applied to it.
7850 haveFirstLetterStyle = HasFirstLetterStyle(block);
7851 if (haveFirstLetterStyle) {
7852 RemoveLetterFrames(mPresShell->GetPresContext(), mPresShell,
7853 block);
7854 // Reget |frame|, since we might have killed it.
7855 // Do we really need to call CharacterDataChanged in this case, though?
7856 frame = aContent->GetPrimaryFrame();
7857 NS_ASSERTION(frame, "Should have frame here!");
7858 }
7859 }
7860
7861 frame->CharacterDataChanged(aInfo);
7862
7863 if (haveFirstLetterStyle) {
7864 RecoverLetterFrames(block);
7865 }
7866 }
7867
7868 return rv;
7869 }
7870
7871 void
7872 nsCSSFrameConstructor::BeginUpdate() {
7873 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
7874 "Someone forgot a script blocker");
7875
7876 nsRootPresContext* rootPresContext =
7877 mPresShell->GetPresContext()->GetRootPresContext();
7878 if (rootPresContext) {
7879 rootPresContext->IncrementDOMGeneration();
7880 }
7881
7882 ++sGlobalGenerationNumber;
7883 ++mUpdateCount;
7884 }
7885
7886 void
7887 nsCSSFrameConstructor::EndUpdate()
7888 {
7889 if (mUpdateCount == 1) {
7890 // This is the end of our last update. Before we decrement
7891 // mUpdateCount, recalc quotes and counters as needed.
7892
7893 RecalcQuotesAndCounters();
7894 NS_ASSERTION(mUpdateCount == 1, "Odd update count");
7895 }
7896 NS_ASSERTION(mUpdateCount, "Negative mUpdateCount!");
7897 --mUpdateCount;
7898 }
7899
7900 void
7901 nsCSSFrameConstructor::RecalcQuotesAndCounters()
7902 {
7903 if (mQuotesDirty) {
7904 mQuotesDirty = false;
7905 mQuoteList.RecalcAll();
7906 }
7907
7908 if (mCountersDirty) {
7909 mCountersDirty = false;
7910 mCounterManager.RecalcAll();
7911 }
7912
7913 NS_ASSERTION(!mQuotesDirty, "Quotes updates will be lost");
7914 NS_ASSERTION(!mCountersDirty, "Counter updates will be lost");
7915 }
7916
7917 void
7918 nsCSSFrameConstructor::WillDestroyFrameTree()
7919 {
7920 #if defined(DEBUG_dbaron_off)
7921 mCounterManager.Dump();
7922 #endif
7923
7924 mIsDestroyingFrameTree = true;
7925
7926 // Prevent frame tree destruction from being O(N^2)
7927 mQuoteList.Clear();
7928 mCounterManager.Clear();
7929
7930 // Remove our presshell as a style flush observer. But leave
7931 // RestyleManager::mObservingRefreshDriver true so we don't readd to
7932 // it even if someone tries to post restyle events on us from this
7933 // point on for some reason.
7934 mPresShell->GetPresContext()->RefreshDriver()->
7935 RemoveStyleFlushObserver(mPresShell);
7936
7937 nsFrameManager::Destroy();
7938 }
7939
7940 //STATIC
7941
7942 // XXXbz I'd really like this method to go away. Once we have inline-block and
7943 // I can just use that for sized broken images, that can happen, maybe.
7944 void nsCSSFrameConstructor::GetAlternateTextFor(nsIContent* aContent,
7945 nsIAtom* aTag, // content object's tag
7946 nsXPIDLString& aAltText)
7947 {
7948 // The "alt" attribute specifies alternate text that is rendered
7949 // when the image can not be displayed
7950
7951 // If there's no "alt" attribute, and aContent is an input
7952 // element, then use the value of the "value" attribute
7953 if (!aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aAltText) &&
7954 nsGkAtoms::input == aTag) {
7955 // If there's no "value" attribute either, then use the localized string
7956 // for "Submit" as the alternate text.
7957 if (!aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aAltText)) {
7958 nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
7959 "Submit", aAltText);
7960 }
7961 }
7962 }
7963
7964 nsIFrame*
7965 nsCSSFrameConstructor::CreateContinuingOuterTableFrame(nsIPresShell* aPresShell,
7966 nsPresContext* aPresContext,
7967 nsIFrame* aFrame,
7968 nsIFrame* aParentFrame,
7969 nsIContent* aContent,
7970 nsStyleContext* aStyleContext)
7971 {
7972 nsIFrame* newFrame = NS_NewTableOuterFrame(aPresShell, aStyleContext);
7973
7974 newFrame->Init(aContent, aParentFrame, aFrame);
7975
7976 // Create a continuing inner table frame, and if there's a caption then
7977 // replicate the caption
7978 nsFrameItems newChildFrames;
7979
7980 nsIFrame* childFrame = aFrame->GetFirstPrincipalChild();
7981 if (childFrame) {
7982 nsIFrame* continuingTableFrame =
7983 CreateContinuingFrame(aPresContext, childFrame, newFrame);
7984 newChildFrames.AddChild(continuingTableFrame);
7985
7986 NS_ASSERTION(!childFrame->GetNextSibling(),"there can be only one inner table frame");
7987 }
7988
7989 // Set the outer table's initial child list
7990 newFrame->SetInitialChildList(kPrincipalList, newChildFrames);
7991
7992 return newFrame;
7993 }
7994
7995 nsIFrame*
7996 nsCSSFrameConstructor::CreateContinuingTableFrame(nsIPresShell* aPresShell,
7997 nsPresContext* aPresContext,
7998 nsIFrame* aFrame,
7999 nsIFrame* aParentFrame,
8000 nsIContent* aContent,
8001 nsStyleContext* aStyleContext)
8002 {
8003 nsIFrame* newFrame = NS_NewTableFrame(aPresShell, aStyleContext);
8004
8005 newFrame->Init(aContent, aParentFrame, aFrame);
8006
8007 // Replicate any header/footer frames
8008 nsFrameItems childFrames;
8009 nsIFrame* childFrame = aFrame->GetFirstPrincipalChild();
8010 for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
8011 // See if it's a header/footer, possibly wrapped in a scroll frame.
8012 nsTableRowGroupFrame* rowGroupFrame =
8013 static_cast<nsTableRowGroupFrame*>(childFrame);
8014 // If the row group was continued, then don't replicate it.
8015 nsIFrame* rgNextInFlow = rowGroupFrame->GetNextInFlow();
8016 if (rgNextInFlow) {
8017 rowGroupFrame->SetRepeatable(false);
8018 }
8019 else if (rowGroupFrame->IsRepeatable()) {
8020 // Replicate the header/footer frame.
8021 nsTableRowGroupFrame* headerFooterFrame;
8022 nsFrameItems childItems;
8023 nsFrameConstructorState state(mPresShell,
8024 GetAbsoluteContainingBlock(newFrame, FIXED_POS),
8025 GetAbsoluteContainingBlock(newFrame, ABS_POS),
8026 nullptr);
8027 state.mCreatingExtraFrames = true;
8028
8029 nsStyleContext* const headerFooterStyleContext = rowGroupFrame->StyleContext();
8030 headerFooterFrame = static_cast<nsTableRowGroupFrame*>
8031 (NS_NewTableRowGroupFrame(aPresShell, headerFooterStyleContext));
8032
8033 nsIContent* headerFooter = rowGroupFrame->GetContent();
8034 headerFooterFrame->Init(headerFooter, newFrame, nullptr);
8035
8036 nsFrameConstructorSaveState absoluteSaveState;
8037 MakeTablePartAbsoluteContainingBlockIfNeeded(state,
8038 headerFooterStyleContext->StyleDisplay(),
8039 absoluteSaveState,
8040 headerFooterFrame);
8041
8042 ProcessChildren(state, headerFooter, rowGroupFrame->StyleContext(),
8043 headerFooterFrame, true, childItems, false,
8044 nullptr);
8045 NS_ASSERTION(state.mFloatedItems.IsEmpty(), "unexpected floated element");
8046 headerFooterFrame->SetInitialChildList(kPrincipalList, childItems);
8047 headerFooterFrame->SetRepeatable(true);
8048
8049 // Table specific initialization
8050 headerFooterFrame->InitRepeatedFrame(aPresContext, rowGroupFrame);
8051
8052 // XXX Deal with absolute and fixed frames...
8053 childFrames.AddChild(headerFooterFrame);
8054 }
8055 }
8056
8057 // Set the table frame's initial child list
8058 newFrame->SetInitialChildList(kPrincipalList, childFrames);
8059
8060 return newFrame;
8061 }
8062
8063 nsIFrame*
8064 nsCSSFrameConstructor::CreateContinuingFrame(nsPresContext* aPresContext,
8065 nsIFrame* aFrame,
8066 nsIFrame* aParentFrame,
8067 bool aIsFluid)
8068 {
8069 nsIPresShell* shell = aPresContext->PresShell();
8070 nsStyleContext* styleContext = aFrame->StyleContext();
8071 nsIFrame* newFrame = nullptr;
8072 nsIFrame* nextContinuation = aFrame->GetNextContinuation();
8073 nsIFrame* nextInFlow = aFrame->GetNextInFlow();
8074
8075 // Use the frame type to determine what type of frame to create
8076 nsIAtom* frameType = aFrame->GetType();
8077 nsIContent* content = aFrame->GetContent();
8078
8079 NS_ASSERTION(aFrame->GetSplittableType() != NS_FRAME_NOT_SPLITTABLE,
8080 "why CreateContinuingFrame for a non-splittable frame?");
8081
8082 if (nsGkAtoms::textFrame == frameType) {
8083 newFrame = NS_NewContinuingTextFrame(shell, styleContext);
8084 newFrame->Init(content, aParentFrame, aFrame);
8085 } else if (nsGkAtoms::inlineFrame == frameType) {
8086 newFrame = NS_NewInlineFrame(shell, styleContext);
8087 newFrame->Init(content, aParentFrame, aFrame);
8088 } else if (nsGkAtoms::blockFrame == frameType) {
8089 newFrame = NS_NewBlockFrame(shell, styleContext);
8090 newFrame->Init(content, aParentFrame, aFrame);
8091 #ifdef MOZ_XUL
8092 } else if (nsGkAtoms::XULLabelFrame == frameType) {
8093 newFrame = NS_NewXULLabelFrame(shell, styleContext);
8094 newFrame->Init(content, aParentFrame, aFrame);
8095 #endif
8096 } else if (nsGkAtoms::columnSetFrame == frameType) {
8097 newFrame = NS_NewColumnSetFrame(shell, styleContext, nsFrameState(0));
8098 newFrame->Init(content, aParentFrame, aFrame);
8099 } else if (nsGkAtoms::pageFrame == frameType) {
8100 nsIFrame* canvasFrame;
8101 newFrame = ConstructPageFrame(shell, aPresContext, aParentFrame, aFrame,
8102 canvasFrame);
8103 } else if (nsGkAtoms::tableOuterFrame == frameType) {
8104 newFrame =
8105 CreateContinuingOuterTableFrame(shell, aPresContext, aFrame, aParentFrame,
8106 content, styleContext);
8107
8108 } else if (nsGkAtoms::tableFrame == frameType) {
8109 newFrame =
8110 CreateContinuingTableFrame(shell, aPresContext, aFrame, aParentFrame,
8111 content, styleContext);
8112
8113 } else if (nsGkAtoms::tableRowGroupFrame == frameType) {
8114 newFrame = NS_NewTableRowGroupFrame(shell, styleContext);
8115 newFrame->Init(content, aParentFrame, aFrame);
8116 if (newFrame->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
8117 nsTableFrame::RegisterPositionedTablePart(newFrame);
8118 }
8119 } else if (nsGkAtoms::tableRowFrame == frameType) {
8120 newFrame = NS_NewTableRowFrame(shell, styleContext);
8121
8122 newFrame->Init(content, aParentFrame, aFrame);
8123 if (newFrame->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
8124 nsTableFrame::RegisterPositionedTablePart(newFrame);
8125 }
8126
8127 // Create a continuing frame for each table cell frame
8128 nsFrameItems newChildList;
8129 nsIFrame* cellFrame = aFrame->GetFirstPrincipalChild();
8130 while (cellFrame) {
8131 // See if it's a table cell frame
8132 if (IS_TABLE_CELL(cellFrame->GetType())) {
8133 nsIFrame* continuingCellFrame =
8134 CreateContinuingFrame(aPresContext, cellFrame, newFrame);
8135 newChildList.AddChild(continuingCellFrame);
8136 }
8137 cellFrame = cellFrame->GetNextSibling();
8138 }
8139
8140 // Set the table cell's initial child list
8141 newFrame->SetInitialChildList(kPrincipalList, newChildList);
8142
8143 } else if (IS_TABLE_CELL(frameType)) {
8144 // Warning: If you change this and add a wrapper frame around table cell
8145 // frames, make sure Bug 368554 doesn't regress!
8146 // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp.
8147 newFrame = NS_NewTableCellFrame(shell, styleContext, IsBorderCollapse(aParentFrame));
8148
8149 newFrame->Init(content, aParentFrame, aFrame);
8150 if (newFrame->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
8151 nsTableFrame::RegisterPositionedTablePart(newFrame);
8152 }
8153
8154 // Create a continuing area frame
8155 nsIFrame* blockFrame = aFrame->GetFirstPrincipalChild();
8156 nsIFrame* continuingBlockFrame =
8157 CreateContinuingFrame(aPresContext, blockFrame, newFrame);
8158
8159 // Set the table cell's initial child list
8160 SetInitialSingleChild(newFrame, continuingBlockFrame);
8161 } else if (nsGkAtoms::lineFrame == frameType) {
8162 newFrame = NS_NewFirstLineFrame(shell, styleContext);
8163 newFrame->Init(content, aParentFrame, aFrame);
8164 } else if (nsGkAtoms::letterFrame == frameType) {
8165 newFrame = NS_NewFirstLetterFrame(shell, styleContext);
8166 newFrame->Init(content, aParentFrame, aFrame);
8167 } else if (nsGkAtoms::imageFrame == frameType) {
8168 newFrame = NS_NewImageFrame(shell, styleContext);
8169 newFrame->Init(content, aParentFrame, aFrame);
8170 } else if (nsGkAtoms::imageControlFrame == frameType) {
8171 newFrame = NS_NewImageControlFrame(shell, styleContext);
8172 newFrame->Init(content, aParentFrame, aFrame);
8173 } else if (nsGkAtoms::placeholderFrame == frameType) {
8174 // create a continuing out of flow frame
8175 nsIFrame* oofFrame = nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
8176 nsIFrame* oofContFrame =
8177 CreateContinuingFrame(aPresContext, oofFrame, aParentFrame);
8178 newFrame =
8179 CreatePlaceholderFrameFor(shell, content, oofContFrame, styleContext,
8180 aParentFrame, aFrame,
8181 aFrame->GetStateBits() & PLACEHOLDER_TYPE_MASK);
8182 } else if (nsGkAtoms::fieldSetFrame == frameType) {
8183 newFrame = NS_NewFieldSetFrame(shell, styleContext);
8184
8185 newFrame->Init(content, aParentFrame, aFrame);
8186
8187 // Create a continuing area frame
8188 // XXXbz we really shouldn't have to do this by hand!
8189 nsIFrame* blockFrame = GetFieldSetBlockFrame(aFrame);
8190 if (blockFrame) {
8191 nsIFrame* continuingBlockFrame =
8192 CreateContinuingFrame(aPresContext, blockFrame, newFrame);
8193 // Set the fieldset's initial child list
8194 SetInitialSingleChild(newFrame, continuingBlockFrame);
8195 } else {
8196 MOZ_ASSERT(aFrame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER,
8197 "FieldSet block may only be null for overflow containers");
8198 }
8199 } else if (nsGkAtoms::legendFrame == frameType) {
8200 newFrame = NS_NewLegendFrame(shell, styleContext);
8201 newFrame->Init(content, aParentFrame, aFrame);
8202 } else if (nsGkAtoms::flexContainerFrame == frameType) {
8203 newFrame = NS_NewFlexContainerFrame(shell, styleContext);
8204 newFrame->Init(content, aParentFrame, aFrame);
8205 } else {
8206 NS_RUNTIMEABORT("unexpected frame type");
8207 }
8208
8209 // Init() set newFrame to be a fluid continuation of aFrame.
8210 // If we want a non-fluid continuation, we need to call SetPrevContinuation()
8211 // to reset NS_FRAME_IS_FLUID_CONTINUATION.
8212 if (!aIsFluid) {
8213 newFrame->SetPrevContinuation(aFrame);
8214 }
8215
8216 // A continuation of generated content is also generated content
8217 if (aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT) {
8218 newFrame->AddStateBits(NS_FRAME_GENERATED_CONTENT);
8219 }
8220
8221 // A continuation of nsIAnonymousContentCreator content is also
8222 // nsIAnonymousContentCreator created content
8223 if (aFrame->GetStateBits() & NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT) {
8224 newFrame->AddStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT);
8225 }
8226
8227 // A continuation of an out-of-flow is also an out-of-flow
8228 if (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
8229 newFrame->AddStateBits(NS_FRAME_OUT_OF_FLOW);
8230 }
8231
8232 if (nextInFlow) {
8233 nextInFlow->SetPrevInFlow(newFrame);
8234 newFrame->SetNextInFlow(nextInFlow);
8235 } else if (nextContinuation) {
8236 nextContinuation->SetPrevContinuation(newFrame);
8237 newFrame->SetNextContinuation(nextContinuation);
8238 }
8239
8240 NS_POSTCONDITION(!newFrame->GetNextSibling(), "unexpected sibling");
8241 return newFrame;
8242 }
8243
8244 nsresult
8245 nsCSSFrameConstructor::ReplicateFixedFrames(nsPageContentFrame* aParentFrame)
8246 {
8247 // Now deal with fixed-pos things.... They should appear on all pages,
8248 // so we want to move over the placeholders when processing the child
8249 // of the pageContentFrame.
8250
8251 nsIFrame* prevPageContentFrame = aParentFrame->GetPrevInFlow();
8252 if (!prevPageContentFrame) {
8253 return NS_OK;
8254 }
8255 nsIFrame* canvasFrame = aParentFrame->GetFirstPrincipalChild();
8256 nsIFrame* prevCanvasFrame = prevPageContentFrame->GetFirstPrincipalChild();
8257 if (!canvasFrame || !prevCanvasFrame) {
8258 // document's root element frame missing
8259 return NS_ERROR_UNEXPECTED;
8260 }
8261
8262 nsFrameItems fixedPlaceholders;
8263 nsIFrame* firstFixed = prevPageContentFrame->GetFirstChild(nsIFrame::kFixedList);
8264 if (!firstFixed) {
8265 return NS_OK;
8266 }
8267
8268 // Don't allow abs-pos descendants of the fixed content to escape the content.
8269 // This should not normally be possible (because fixed-pos elements should
8270 // be absolute containers) but fixed-pos tables currently aren't abs-pos
8271 // containers.
8272 nsFrameConstructorState state(mPresShell, aParentFrame,
8273 nullptr,
8274 mRootElementFrame);
8275 state.mCreatingExtraFrames = true;
8276
8277 // We can't use an ancestor filter here, because we're not going to
8278 // be usefully recurring down the tree. This means that other
8279 // places in frame construction can't assume a filter is
8280 // initialized!
8281
8282 // Iterate across fixed frames and replicate each whose placeholder is a
8283 // descendant of aFrame. (We don't want to explicitly copy placeholders that
8284 // are within fixed frames, because that would cause duplicates on the new
8285 // page - bug 389619)
8286 for (nsIFrame* fixed = firstFixed; fixed; fixed = fixed->GetNextSibling()) {
8287 nsIFrame* prevPlaceholder = GetPlaceholderFrameFor(fixed);
8288 if (prevPlaceholder &&
8289 nsLayoutUtils::IsProperAncestorFrame(prevCanvasFrame, prevPlaceholder)) {
8290 // We want to use the same style as the primary style frame for
8291 // our content
8292 nsIContent* content = fixed->GetContent();
8293 nsStyleContext* styleContext =
8294 nsLayoutUtils::GetStyleFrame(content)->StyleContext();
8295 FrameConstructionItemList items;
8296 AddFrameConstructionItemsInternal(state, content, canvasFrame,
8297 content->Tag(),
8298 content->GetNameSpaceID(),
8299 true,
8300 styleContext,
8301 ITEM_ALLOW_XBL_BASE |
8302 ITEM_ALLOW_PAGE_BREAK,
8303 nullptr, items);
8304 ConstructFramesFromItemList(state, items, canvasFrame, fixedPlaceholders);
8305 }
8306 }
8307
8308 // Add the placeholders to our primary child list.
8309 // XXXbz this is a little screwed up, since the fixed frames will have
8310 // broken auto-positioning. Oh, well.
8311 NS_ASSERTION(!canvasFrame->GetFirstPrincipalChild(),
8312 "leaking frames; doc root continuation must be empty");
8313 canvasFrame->SetInitialChildList(kPrincipalList, fixedPlaceholders);
8314 return NS_OK;
8315 }
8316
8317 nsIFrame*
8318 nsCSSFrameConstructor::GetInsertionPoint(nsIContent* aContainer,
8319 nsIContent* aChildContent,
8320 bool* aMultiple)
8321 {
8322 nsBindingManager *bindingManager = mDocument->BindingManager();
8323
8324 nsIContent* insertionElement;
8325 if (aChildContent) {
8326 // We've got an explicit insertion child. Check to see if it's
8327 // anonymous.
8328 if (aChildContent->GetBindingParent() == aContainer) {
8329 // This child content is anonymous. Don't use the insertion
8330 // point, since that's only for the explicit kids.
8331 return GetFrameFor(aContainer);
8332 }
8333
8334 insertionElement = bindingManager->FindNestedInsertionPoint(aContainer, aChildContent);
8335 }
8336 else {
8337 bool multiple;
8338 insertionElement = bindingManager->FindNestedSingleInsertionPoint(aContainer, &multiple);
8339
8340 if (multiple) {
8341 if (aMultiple) {
8342 *aMultiple = true;
8343 }
8344 return nullptr;
8345 }
8346 }
8347
8348 if (!insertionElement) {
8349 insertionElement = aContainer;
8350 }
8351
8352 nsIFrame* insertionPoint = GetFrameFor(insertionElement);
8353
8354 // fieldsets have multiple insertion points. Note that we might
8355 // have to look at insertionElement here...
8356 if (aMultiple && insertionElement->IsHTML(nsGkAtoms::fieldset)) {
8357 *aMultiple = true;
8358 }
8359
8360 return insertionPoint;
8361 }
8362
8363 // Capture state for the frame tree rooted at the frame associated with the
8364 // content object, aContent
8365 void
8366 nsCSSFrameConstructor::CaptureStateForFramesOf(nsIContent* aContent,
8367 nsILayoutHistoryState* aHistoryState)
8368 {
8369 if (!aHistoryState) {
8370 return;
8371 }
8372 nsIFrame* frame = aContent->GetPrimaryFrame();
8373 if (frame == mRootElementFrame) {
8374 frame = mFixedContainingBlock;
8375 }
8376 for ( ; frame;
8377 frame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame)) {
8378 CaptureFrameState(frame, aHistoryState);
8379 }
8380 }
8381
8382 static bool EqualURIs(mozilla::css::URLValue *aURI1,
8383 mozilla::css::URLValue *aURI2)
8384 {
8385 return aURI1 == aURI2 || // handle null==null, and optimize
8386 (aURI1 && aURI2 && aURI1->URIEquals(*aURI2));
8387 }
8388
8389 nsresult
8390 nsCSSFrameConstructor::MaybeRecreateFramesForElement(Element* aElement)
8391 {
8392 nsRefPtr<nsStyleContext> oldContext = GetUndisplayedContent(aElement);
8393 if (!oldContext) {
8394 return NS_OK;
8395 }
8396
8397 // The parent has a frame, so try resolving a new context.
8398 nsRefPtr<nsStyleContext> newContext = mPresShell->StyleSet()->
8399 ResolveStyleFor(aElement, oldContext->GetParent());
8400
8401 ChangeUndisplayedContent(aElement, newContext);
8402 const nsStyleDisplay* disp = newContext->StyleDisplay();
8403 if (disp->mDisplay == NS_STYLE_DISPLAY_NONE) {
8404 // We can skip trying to recreate frames here, but only if our style
8405 // context does not have a binding URI that differs from our old one.
8406 // Otherwise, we should try to recreate, because we may want to apply the
8407 // new binding
8408 if (!disp->mBinding) {
8409 return NS_OK;
8410 }
8411 const nsStyleDisplay* oldDisp = oldContext->PeekStyleDisplay();
8412 if (oldDisp && EqualURIs(disp->mBinding, oldDisp->mBinding)) {
8413 return NS_OK;
8414 }
8415 }
8416
8417 return RecreateFramesForContent(aElement, false);
8418 }
8419
8420 static nsIFrame*
8421 FindFirstNonWhitespaceChild(nsIFrame* aParentFrame)
8422 {
8423 nsIFrame* f = aParentFrame->GetFirstPrincipalChild();
8424 while (f && f->GetType() == nsGkAtoms::textFrame &&
8425 f->GetContent()->TextIsOnlyWhitespace()) {
8426 f = f->GetNextSibling();
8427 }
8428 return f;
8429 }
8430
8431 static nsIFrame*
8432 FindNextNonWhitespaceSibling(nsIFrame* aFrame)
8433 {
8434 nsIFrame* f = aFrame;
8435 do {
8436 f = f->GetNextSibling();
8437 } while (f && f->GetType() == nsGkAtoms::textFrame &&
8438 f->GetContent()->TextIsOnlyWhitespace());
8439 return f;
8440 }
8441
8442 static nsIFrame*
8443 FindPreviousNonWhitespaceSibling(nsIFrame* aFrame)
8444 {
8445 nsIFrame* f = aFrame;
8446 do {
8447 f = f->GetPrevSibling();
8448 } while (f && f->GetType() == nsGkAtoms::textFrame &&
8449 f->GetContent()->TextIsOnlyWhitespace());
8450 return f;
8451 }
8452
8453 bool
8454 nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame,
8455 nsresult* aResult)
8456 {
8457 NS_PRECONDITION(aFrame, "Must have a frame");
8458 NS_PRECONDITION(aFrame->GetParent(), "Frame shouldn't be root");
8459 NS_PRECONDITION(aResult, "Null out param?");
8460 NS_PRECONDITION(aFrame == aFrame->FirstContinuation(),
8461 "aFrame not the result of GetPrimaryFrame()?");
8462
8463 if (IsFramePartOfIBSplit(aFrame)) {
8464 // The removal functions can't handle removal of an {ib} split directly; we
8465 // need to rebuild the containing block.
8466 #ifdef DEBUG
8467 if (gNoisyContentUpdates) {
8468 printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
8469 "frame=");
8470 nsFrame::ListTag(stdout, aFrame);
8471 printf(" is ib-split\n");
8472 }
8473 #endif
8474
8475 *aResult = ReframeContainingBlock(aFrame);
8476 return true;
8477 }
8478
8479 if (aFrame->GetContentInsertionFrame()->GetType() == nsGkAtoms::legendFrame &&
8480 aFrame->GetParent()->GetType() == nsGkAtoms::fieldSetFrame) {
8481 // When we remove the legend for a fieldset, we should reframe
8482 // the fieldset to ensure another legend is used, if there is one
8483 *aResult = RecreateFramesForContent(aFrame->GetParent()->GetContent(), false);
8484 return true;
8485 }
8486
8487 // Now check for possibly needing to reconstruct due to a pseudo parent
8488 nsIFrame* inFlowFrame =
8489 (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) ?
8490 GetPlaceholderFrameFor(aFrame) : aFrame;
8491 MOZ_ASSERT(inFlowFrame, "How did that happen?");
8492 MOZ_ASSERT(inFlowFrame == inFlowFrame->FirstContinuation(),
8493 "placeholder for primary frame has previous continuations?");
8494 nsIFrame* parent = inFlowFrame->GetParent();
8495 if (IsTablePseudo(parent)) {
8496 if (FindFirstNonWhitespaceChild(parent) == inFlowFrame ||
8497 !FindNextNonWhitespaceSibling(inFlowFrame->LastContinuation()) ||
8498 // If we're a table-column-group, then the GetFirstChild check above is
8499 // not going to catch cases when we're the first child.
8500 (inFlowFrame->GetType() == nsGkAtoms::tableColGroupFrame &&
8501 parent->GetFirstChild(nsIFrame::kColGroupList) == inFlowFrame) ||
8502 // Similar if we're a table-caption.
8503 (inFlowFrame->GetType() == nsGkAtoms::tableCaptionFrame &&
8504 parent->GetFirstChild(nsIFrame::kCaptionList) == inFlowFrame)) {
8505 // We're the first or last frame in the pseudo. Need to reframe.
8506 // Good enough to recreate frames for |parent|'s content
8507 *aResult = RecreateFramesForContent(parent->GetContent(), true);
8508 return true;
8509 }
8510 }
8511
8512 // Might need to reconstruct things if this frame's nextSibling is a table
8513 // pseudo, since removal of this frame might mean that this pseudo needs to
8514 // get merged with the frame's prevSibling if that's also a table pseudo.
8515 nsIFrame* nextSibling =
8516 FindNextNonWhitespaceSibling(inFlowFrame->LastContinuation());
8517 NS_ASSERTION(!IsTablePseudo(inFlowFrame), "Shouldn't happen here");
8518 if (nextSibling && IsTablePseudo(nextSibling)) {
8519 nsIFrame* prevSibling = FindPreviousNonWhitespaceSibling(inFlowFrame);
8520 if (prevSibling && IsTablePseudo(prevSibling)) {
8521 #ifdef DEBUG
8522 if (gNoisyContentUpdates) {
8523 printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
8524 "frame=");
8525 nsFrame::ListTag(stdout, aFrame);
8526 printf(" has a table pseudo next sibling of different type and a "
8527 "table pseudo prevsibling\n");
8528 }
8529 #endif
8530 // Good enough to recreate frames for aFrame's parent's content; even if
8531 // aFrame's parent is a table pseudo, that'll be the right content node.
8532 *aResult = RecreateFramesForContent(parent->GetContent(), true);
8533 return true;
8534 }
8535 }
8536
8537 // Might need to reconstruct things if the removed frame's nextSibling is an
8538 // anonymous flex item. The removed frame might've been what divided two
8539 // runs of inline content into two anonymous flex items, which would now
8540 // need to be merged.
8541 // NOTE: It's fine that we've advanced nextSibling past whitespace (up above);
8542 // we're only interested in anonymous flex items here, and those can never
8543 // be adjacent to whitespace, since they absorb contiguous runs of inline
8544 // non-replaced content (including whitespace).
8545 if (nextSibling && IsAnonymousFlexItem(nextSibling)) {
8546 NS_ABORT_IF_FALSE(parent->GetType() == nsGkAtoms::flexContainerFrame,
8547 "anonymous flex items should only exist as children "
8548 "of flex container frames");
8549 #ifdef DEBUG
8550 if (gNoisyContentUpdates) {
8551 printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
8552 "frame=");
8553 nsFrame::ListTag(stdout, aFrame);
8554 printf(" has an anonymous flex item as its next sibling\n");
8555 }
8556 #endif // DEBUG
8557 // Recreate frames for the flex container (the removed frame's parent)
8558 *aResult = RecreateFramesForContent(parent->GetContent(), true);
8559 return true;
8560 }
8561
8562 // Might need to reconstruct things if the removed frame's nextSibling is
8563 // null and its parent is an anonymous flex item. (This might be the last
8564 // remaining child of that anonymous flex item, which can then go away.)
8565 if (!nextSibling && IsAnonymousFlexItem(parent)) {
8566 NS_ABORT_IF_FALSE(parent->GetParent() &&
8567 parent->GetParent()->GetType() == nsGkAtoms::flexContainerFrame,
8568 "anonymous flex items should only exist as children "
8569 "of flex container frames");
8570 #ifdef DEBUG
8571 if (gNoisyContentUpdates) {
8572 printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
8573 "frame=");
8574 nsFrame::ListTag(stdout, aFrame);
8575 printf(" has an anonymous flex item as its parent\n");
8576 }
8577 #endif // DEBUG
8578 // Recreate frames for the flex container (the removed frame's grandparent)
8579 *aResult = RecreateFramesForContent(parent->GetParent()->GetContent(),
8580 true);
8581 return true;
8582 }
8583
8584 #ifdef MOZ_XUL
8585 if (aFrame->GetType() == nsGkAtoms::popupSetFrame) {
8586 nsIRootBox* rootBox = nsIRootBox::GetRootBox(mPresShell);
8587 if (rootBox && rootBox->GetPopupSetFrame() == aFrame) {
8588 *aResult = ReconstructDocElementHierarchy();
8589 return true;
8590 }
8591 }
8592 #endif
8593
8594 // Reconstruct if inflowFrame is parent's only child, and parent is, or has,
8595 // a non-fluid continuation, i.e. it was split by bidi resolution
8596 if (!inFlowFrame->GetPrevSibling() &&
8597 !inFlowFrame->GetNextSibling() &&
8598 ((parent->GetPrevContinuation() && !parent->GetPrevInFlow()) ||
8599 (parent->GetNextContinuation() && !parent->GetNextInFlow()))) {
8600 *aResult = RecreateFramesForContent(parent->GetContent(), true);
8601 return true;
8602 }
8603
8604 // We might still need to reconstruct things if the parent of inFlowFrame is
8605 // ib-split, since in that case the removal of aFrame might affect the
8606 // splitting of its parent.
8607 if (!IsFramePartOfIBSplit(parent)) {
8608 return false;
8609 }
8610
8611 // If inFlowFrame is not the only in-flow child of |parent|, then removing
8612 // it will change nothing about the {ib} split.
8613 if (inFlowFrame != parent->GetFirstPrincipalChild() ||
8614 inFlowFrame->LastContinuation()->GetNextSibling()) {
8615 return false;
8616 }
8617
8618 // If the parent is the first or last part of the {ib} split, then
8619 // removing one of its kids will have no effect on the splitting.
8620 // Get the first continuation up front so we don't have to do it twice.
8621 nsIFrame* parentFirstContinuation = parent->FirstContinuation();
8622 if (!GetIBSplitSibling(parentFirstContinuation) ||
8623 !GetIBSplitPrevSibling(parentFirstContinuation)) {
8624 return false;
8625 }
8626
8627 #ifdef DEBUG
8628 if (gNoisyContentUpdates) {
8629 printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
8630 "frame=");
8631 nsFrame::ListTag(stdout, parent);
8632 printf(" is ib-split\n");
8633 }
8634 #endif
8635
8636 *aResult = ReframeContainingBlock(parent);
8637 return true;
8638 }
8639
8640 nsresult
8641 nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent,
8642 bool aAsyncInsert)
8643 {
8644 NS_PRECONDITION(!aAsyncInsert || aContent->IsElement(),
8645 "Can only insert elements async");
8646 // If there is no document, we don't want to recreate frames for it. (You
8647 // shouldn't generally be giving this method content without a document
8648 // anyway).
8649 // Rebuilding the frame tree can have bad effects, especially if it's the
8650 // frame tree for chrome (see bug 157322).
8651 NS_ENSURE_TRUE(aContent->GetDocument(), NS_ERROR_FAILURE);
8652
8653 // Is the frame ib-split? If so, we need to reframe the containing
8654 // block *here*, rather than trying to remove and re-insert the
8655 // content (which would otherwise result in *two* nested reframe
8656 // containing block from ContentRemoved() and ContentInserted(),
8657 // below!). We'd really like to optimize away one of those
8658 // containing block reframes, hence the code here.
8659
8660 nsIFrame* frame = aContent->GetPrimaryFrame();
8661 if (frame && frame->IsFrameOfType(nsIFrame::eMathML)) {
8662 // Reframe the topmost MathML element to prevent exponential blowup
8663 // (see bug 397518)
8664 while (true) {
8665 nsIContent* parentContent = aContent->GetParent();
8666 nsIFrame* parentContentFrame = parentContent->GetPrimaryFrame();
8667 if (!parentContentFrame || !parentContentFrame->IsFrameOfType(nsIFrame::eMathML))
8668 break;
8669 aContent = parentContent;
8670 frame = parentContentFrame;
8671 }
8672 }
8673
8674 if (frame) {
8675 nsIFrame* nonGeneratedAncestor = nsLayoutUtils::GetNonGeneratedAncestor(frame);
8676 if (nonGeneratedAncestor->GetContent() != aContent) {
8677 return RecreateFramesForContent(nonGeneratedAncestor->GetContent(), aAsyncInsert);
8678 }
8679
8680 if (frame->GetStateBits() & NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT) {
8681 // Recreate the frames for the entire nsIAnonymousContentCreator tree
8682 // since |frame| or one of its descendants may need an nsStyleContext
8683 // that associates it to a CSS pseudo-element, and only the
8684 // nsIAnonymousContentCreator that created this content knows how to make
8685 // that happen.
8686 nsIAnonymousContentCreator* acc = nullptr;
8687 nsIFrame* ancestor = frame->GetParent();
8688 while (!(acc = do_QueryFrame(ancestor))) {
8689 ancestor = ancestor->GetParent();
8690 }
8691 NS_ASSERTION(acc, "Where is the nsIAnonymousContentCreator? We may fail "
8692 "to recreate its content correctly");
8693 // nsSVGUseFrame is special, and we know this is unnecessary for it.
8694 if (ancestor->GetType() != nsGkAtoms::svgUseFrame) {
8695 NS_ASSERTION(aContent->IsInNativeAnonymousSubtree(),
8696 "Why is NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT set?");
8697 return RecreateFramesForContent(ancestor->GetContent(), aAsyncInsert);
8698 }
8699 }
8700
8701 nsIFrame* parent = frame->GetParent();
8702 nsIContent* parentContent = parent ? parent->GetContent() : nullptr;
8703 // If the parent frame is a leaf then the subsequent insert will fail to
8704 // create a frame, so we need to recreate the parent content. This happens
8705 // with native anonymous content from the editor.
8706 if (parent && parent->IsLeaf() && parentContent &&
8707 parentContent != aContent) {
8708 return RecreateFramesForContent(parentContent, aAsyncInsert);
8709 }
8710 }
8711
8712 nsresult rv = NS_OK;
8713
8714 if (frame && MaybeRecreateContainerForFrameRemoval(frame, &rv)) {
8715 return rv;
8716 }
8717
8718 nsINode* containerNode = aContent->GetParentNode();
8719 // XXXbz how can containerNode be null here?
8720 if (containerNode) {
8721 // Before removing the frames associated with the content object,
8722 // ask them to save their state onto a temporary state object.
8723 CaptureStateForFramesOf(aContent, mTempFrameTreeState);
8724
8725 // Need the nsIContent parent, which might be null here, since we need to
8726 // pass it to ContentInserted and ContentRemoved.
8727 nsCOMPtr<nsIContent> container = aContent->GetParent();
8728
8729 // Remove the frames associated with the content object.
8730 bool didReconstruct;
8731 rv = ContentRemoved(container, aContent,
8732 aContent->IsRootOfAnonymousSubtree() ?
8733 nullptr :
8734 aContent->GetNextSibling(),
8735 REMOVE_FOR_RECONSTRUCTION, &didReconstruct);
8736
8737 if (NS_SUCCEEDED(rv) && !didReconstruct) {
8738 // Now, recreate the frames associated with this content object. If
8739 // ContentRemoved triggered reconstruction, then we don't need to do this
8740 // because the frames will already have been built.
8741 if (aAsyncInsert) {
8742 RestyleManager()->PostRestyleEvent(
8743 aContent->AsElement(), nsRestyleHint(0), nsChangeHint_ReconstructFrame);
8744 } else {
8745 rv = ContentInserted(container, aContent, mTempFrameTreeState, false);
8746 }
8747 }
8748 }
8749
8750 return rv;
8751 }
8752
8753 //////////////////////////////////////////////////////////////////////
8754
8755 // Block frame construction code
8756
8757 already_AddRefed<nsStyleContext>
8758 nsCSSFrameConstructor::GetFirstLetterStyle(nsIContent* aContent,
8759 nsStyleContext* aStyleContext)
8760 {
8761 if (aContent) {
8762 return mPresShell->StyleSet()->
8763 ResolvePseudoElementStyle(aContent->AsElement(),
8764 nsCSSPseudoElements::ePseudo_firstLetter,
8765 aStyleContext,
8766 nullptr);
8767 }
8768 return nullptr;
8769 }
8770
8771 already_AddRefed<nsStyleContext>
8772 nsCSSFrameConstructor::GetFirstLineStyle(nsIContent* aContent,
8773 nsStyleContext* aStyleContext)
8774 {
8775 if (aContent) {
8776 return mPresShell->StyleSet()->
8777 ResolvePseudoElementStyle(aContent->AsElement(),
8778 nsCSSPseudoElements::ePseudo_firstLine,
8779 aStyleContext,
8780 nullptr);
8781 }
8782 return nullptr;
8783 }
8784
8785 // Predicate to see if a given content (block element) has
8786 // first-letter style applied to it.
8787 bool
8788 nsCSSFrameConstructor::ShouldHaveFirstLetterStyle(nsIContent* aContent,
8789 nsStyleContext* aStyleContext)
8790 {
8791 return nsLayoutUtils::HasPseudoStyle(aContent, aStyleContext,
8792 nsCSSPseudoElements::ePseudo_firstLetter,
8793 mPresShell->GetPresContext());
8794 }
8795
8796 bool
8797 nsCSSFrameConstructor::HasFirstLetterStyle(nsIFrame* aBlockFrame)
8798 {
8799 NS_PRECONDITION(aBlockFrame, "Need a frame");
8800 NS_ASSERTION(nsLayoutUtils::GetAsBlock(aBlockFrame),
8801 "Not a block frame?");
8802
8803 return (aBlockFrame->GetStateBits() & NS_BLOCK_HAS_FIRST_LETTER_STYLE) != 0;
8804 }
8805
8806 bool
8807 nsCSSFrameConstructor::ShouldHaveFirstLineStyle(nsIContent* aContent,
8808 nsStyleContext* aStyleContext)
8809 {
8810 bool hasFirstLine =
8811 nsLayoutUtils::HasPseudoStyle(aContent, aStyleContext,
8812 nsCSSPseudoElements::ePseudo_firstLine,
8813 mPresShell->GetPresContext());
8814 if (hasFirstLine) {
8815 // But disable for fieldsets
8816 int32_t namespaceID;
8817 nsIAtom* tag = mDocument->BindingManager()->ResolveTag(aContent,
8818 &namespaceID);
8819 // This check must match the one in FindHTMLData.
8820 hasFirstLine = tag != nsGkAtoms::fieldset ||
8821 namespaceID != kNameSpaceID_XHTML;
8822 }
8823
8824 return hasFirstLine;
8825 }
8826
8827 void
8828 nsCSSFrameConstructor::ShouldHaveSpecialBlockStyle(nsIContent* aContent,
8829 nsStyleContext* aStyleContext,
8830 bool* aHaveFirstLetterStyle,
8831 bool* aHaveFirstLineStyle)
8832 {
8833 *aHaveFirstLetterStyle =
8834 ShouldHaveFirstLetterStyle(aContent, aStyleContext);
8835 *aHaveFirstLineStyle =
8836 ShouldHaveFirstLineStyle(aContent, aStyleContext);
8837 }
8838
8839 /* static */
8840 const nsCSSFrameConstructor::PseudoParentData
8841 nsCSSFrameConstructor::sPseudoParentData[eParentTypeCount] = {
8842 { // Cell
8843 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
8844 FCDATA_USE_CHILD_ITEMS |
8845 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow),
8846 &nsCSSFrameConstructor::ConstructTableCell),
8847 &nsCSSAnonBoxes::tableCell
8848 },
8849 { // Row
8850 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
8851 FCDATA_USE_CHILD_ITEMS |
8852 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup),
8853 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup),
8854 &nsCSSAnonBoxes::tableRow
8855 },
8856 { // Row group
8857 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
8858 FCDATA_USE_CHILD_ITEMS |
8859 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
8860 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup),
8861 &nsCSSAnonBoxes::tableRowGroup
8862 },
8863 { // Column group
8864 FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
8865 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_USE_CHILD_ITEMS |
8866 FCDATA_SKIP_ABSPOS_PUSH |
8867 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
8868 NS_NewTableColGroupFrame),
8869 &nsCSSAnonBoxes::tableColGroup
8870 },
8871 { // Table
8872 FULL_CTOR_FCDATA(FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS,
8873 &nsCSSFrameConstructor::ConstructTable),
8874 &nsCSSAnonBoxes::table
8875 }
8876 };
8877
8878 void
8879 nsCSSFrameConstructor::CreateNeededAnonFlexItems(
8880 nsFrameConstructorState& aState,
8881 FrameConstructionItemList& aItems,
8882 nsIFrame* aParentFrame)
8883 {
8884 if (aItems.IsEmpty() ||
8885 aParentFrame->GetType() != nsGkAtoms::flexContainerFrame) {
8886 return;
8887 }
8888
8889 FCItemIterator iter(aItems);
8890 do {
8891 // Advance iter past children that don't want to be wrapped
8892 if (iter.SkipItemsThatDontNeedAnonFlexItem(aState)) {
8893 // Hit the end of the items without finding any remaining children that
8894 // need to be wrapped. We're finished!
8895 return;
8896 }
8897
8898 // If our next potentially-wrappable child is whitespace, then see if
8899 // there's anything wrappable immediately after it. If not, we just drop
8900 // the whitespace and move on. (We're not supposed to create any anonymous
8901 // flex items that _only_ contain whitespace).
8902 // (BUT if this is generated content, then we don't give whitespace nodes
8903 // any special treatment, because they're probably not really whitespace --
8904 // they're just temporarily empty, waiting for their generated text.)
8905 // XXXdholbert If this node's generated text will *actually end up being
8906 // entirely whitespace*, then we technically should still skip over it, per
8907 // the flexbox spec. I'm not bothering with that at this point, since it's
8908 // a pretty extreme edge case.
8909 if (!aParentFrame->IsGeneratedContentFrame() &&
8910 iter.item().IsWhitespace(aState)) {
8911 FCItemIterator afterWhitespaceIter(iter);
8912 bool hitEnd = afterWhitespaceIter.SkipWhitespace(aState);
8913 bool nextChildNeedsAnonFlexItem =
8914 !hitEnd && afterWhitespaceIter.item().NeedsAnonFlexItem(aState);
8915
8916 if (!nextChildNeedsAnonFlexItem) {
8917 // There's nothing after the whitespace that we need to wrap, so we
8918 // just drop this run of whitespace.
8919 iter.DeleteItemsTo(afterWhitespaceIter);
8920 if (hitEnd) {
8921 // Nothing left to do -- we're finished!
8922 return;
8923 }
8924 // else, we have a next child and it does not want to be wrapped. So,
8925 // we jump back to the beginning of the loop to skip over that child
8926 // (and anything else non-wrappable after it)
8927 NS_ABORT_IF_FALSE(!iter.IsDone() &&
8928 !iter.item().NeedsAnonFlexItem(aState),
8929 "hitEnd and/or nextChildNeedsAnonFlexItem lied");
8930 continue;
8931 }
8932 }
8933
8934 // Now |iter| points to the first child that needs to be wrapped in an
8935 // anonymous flex item. Now we see how many children after it also want
8936 // to be wrapped in an anonymous flex item.
8937 FCItemIterator endIter(iter); // iterator to find the end of the group
8938 endIter.SkipItemsThatNeedAnonFlexItem(aState);
8939
8940 NS_ASSERTION(iter != endIter,
8941 "Should've had at least one wrappable child to seek past");
8942
8943 // Now, we create the anonymous flex item to contain the children
8944 // between |iter| and |endIter|.
8945 nsIAtom* pseudoType = nsCSSAnonBoxes::anonymousFlexItem;
8946 nsStyleContext* parentStyle = aParentFrame->StyleContext();
8947 nsIContent* parentContent = aParentFrame->GetContent();
8948 already_AddRefed<nsStyleContext> wrapperStyle =
8949 mPresShell->StyleSet()->ResolveAnonymousBoxStyle(pseudoType, parentStyle);
8950
8951 static const FrameConstructionData sBlockFormattingContextFCData =
8952 FCDATA_DECL(FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS,
8953 NS_NewBlockFormattingContext);
8954
8955 FrameConstructionItem* newItem =
8956 new FrameConstructionItem(&sBlockFormattingContextFCData,
8957 // Use the content of our parent frame
8958 parentContent,
8959 // Lie about the tag; it doesn't matter anyway
8960 pseudoType,
8961 iter.item().mNameSpaceID,
8962 // no pending binding
8963 nullptr,
8964 wrapperStyle,
8965 true, nullptr);
8966
8967 newItem->mIsAllInline = newItem->mHasInlineEnds =
8968 newItem->mStyleContext->StyleDisplay()->IsInlineOutsideStyle();
8969 newItem->mIsBlock = !newItem->mIsAllInline;
8970
8971 NS_ABORT_IF_FALSE(!newItem->mIsAllInline && newItem->mIsBlock,
8972 "expecting anonymous flex items to be block-level "
8973 "(this will make a difference when we encounter "
8974 "'flex-align: baseline')");
8975
8976 // Anonymous flex items induce line boundaries around their
8977 // contents.
8978 newItem->mChildItems.SetLineBoundaryAtStart(true);
8979 newItem->mChildItems.SetLineBoundaryAtEnd(true);
8980 // The parent of the items in aItems is also the parent of the items
8981 // in mChildItems
8982 newItem->mChildItems.SetParentHasNoXBLChildren(
8983 aItems.ParentHasNoXBLChildren());
8984
8985 // Eat up all items between |iter| and |endIter| and put them in our
8986 // wrapper. This advances |iter| to point to |endIter|.
8987 iter.AppendItemsToList(endIter, newItem->mChildItems);
8988
8989 iter.InsertItem(newItem);
8990 } while (!iter.IsDone());
8991 }
8992
8993 /*
8994 * This function works as follows: we walk through the child list (aItems) and
8995 * find items that cannot have aParentFrame as their parent. We wrap
8996 * continuous runs of such items into a FrameConstructionItem for a frame that
8997 * gets them closer to their desired parents. For example, a run of non-row
8998 * children of a row-group will get wrapped in a row. When we later construct
8999 * the frame for this wrapper (in this case for the row), it'll be the correct
9000 * parent for the cells in the set of items we wrapped or we'll wrap cells
9001 * around everything else. At the end of this method, aItems is guaranteed to
9002 * contain only items for frames that can be direct kids of aParentFrame.
9003 */
9004 void
9005 nsCSSFrameConstructor::CreateNeededTablePseudos(nsFrameConstructorState& aState,
9006 FrameConstructionItemList& aItems,
9007 nsIFrame* aParentFrame)
9008 {
9009 ParentType ourParentType = GetParentType(aParentFrame);
9010 if (aItems.AllWantParentType(ourParentType)) {
9011 // Nothing to do here
9012 return;
9013 }
9014
9015 FCItemIterator iter(aItems);
9016 do {
9017 if (iter.SkipItemsWantingParentType(ourParentType)) {
9018 // Nothing else to do here; we're finished
9019 return;
9020 }
9021
9022 // Now we're pointing to the first child that wants a different parent
9023 // type.
9024
9025 // Now try to figure out what kids we can group together. We can generally
9026 // group everything that has a different desired parent type from us. Two
9027 // exceptions to this:
9028 // 1) If our parent type is table, we can't group columns with anything
9029 // else other than whitespace.
9030 // 2) Whitespace that lies between two things we can group which both want
9031 // a non-block parent should be dropped, even if we can't group them
9032 // with each other and even if the whitespace wants a parent of
9033 // ourParentType. Ends of the list count as things that don't want a
9034 // block parent (so that for example we'll drop a whitespace-only list).
9035
9036 FCItemIterator endIter(iter); /* iterator to find the end of the group */
9037 ParentType groupingParentType = endIter.item().DesiredParentType();
9038 if (aItems.AllWantParentType(groupingParentType) &&
9039 groupingParentType != eTypeBlock) {
9040 // Just group them all and be done with it. We need the check for
9041 // eTypeBlock here to catch the "all the items are whitespace" case
9042 // described above.
9043 endIter.SetToEnd();
9044 } else {
9045 // Locate the end of the group.
9046
9047 // Keep track of the type the previous item wanted, in case we have to
9048 // deal with whitespace. Start it off with ourParentType, since that's
9049 // the last thing |iter| would have skipped over.
9050 ParentType prevParentType = ourParentType;
9051 do {
9052 /* Walk an iterator past any whitespace that we might be able to drop from the list */
9053 FCItemIterator spaceEndIter(endIter);
9054 if (prevParentType != eTypeBlock &&
9055 !aParentFrame->IsGeneratedContentFrame() &&
9056 spaceEndIter.item().IsWhitespace(aState)) {
9057 bool trailingSpaces = spaceEndIter.SkipWhitespace(aState);
9058
9059 // We drop the whitespace if these are not trailing spaces and the next item
9060 // does not want a block parent (see case 2 above)
9061 // if these are trailing spaces and aParentFrame is a tabular container
9062 // according to rule 1.3 of CSS 2.1 Sec 17.2.1. (Being a tabular container
9063 // pretty much means ourParentType != eTypeBlock besides the eTypeColGroup case,
9064 // which won't reach here.)
9065 if ((trailingSpaces && ourParentType != eTypeBlock) ||
9066 (!trailingSpaces && spaceEndIter.item().DesiredParentType() !=
9067 eTypeBlock)) {
9068 bool updateStart = (iter == endIter);
9069 endIter.DeleteItemsTo(spaceEndIter);
9070 NS_ASSERTION(trailingSpaces == endIter.IsDone(), "These should match");
9071
9072 if (updateStart) {
9073 iter = endIter;
9074 }
9075
9076 if (trailingSpaces) {
9077 break; /* Found group end */
9078 }
9079
9080 if (updateStart) {
9081 // Update groupingParentType, since it might have been eTypeBlock
9082 // just because of the whitespace.
9083 groupingParentType = iter.item().DesiredParentType();
9084 }
9085 }
9086 }
9087
9088 // Now endIter points to a non-whitespace item or a non-droppable
9089 // whitespace item. In the latter case, if this is the end of the group
9090 // we'll traverse this whitespace again. But it'll all just be quick
9091 // DesiredParentType() checks which will match ourParentType (that's
9092 // what it means that this is the group end), so it's OK.
9093 prevParentType = endIter.item().DesiredParentType();
9094 if (prevParentType == ourParentType) {
9095 // End the group at endIter.
9096 break;
9097 }
9098
9099 if (ourParentType == eTypeTable &&
9100 (prevParentType == eTypeColGroup) !=
9101 (groupingParentType == eTypeColGroup)) {
9102 // Either we started with columns and now found something else, or vice
9103 // versa. In any case, end the grouping.
9104 break;
9105 }
9106
9107 // Include the whitespace we didn't drop (if any) in the group, since
9108 // this is not the end of the group. Note that this doesn't change
9109 // prevParentType, since if we didn't drop the whitespace then we ended
9110 // at something that wants a block parent.
9111 endIter = spaceEndIter;
9112
9113 endIter.Next();
9114 } while (!endIter.IsDone());
9115 }
9116
9117 if (iter == endIter) {
9118 // Nothing to wrap here; just skipped some whitespace
9119 continue;
9120 }
9121
9122 // Now group together all the items between iter and endIter. The right
9123 // parent type to use depends on ourParentType.
9124 ParentType wrapperType;
9125 switch (ourParentType) {
9126 case eTypeBlock:
9127 wrapperType = eTypeTable;
9128 break;
9129 case eTypeRow:
9130 // The parent type for a cell is eTypeBlock, since that's what a cell
9131 // looks like to its kids.
9132 wrapperType = eTypeBlock;
9133 break;
9134 case eTypeRowGroup:
9135 wrapperType = eTypeRow;
9136 break;
9137 case eTypeTable:
9138 // Either colgroup or rowgroup, depending on what we're grouping.
9139 wrapperType = groupingParentType == eTypeColGroup ?
9140 eTypeColGroup : eTypeRowGroup;
9141 break;
9142 default:
9143 MOZ_CRASH("Colgroups should be suppresing non-col child items");
9144 }
9145
9146 const PseudoParentData& pseudoData = sPseudoParentData[wrapperType];
9147 nsIAtom* pseudoType = *pseudoData.mPseudoType;
9148 nsStyleContext* parentStyle = aParentFrame->StyleContext();
9149 nsIContent* parentContent = aParentFrame->GetContent();
9150
9151 if (pseudoType == nsCSSAnonBoxes::table &&
9152 parentStyle->StyleDisplay()->mDisplay == NS_STYLE_DISPLAY_INLINE) {
9153 pseudoType = nsCSSAnonBoxes::inlineTable;
9154 }
9155
9156 already_AddRefed<nsStyleContext> wrapperStyle =
9157 mPresShell->StyleSet()->ResolveAnonymousBoxStyle(pseudoType, parentStyle);
9158 FrameConstructionItem* newItem =
9159 new FrameConstructionItem(&pseudoData.mFCData,
9160 // Use the content of our parent frame
9161 parentContent,
9162 // Lie about the tag; it doesn't matter anyway
9163 pseudoType,
9164 // The namespace does matter, however; it needs
9165 // to match that of our first child item to
9166 // match the old behavior
9167 iter.item().mNameSpaceID,
9168 // no pending binding
9169 nullptr,
9170 wrapperStyle,
9171 true, nullptr);
9172
9173 // Here we're cheating a tad... technically, table-internal items should be
9174 // inline if aParentFrame is inline, but they'll get wrapped in an
9175 // inline-table in the end, so it'll all work out. In any case, arguably
9176 // we don't need to maintain this state at this point... but it's better
9177 // to, I guess.
9178 newItem->mIsAllInline = newItem->mHasInlineEnds =
9179 newItem->mStyleContext->StyleDisplay()->IsInlineOutsideStyle();
9180
9181 // Table pseudo frames always induce line boundaries around their
9182 // contents.
9183 newItem->mChildItems.SetLineBoundaryAtStart(true);
9184 newItem->mChildItems.SetLineBoundaryAtEnd(true);
9185 // The parent of the items in aItems is also the parent of the items
9186 // in mChildItems
9187 newItem->mChildItems.SetParentHasNoXBLChildren(
9188 aItems.ParentHasNoXBLChildren());
9189
9190 // Eat up all items between |iter| and |endIter| and put them in our wrapper
9191 // Advances |iter| to point to |endIter|.
9192 iter.AppendItemsToList(endIter, newItem->mChildItems);
9193
9194 iter.InsertItem(newItem);
9195
9196 // Now |iter| points to the item that was the first one we didn't wrap;
9197 // loop and see whether we need to skip it or wrap it in something
9198 // different.
9199 } while (!iter.IsDone());
9200 }
9201
9202 inline void
9203 nsCSSFrameConstructor::ConstructFramesFromItemList(nsFrameConstructorState& aState,
9204 FrameConstructionItemList& aItems,
9205 nsIFrame* aParentFrame,
9206 nsFrameItems& aFrameItems)
9207 {
9208 CreateNeededTablePseudos(aState, aItems, aParentFrame);
9209 CreateNeededAnonFlexItems(aState, aItems, aParentFrame);
9210
9211 aItems.SetTriedConstructingFrames();
9212 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
9213 NS_ASSERTION(iter.item().DesiredParentType() == GetParentType(aParentFrame),
9214 "Needed pseudos didn't get created; expect bad things");
9215 ConstructFramesFromItem(aState, iter, aParentFrame, aFrameItems);
9216 }
9217
9218 NS_ASSERTION(!aState.mHavePendingPopupgroup,
9219 "Should have proccessed it by now");
9220 }
9221
9222 void
9223 nsCSSFrameConstructor::AddFCItemsForAnonymousContent(
9224 nsFrameConstructorState& aState,
9225 nsIFrame* aFrame,
9226 nsTArray<nsIAnonymousContentCreator::ContentInfo>& aAnonymousItems,
9227 FrameConstructionItemList& aItemsToConstruct,
9228 uint32_t aExtraFlags)
9229 {
9230 for (uint32_t i = 0; i < aAnonymousItems.Length(); ++i) {
9231 nsIContent* content = aAnonymousItems[i].mContent;
9232 #ifdef DEBUG
9233 nsIAnonymousContentCreator* creator = do_QueryFrame(aFrame);
9234 NS_ASSERTION(!creator || !creator->CreateFrameFor(content),
9235 "If you need to use CreateFrameFor, you need to call "
9236 "CreateAnonymousFrames manually and not follow the standard "
9237 "ProcessChildren() codepath for this frame");
9238 #endif
9239 // Assert some things about this content
9240 NS_ABORT_IF_FALSE(!(content->GetFlags() &
9241 (NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME)),
9242 "Should not be marked as needing frames");
9243 NS_ABORT_IF_FALSE(!content->IsElement() ||
9244 !(content->GetFlags() & ELEMENT_ALL_RESTYLE_FLAGS),
9245 "Should have no pending restyle flags");
9246 NS_ABORT_IF_FALSE(!content->GetPrimaryFrame(),
9247 "Should have no existing frame");
9248 NS_ABORT_IF_FALSE(!content->IsNodeOfType(nsINode::eCOMMENT) &&
9249 !content->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION),
9250 "Why is someone creating garbage anonymous content");
9251
9252 nsRefPtr<nsStyleContext> styleContext;
9253 TreeMatchContext::AutoFlexItemStyleFixupSkipper
9254 flexItemStyleFixupSkipper(aState.mTreeMatchContext);
9255 if (aAnonymousItems[i].mStyleContext) {
9256 styleContext = aAnonymousItems[i].mStyleContext.forget();
9257 } else {
9258 styleContext = ResolveStyleContext(aFrame, content, &aState);
9259 }
9260
9261 nsTArray<nsIAnonymousContentCreator::ContentInfo>* anonChildren = nullptr;
9262 if (!aAnonymousItems[i].mChildren.IsEmpty()) {
9263 anonChildren = &aAnonymousItems[i].mChildren;
9264 }
9265
9266 uint32_t flags = ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK |
9267 ITEM_IS_ANONYMOUSCONTENTCREATOR_CONTENT | aExtraFlags;
9268
9269 AddFrameConstructionItemsInternal(aState, content, aFrame,
9270 content->Tag(), content->GetNameSpaceID(),
9271 true, styleContext, flags,
9272 anonChildren, aItemsToConstruct);
9273 }
9274 }
9275
9276 void
9277 nsCSSFrameConstructor::ProcessChildren(nsFrameConstructorState& aState,
9278 nsIContent* aContent,
9279 nsStyleContext* aStyleContext,
9280 nsIFrame* aFrame,
9281 const bool aCanHaveGeneratedContent,
9282 nsFrameItems& aFrameItems,
9283 const bool aAllowBlockStyles,
9284 PendingBinding* aPendingBinding,
9285 nsIFrame* aPossiblyLeafFrame)
9286 {
9287 NS_PRECONDITION(aFrame, "Must have parent frame here");
9288 NS_PRECONDITION(aFrame->GetContentInsertionFrame() == aFrame,
9289 "Parent frame in ProcessChildren should be its own "
9290 "content insertion frame");
9291 const uint32_t kMaxDepth = 2 * MAX_REFLOW_DEPTH;
9292 static_assert(kMaxDepth <= UINT16_MAX, "mCurrentDepth type is too narrow");
9293 AutoRestore<uint16_t> savedDepth(mCurrentDepth);
9294 if (mCurrentDepth != UINT16_MAX) {
9295 ++mCurrentDepth;
9296 }
9297
9298 if (!aPossiblyLeafFrame) {
9299 aPossiblyLeafFrame = aFrame;
9300 }
9301
9302 // XXXbz ideally, this would do all the pushing of various
9303 // containing blocks as needed, so callers don't have to do it...
9304
9305 bool haveFirstLetterStyle = false, haveFirstLineStyle = false;
9306 if (aAllowBlockStyles) {
9307 ShouldHaveSpecialBlockStyle(aContent, aStyleContext, &haveFirstLetterStyle,
9308 &haveFirstLineStyle);
9309 }
9310
9311 // The logic here needs to match the logic in GetFloatContainingBlock()
9312 nsFrameConstructorSaveState floatSaveState;
9313 if (ShouldSuppressFloatingOfDescendants(aFrame)) {
9314 aState.PushFloatContainingBlock(nullptr, floatSaveState);
9315 } else if (aFrame->IsFloatContainingBlock()) {
9316 aState.PushFloatContainingBlock(aFrame, floatSaveState);
9317 }
9318
9319 nsFrameConstructorState::PendingBindingAutoPusher pusher(aState,
9320 aPendingBinding);
9321
9322 FrameConstructionItemList itemsToConstruct;
9323
9324 // If we have first-letter or first-line style then frames can get
9325 // moved around so don't set these flags.
9326 if (aAllowBlockStyles && !haveFirstLetterStyle && !haveFirstLineStyle) {
9327 itemsToConstruct.SetLineBoundaryAtStart(true);
9328 itemsToConstruct.SetLineBoundaryAtEnd(true);
9329 }
9330
9331 // Create any anonymous frames we need here. This must happen before the
9332 // non-anonymous children are processed to ensure that popups are never
9333 // constructed before the popupset.
9334 nsAutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> anonymousItems;
9335 GetAnonymousContent(aContent, aPossiblyLeafFrame, anonymousItems);
9336 #ifdef DEBUG
9337 for (uint32_t i = 0; i < anonymousItems.Length(); ++i) {
9338 NS_ABORT_IF_FALSE(anonymousItems[i].mContent->IsRootOfAnonymousSubtree(),
9339 "Content should know it's an anonymous subtree");
9340 }
9341 #endif
9342 AddFCItemsForAnonymousContent(aState, aFrame, anonymousItems,
9343 itemsToConstruct);
9344
9345 if (!aPossiblyLeafFrame->IsLeaf()) {
9346 // :before/:after content should have the same style context parent
9347 // as normal kids.
9348 // Note that we don't use this style context for looking up things like
9349 // special block styles because in some cases involving table pseudo-frames
9350 // it has nothing to do with the parent frame's desired behavior.
9351 nsStyleContext* styleContext;
9352
9353 if (aCanHaveGeneratedContent) {
9354 aFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT);
9355 styleContext =
9356 nsFrame::CorrectStyleParentFrame(aFrame, nullptr)->StyleContext();
9357 // Probe for generated content before
9358 CreateGeneratedContentItem(aState, aFrame, aContent, styleContext,
9359 nsCSSPseudoElements::ePseudo_before,
9360 itemsToConstruct);
9361 }
9362
9363 const bool addChildItems = MOZ_LIKELY(mCurrentDepth < kMaxDepth);
9364 if (!addChildItems) {
9365 NS_WARNING("ProcessChildren max depth exceeded");
9366 }
9367
9368 FlattenedChildIterator iter(aContent);
9369 for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
9370 // Get the parent of the content and check if it is a XBL children element
9371 // (if the content is a children element then parent != aContent because the
9372 // FlattenedChildIterator will transitively iterate through <xbl:children>
9373 // for default content). Push the children element as an ancestor here because
9374 // it does not have a frame and would not otherwise be pushed as an ancestor.
9375 nsIContent* parent = child->GetParent();
9376 MOZ_ASSERT(parent, "Parent must be non-null because we are iterating children.");
9377 TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext);
9378 if (parent != aContent && parent->IsElement()) {
9379 if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
9380 ancestorPusher.PushAncestorAndStyleScope(parent->AsElement());
9381 } else {
9382 ancestorPusher.PushStyleScope(parent->AsElement());
9383 }
9384 }
9385
9386 // Frame construction item construction should not post
9387 // restyles, so removing restyle flags here is safe.
9388 if (child->IsElement()) {
9389 child->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
9390 }
9391 if (addChildItems) {
9392 AddFrameConstructionItems(aState, child, iter.XBLInvolved(), aFrame,
9393 itemsToConstruct);
9394 } else {
9395 ClearLazyBits(child, child->GetNextSibling());
9396 }
9397 }
9398 itemsToConstruct.SetParentHasNoXBLChildren(!iter.XBLInvolved());
9399
9400 if (aCanHaveGeneratedContent) {
9401 // Probe for generated content after
9402 CreateGeneratedContentItem(aState, aFrame, aContent, styleContext,
9403 nsCSSPseudoElements::ePseudo_after,
9404 itemsToConstruct);
9405 }
9406 } else {
9407 ClearLazyBits(aContent->GetFirstChild(), nullptr);
9408 }
9409
9410 ConstructFramesFromItemList(aState, itemsToConstruct, aFrame, aFrameItems);
9411
9412 NS_ASSERTION(!aAllowBlockStyles || !aFrame->IsBoxFrame(),
9413 "can't be both block and box");
9414
9415 if (haveFirstLetterStyle) {
9416 WrapFramesInFirstLetterFrame(aContent, aFrame, aFrameItems);
9417 }
9418 if (haveFirstLineStyle) {
9419 WrapFramesInFirstLineFrame(aState, aContent, aFrame, nullptr,
9420 aFrameItems);
9421 }
9422
9423 // We might end up with first-line frames that change
9424 // AnyKidsNeedBlockParent() without changing itemsToConstruct, but that
9425 // should never happen for cases whan aFrame->IsBoxFrame().
9426 NS_ASSERTION(!haveFirstLineStyle || !aFrame->IsBoxFrame(),
9427 "Shouldn't have first-line style if we're a box");
9428 NS_ASSERTION(!aFrame->IsBoxFrame() ||
9429 itemsToConstruct.AnyItemsNeedBlockParent() ==
9430 (AnyKidsNeedBlockParent(aFrameItems.FirstChild()) != nullptr),
9431 "Something went awry in our block parent calculations");
9432
9433 if (aFrame->IsBoxFrame() && itemsToConstruct.AnyItemsNeedBlockParent()) {
9434 // XXXbz we could do this on the FrameConstructionItemList level,
9435 // no? And if we cared we could look through the item list
9436 // instead of groveling through the framelist here..
9437 nsStyleContext *frameStyleContext = aFrame->StyleContext();
9438 // Report a warning for non-GC frames, for chrome:
9439 if (!aFrame->IsGeneratedContentFrame() &&
9440 mPresShell->GetPresContext()->IsChrome()) {
9441 nsIContent *badKid = AnyKidsNeedBlockParent(aFrameItems.FirstChild());
9442 nsDependentAtomString parentTag(aContent->Tag()), kidTag(badKid->Tag());
9443 const char16_t* params[] = { parentTag.get(), kidTag.get() };
9444 const nsStyleDisplay *display = frameStyleContext->StyleDisplay();
9445 const char *message =
9446 (display->mDisplay == NS_STYLE_DISPLAY_INLINE_BOX)
9447 ? "NeededToWrapXULInlineBox" : "NeededToWrapXUL";
9448 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
9449 NS_LITERAL_CSTRING("FrameConstructor"),
9450 mDocument,
9451 nsContentUtils::eXUL_PROPERTIES,
9452 message,
9453 params, ArrayLength(params));
9454 }
9455
9456 nsRefPtr<nsStyleContext> blockSC = mPresShell->StyleSet()->
9457 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::mozXULAnonymousBlock,
9458 frameStyleContext);
9459 nsIFrame *blockFrame = NS_NewBlockFrame(mPresShell, blockSC);
9460 // We might, in theory, want to set NS_BLOCK_FLOAT_MGR and
9461 // NS_BLOCK_MARGIN_ROOT, but I think it's a bad idea given that
9462 // a real block placed here wouldn't get those set on it.
9463
9464 InitAndRestoreFrame(aState, aContent, aFrame, blockFrame, false);
9465
9466 NS_ASSERTION(!blockFrame->HasView(), "need to do view reparenting");
9467 ReparentFrames(this, blockFrame, aFrameItems);
9468
9469 blockFrame->SetInitialChildList(kPrincipalList, aFrameItems);
9470 NS_ASSERTION(aFrameItems.IsEmpty(), "How did that happen?");
9471 aFrameItems.Clear();
9472 aFrameItems.AddChild(blockFrame);
9473
9474 aFrame->AddStateBits(NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK);
9475 }
9476 }
9477
9478 //----------------------------------------------------------------------
9479
9480 // Support for :first-line style
9481
9482 // Special routine to handle placing a list of frames into a block
9483 // frame that has first-line style. The routine ensures that the first
9484 // collection of inline frames end up in a first-line frame.
9485 // NOTE: aState may have containing block information related to a
9486 // different part of the frame tree than where the first line occurs.
9487 // In particular aState may be set up for where ContentInserted or
9488 // ContentAppended is inserting content, which may be some
9489 // non-first-in-flow continuation of the block to which the first-line
9490 // belongs. So this function needs to be careful about how it uses
9491 // aState.
9492 void
9493 nsCSSFrameConstructor::WrapFramesInFirstLineFrame(
9494 nsFrameConstructorState& aState,
9495 nsIContent* aBlockContent,
9496 nsIFrame* aBlockFrame,
9497 nsIFrame* aLineFrame,
9498 nsFrameItems& aFrameItems)
9499 {
9500 // Find the part of aFrameItems that we want to put in the first-line
9501 nsFrameList::FrameLinkEnumerator link(aFrameItems);
9502 while (!link.AtEnd() && link.NextFrame()->IsInlineOutside()) {
9503 link.Next();
9504 }
9505
9506 nsFrameList firstLineChildren = aFrameItems.ExtractHead(link);
9507
9508 if (firstLineChildren.IsEmpty()) {
9509 // Nothing is supposed to go into the first-line; nothing to do
9510 return;
9511 }
9512
9513 if (!aLineFrame) {
9514 // Create line frame
9515 nsStyleContext* parentStyle =
9516 nsFrame::CorrectStyleParentFrame(aBlockFrame,
9517 nsCSSPseudoElements::firstLine)->
9518 StyleContext();
9519 nsRefPtr<nsStyleContext> firstLineStyle = GetFirstLineStyle(aBlockContent,
9520 parentStyle);
9521
9522 aLineFrame = NS_NewFirstLineFrame(mPresShell, firstLineStyle);
9523
9524 // Initialize the line frame
9525 InitAndRestoreFrame(aState, aBlockContent, aBlockFrame, aLineFrame);
9526
9527 // The lineFrame will be the block's first child; the rest of the
9528 // frame list (after lastInlineFrame) will be the second and
9529 // subsequent children; insert lineFrame into aFrameItems.
9530 aFrameItems.InsertFrame(nullptr, nullptr, aLineFrame);
9531
9532 NS_ASSERTION(aLineFrame->StyleContext() == firstLineStyle,
9533 "Bogus style context on line frame");
9534 }
9535
9536 // Give the inline frames to the lineFrame <b>after</b> reparenting them
9537 ReparentFrames(this, aLineFrame, firstLineChildren);
9538 if (aLineFrame->PrincipalChildList().IsEmpty() &&
9539 (aLineFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
9540 aLineFrame->SetInitialChildList(kPrincipalList, firstLineChildren);
9541 } else {
9542 AppendFrames(aLineFrame, kPrincipalList, firstLineChildren);
9543 }
9544 }
9545
9546 // Special routine to handle appending a new frame to a block frame's
9547 // child list. Takes care of placing the new frame into the right
9548 // place when first-line style is present.
9549 void
9550 nsCSSFrameConstructor::AppendFirstLineFrames(
9551 nsFrameConstructorState& aState,
9552 nsIContent* aBlockContent,
9553 nsIFrame* aBlockFrame,
9554 nsFrameItems& aFrameItems)
9555 {
9556 // It's possible that aBlockFrame needs to have a first-line frame
9557 // created because it doesn't currently have any children.
9558 const nsFrameList& blockKids = aBlockFrame->PrincipalChildList();
9559 if (blockKids.IsEmpty()) {
9560 WrapFramesInFirstLineFrame(aState, aBlockContent,
9561 aBlockFrame, nullptr, aFrameItems);
9562 return;
9563 }
9564
9565 // Examine the last block child - if it's a first-line frame then
9566 // appended frames need special treatment.
9567 nsIFrame* lastBlockKid = blockKids.LastChild();
9568 if (lastBlockKid->GetType() != nsGkAtoms::lineFrame) {
9569 // No first-line frame at the end of the list, therefore there is
9570 // an intervening block between any first-line frame the frames
9571 // we are appending. Therefore, we don't need any special
9572 // treatment of the appended frames.
9573 return;
9574 }
9575
9576 WrapFramesInFirstLineFrame(aState, aBlockContent, aBlockFrame,
9577 lastBlockKid, aFrameItems);
9578 }
9579
9580 // Special routine to handle inserting a new frame into a block
9581 // frame's child list. Takes care of placing the new frame into the
9582 // right place when first-line style is present.
9583 nsresult
9584 nsCSSFrameConstructor::InsertFirstLineFrames(
9585 nsFrameConstructorState& aState,
9586 nsIContent* aContent,
9587 nsIFrame* aBlockFrame,
9588 nsIFrame** aParentFrame,
9589 nsIFrame* aPrevSibling,
9590 nsFrameItems& aFrameItems)
9591 {
9592 nsresult rv = NS_OK;
9593 // XXXbz If you make this method actually do something, check to
9594 // make sure that the caller is passing what you expect. In
9595 // particular, which content is aContent? And audit the rest of
9596 // this code too; it makes bogus assumptions and may not build.
9597 #if 0
9598 nsIFrame* parentFrame = *aParentFrame;
9599 nsIFrame* newFrame = aFrameItems.childList;
9600 bool isInline = IsInlineOutside(newFrame);
9601
9602 if (!aPrevSibling) {
9603 // Insertion will become the first frame. Two cases: we either
9604 // already have a first-line frame or we don't.
9605 nsIFrame* firstBlockKid = aBlockFrame->GetFirstPrincipalChild();
9606 if (firstBlockKid->GetType() == nsGkAtoms::lineFrame) {
9607 // We already have a first-line frame
9608 nsIFrame* lineFrame = firstBlockKid;
9609
9610 if (isInline) {
9611 // Easy case: the new inline frame will go into the lineFrame.
9612 ReparentFrame(this, lineFrame, newFrame);
9613 InsertFrames(lineFrame, kPrincipalList, nullptr, newFrame);
9614
9615 // Since the frame is going into the lineFrame, don't let it
9616 // go into the block too.
9617 aFrameItems.childList = nullptr;
9618 aFrameItems.lastChild = nullptr;
9619 }
9620 else {
9621 // Harder case: We are about to insert a block level element
9622 // before the first-line frame.
9623 // XXX need a method to steal away frames from the line-frame
9624 }
9625 }
9626 else {
9627 // We do not have a first-line frame
9628 if (isInline) {
9629 // We now need a first-line frame to contain the inline frame.
9630 nsIFrame* lineFrame = NS_NewFirstLineFrame(firstLineStyle);
9631
9632 if (NS_SUCCEEDED(rv)) {
9633 // Lookup first-line style context
9634 nsStyleContext* parentStyle =
9635 nsFrame::CorrectStyleParentFrame(aBlockFrame,
9636 nsCSSPseudoElements::firstLine)->
9637 StyleContext();
9638 nsRefPtr<nsStyleContext> firstLineStyle =
9639 GetFirstLineStyle(aContent, parentStyle);
9640
9641 // Initialize the line frame
9642 InitAndRestoreFrame(aState, aContent, aBlockFrame, lineFrame);
9643
9644 // Make sure the caller inserts the lineFrame into the
9645 // blocks list of children.
9646 aFrameItems.childList = lineFrame;
9647 aFrameItems.lastChild = lineFrame;
9648
9649 // Give the inline frames to the lineFrame <b>after</b>
9650 // reparenting them
9651 NS_ASSERTION(lineFrame->StyleContext() == firstLineStyle,
9652 "Bogus style context on line frame");
9653 ReparentFrame(aPresContext, lineFrame, newFrame);
9654 lineFrame->SetInitialChildList(kPrincipalList, newFrame);
9655 }
9656 }
9657 else {
9658 // Easy case: the regular insertion logic can insert the new
9659 // frame because it's a block frame.
9660 }
9661 }
9662 }
9663 else {
9664 // Insertion will not be the first frame.
9665 nsIFrame* prevSiblingParent = aPrevSibling->GetParent();
9666 if (prevSiblingParent == aBlockFrame) {
9667 // Easy case: The prev-siblings parent is the block
9668 // frame. Therefore the prev-sibling is not currently in a
9669 // line-frame. Therefore the new frame which is going after it,
9670 // regardless of type, is not going into a line-frame.
9671 }
9672 else {
9673 // If the prevSiblingParent is not the block-frame then it must
9674 // be a line-frame (if it were a letter-frame, that logic would
9675 // already have adjusted the prev-sibling to be the
9676 // letter-frame).
9677 if (isInline) {
9678 // Easy case: the insertion can go where the caller thinks it
9679 // should go (which is into prevSiblingParent).
9680 }
9681 else {
9682 // Block elements don't end up in line-frames, therefore
9683 // change the insertion point to aBlockFrame. However, there
9684 // might be more inline elements following aPrevSibling that
9685 // need to be pulled out of the line-frame and become children
9686 // of the block.
9687 nsIFrame* nextSibling = aPrevSibling->GetNextSibling();
9688 nsIFrame* nextLineFrame = prevSiblingParent->GetNextInFlow();
9689 if (nextSibling || nextLineFrame) {
9690 // Oy. We have work to do. Create a list of the new frames
9691 // that are going into the block by stripping them away from
9692 // the line-frame(s).
9693 if (nextSibling) {
9694 nsLineFrame* lineFrame = (nsLineFrame*) prevSiblingParent;
9695 nsFrameList tail = lineFrame->StealFramesAfter(aPrevSibling);
9696 // XXX do something with 'tail'
9697 }
9698
9699 nsLineFrame* nextLineFrame = (nsLineFrame*) lineFrame;
9700 for (;;) {
9701 nextLineFrame = nextLineFrame->GetNextInFlow();
9702 if (!nextLineFrame) {
9703 break;
9704 }
9705 nsIFrame* kids = nextLineFrame->GetFirstPrincipalChild();
9706 }
9707 }
9708 else {
9709 // We got lucky: aPrevSibling was the last inline frame in
9710 // the line-frame.
9711 ReparentFrame(this, aBlockFrame, newFrame);
9712 InsertFrames(aBlockFrame, kPrincipalList,
9713 prevSiblingParent, newFrame);
9714 aFrameItems.childList = nullptr;
9715 aFrameItems.lastChild = nullptr;
9716 }
9717 }
9718 }
9719 }
9720
9721 #endif
9722 return rv;
9723 }
9724
9725 //----------------------------------------------------------------------
9726
9727 // First-letter support
9728
9729 // Determine how many characters in the text fragment apply to the
9730 // first letter
9731 static int32_t
9732 FirstLetterCount(const nsTextFragment* aFragment)
9733 {
9734 int32_t count = 0;
9735 int32_t firstLetterLength = 0;
9736
9737 int32_t i, n = aFragment->GetLength();
9738 for (i = 0; i < n; i++) {
9739 char16_t ch = aFragment->CharAt(i);
9740 // FIXME: take content language into account when deciding whitespace.
9741 if (dom::IsSpaceCharacter(ch)) {
9742 if (firstLetterLength) {
9743 break;
9744 }
9745 count++;
9746 continue;
9747 }
9748 // XXX I18n
9749 if ((ch == '\'') || (ch == '\"')) {
9750 if (firstLetterLength) {
9751 break;
9752 }
9753 // keep looping
9754 firstLetterLength = 1;
9755 }
9756 else {
9757 count++;
9758 break;
9759 }
9760 }
9761
9762 return count;
9763 }
9764
9765 static bool
9766 NeedFirstLetterContinuation(nsIContent* aContent)
9767 {
9768 NS_PRECONDITION(aContent, "null ptr");
9769
9770 bool result = false;
9771 if (aContent) {
9772 const nsTextFragment* frag = aContent->GetText();
9773 if (frag) {
9774 int32_t flc = FirstLetterCount(frag);
9775 int32_t tl = frag->GetLength();
9776 if (flc < tl) {
9777 result = true;
9778 }
9779 }
9780 }
9781 return result;
9782 }
9783
9784 static bool IsFirstLetterContent(nsIContent* aContent)
9785 {
9786 return aContent->TextLength() &&
9787 !aContent->TextIsOnlyWhitespace();
9788 }
9789
9790 /**
9791 * Create a letter frame, only make it a floating frame.
9792 */
9793 void
9794 nsCSSFrameConstructor::CreateFloatingLetterFrame(
9795 nsFrameConstructorState& aState,
9796 nsIFrame* aBlockFrame,
9797 nsIContent* aTextContent,
9798 nsIFrame* aTextFrame,
9799 nsIContent* aBlockContent,
9800 nsIFrame* aParentFrame,
9801 nsStyleContext* aStyleContext,
9802 nsFrameItems& aResult)
9803 {
9804 // Create the first-letter-frame
9805 nsIFrame* letterFrame;
9806 nsStyleSet *styleSet = mPresShell->StyleSet();
9807
9808 letterFrame = NS_NewFirstLetterFrame(mPresShell, aStyleContext);
9809 // We don't want to use a text content for a non-text frame (because we want
9810 // its primary frame to be a text frame). So use its parent for the
9811 // first-letter.
9812 nsIContent* letterContent = aTextContent->GetParent();
9813 nsIFrame* containingBlock = aState.GetGeometricParent(
9814 aStyleContext->StyleDisplay(), aParentFrame);
9815 InitAndRestoreFrame(aState, letterContent, containingBlock, letterFrame);
9816
9817 // Init the text frame to refer to the letter frame. Make sure we
9818 // get a proper style context for it (the one passed in is for the
9819 // letter frame and will have the float property set on it; the text
9820 // frame shouldn't have that set).
9821 nsRefPtr<nsStyleContext> textSC;
9822 textSC = styleSet->ResolveStyleForNonElement(aStyleContext);
9823 aTextFrame->SetStyleContextWithoutNotification(textSC);
9824 InitAndRestoreFrame(aState, aTextContent, letterFrame, aTextFrame);
9825
9826 // And then give the text frame to the letter frame
9827 SetInitialSingleChild(letterFrame, aTextFrame);
9828
9829 // See if we will need to continue the text frame (does it contain
9830 // more than just the first-letter text or not?) If it does, then we
9831 // create (in advance) a continuation frame for it.
9832 nsIFrame* nextTextFrame = nullptr;
9833 if (NeedFirstLetterContinuation(aTextContent)) {
9834 // Create continuation
9835 nextTextFrame =
9836 CreateContinuingFrame(aState.mPresContext, aTextFrame, aParentFrame);
9837 // Repair the continuations style context
9838 nsStyleContext* parentStyleContext = aStyleContext->GetParent();
9839 if (parentStyleContext) {
9840 nsRefPtr<nsStyleContext> newSC;
9841 newSC = styleSet->ResolveStyleForNonElement(parentStyleContext);
9842 nextTextFrame->SetStyleContext(newSC);
9843 }
9844 }
9845
9846 NS_ASSERTION(aResult.IsEmpty(), "aResult should be an empty nsFrameItems!");
9847 // Put the new float before any of the floats in the block we're doing
9848 // first-letter for, that is, before any floats whose parent is
9849 // containingBlock.
9850 nsFrameList::FrameLinkEnumerator link(aState.mFloatedItems);
9851 while (!link.AtEnd() && link.NextFrame()->GetParent() != containingBlock) {
9852 link.Next();
9853 }
9854
9855 aState.AddChild(letterFrame, aResult, letterContent, aStyleContext,
9856 aParentFrame, false, true, false, true,
9857 link.PrevFrame());
9858
9859 if (nextTextFrame) {
9860 aResult.AddChild(nextTextFrame);
9861 }
9862 }
9863
9864 /**
9865 * Create a new letter frame for aTextFrame. The letter frame will be
9866 * a child of aParentFrame.
9867 */
9868 void
9869 nsCSSFrameConstructor::CreateLetterFrame(nsIFrame* aBlockFrame,
9870 nsIFrame* aBlockContinuation,
9871 nsIContent* aTextContent,
9872 nsIFrame* aParentFrame,
9873 nsFrameItems& aResult)
9874 {
9875 NS_PRECONDITION(aTextContent->IsNodeOfType(nsINode::eTEXT),
9876 "aTextContent isn't text");
9877 NS_ASSERTION(nsLayoutUtils::GetAsBlock(aBlockFrame),
9878 "Not a block frame?");
9879
9880 // Get style context for the first-letter-frame
9881 nsStyleContext* parentStyleContext =
9882 nsFrame::CorrectStyleParentFrame(aParentFrame,
9883 nsCSSPseudoElements::firstLetter)->
9884 StyleContext();
9885
9886 // Use content from containing block so that we can actually
9887 // find a matching style rule.
9888 nsIContent* blockContent = aBlockFrame->GetContent();
9889
9890 // Create first-letter style rule
9891 nsRefPtr<nsStyleContext> sc = GetFirstLetterStyle(blockContent,
9892 parentStyleContext);
9893 if (sc) {
9894 nsRefPtr<nsStyleContext> textSC;
9895 textSC = mPresShell->StyleSet()->ResolveStyleForNonElement(sc);
9896
9897 // Create a new text frame (the original one will be discarded)
9898 // pass a temporary stylecontext, the correct one will be set
9899 // later. Start off by unsetting the primary frame for
9900 // aTextContent, so it's no longer pointing to the to-be-destroyed
9901 // frame.
9902 // XXXbz it would be really nice to destroy the old frame _first_,
9903 // then create the new one, so we could avoid this hack.
9904 aTextContent->SetPrimaryFrame(nullptr);
9905 nsIFrame* textFrame = NS_NewTextFrame(mPresShell, textSC);
9906
9907 NS_ASSERTION(aBlockContinuation == GetFloatContainingBlock(aParentFrame),
9908 "Containing block is confused");
9909 nsFrameConstructorState state(mPresShell,
9910 GetAbsoluteContainingBlock(aParentFrame, FIXED_POS),
9911 GetAbsoluteContainingBlock(aParentFrame, ABS_POS),
9912 aBlockContinuation);
9913
9914 // Create the right type of first-letter frame
9915 const nsStyleDisplay* display = sc->StyleDisplay();
9916 if (display->IsFloatingStyle() && !aParentFrame->IsSVGText()) {
9917 // Make a floating first-letter frame
9918 CreateFloatingLetterFrame(state, aBlockFrame, aTextContent, textFrame,
9919 blockContent, aParentFrame, sc, aResult);
9920 }
9921 else {
9922 // Make an inflow first-letter frame
9923 nsIFrame* letterFrame = NS_NewFirstLetterFrame(mPresShell, sc);
9924
9925 // Initialize the first-letter-frame. We don't want to use a text
9926 // content for a non-text frame (because we want its primary frame to
9927 // be a text frame). So use its parent for the first-letter.
9928 nsIContent* letterContent = aTextContent->GetParent();
9929 letterFrame->Init(letterContent, aParentFrame, nullptr);
9930
9931 InitAndRestoreFrame(state, aTextContent, letterFrame, textFrame);
9932
9933 SetInitialSingleChild(letterFrame, textFrame);
9934 aResult.Clear();
9935 aResult.AddChild(letterFrame);
9936 NS_ASSERTION(!aBlockFrame->GetPrevContinuation(),
9937 "should have the first continuation here");
9938 aBlockFrame->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD);
9939 }
9940 aTextContent->SetPrimaryFrame(textFrame);
9941 }
9942 }
9943
9944 void
9945 nsCSSFrameConstructor::WrapFramesInFirstLetterFrame(
9946 nsIContent* aBlockContent,
9947 nsIFrame* aBlockFrame,
9948 nsFrameItems& aBlockFrames)
9949 {
9950 aBlockFrame->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE);
9951
9952 nsIFrame* parentFrame = nullptr;
9953 nsIFrame* textFrame = nullptr;
9954 nsIFrame* prevFrame = nullptr;
9955 nsFrameItems letterFrames;
9956 bool stopLooking = false;
9957 WrapFramesInFirstLetterFrame(aBlockFrame, aBlockFrame, aBlockFrame,
9958 aBlockFrames.FirstChild(),
9959 &parentFrame, &textFrame, &prevFrame,
9960 letterFrames, &stopLooking);
9961 if (parentFrame) {
9962 if (parentFrame == aBlockFrame) {
9963 // Take textFrame out of the block's frame list and substitute the
9964 // letter frame(s) instead.
9965 aBlockFrames.DestroyFrame(textFrame);
9966 aBlockFrames.InsertFrames(nullptr, prevFrame, letterFrames);
9967 }
9968 else {
9969 // Take the old textFrame out of the inline parent's child list
9970 RemoveFrame(kPrincipalList, textFrame);
9971
9972 // Insert in the letter frame(s)
9973 parentFrame->InsertFrames(kPrincipalList, prevFrame, letterFrames);
9974 }
9975 }
9976 }
9977
9978 void
9979 nsCSSFrameConstructor::WrapFramesInFirstLetterFrame(
9980 nsIFrame* aBlockFrame,
9981 nsIFrame* aBlockContinuation,
9982 nsIFrame* aParentFrame,
9983 nsIFrame* aParentFrameList,
9984 nsIFrame** aModifiedParent,
9985 nsIFrame** aTextFrame,
9986 nsIFrame** aPrevFrame,
9987 nsFrameItems& aLetterFrames,
9988 bool* aStopLooking)
9989 {
9990 nsIFrame* prevFrame = nullptr;
9991 nsIFrame* frame = aParentFrameList;
9992
9993 while (frame) {
9994 nsIFrame* nextFrame = frame->GetNextSibling();
9995
9996 nsIAtom* frameType = frame->GetType();
9997 if (nsGkAtoms::textFrame == frameType) {
9998 // Wrap up first-letter content in a letter frame
9999 nsIContent* textContent = frame->GetContent();
10000 if (IsFirstLetterContent(textContent)) {
10001 // Create letter frame to wrap up the text
10002 CreateLetterFrame(aBlockFrame, aBlockContinuation, textContent,
10003 aParentFrame, aLetterFrames);
10004
10005 // Provide adjustment information for parent
10006 *aModifiedParent = aParentFrame;
10007 *aTextFrame = frame;
10008 *aPrevFrame = prevFrame;
10009 *aStopLooking = true;
10010 return;
10011 }
10012 }
10013 else if (IsInlineFrame(frame) && frameType != nsGkAtoms::brFrame) {
10014 nsIFrame* kids = frame->GetFirstPrincipalChild();
10015 WrapFramesInFirstLetterFrame(aBlockFrame, aBlockContinuation, frame,
10016 kids, aModifiedParent, aTextFrame,
10017 aPrevFrame, aLetterFrames, aStopLooking);
10018 if (*aStopLooking) {
10019 return;
10020 }
10021 }
10022 else {
10023 // This will stop us looking to create more letter frames. For
10024 // example, maybe the frame-type is "letterFrame" or
10025 // "placeholderFrame". This keeps us from creating extra letter
10026 // frames, and also prevents us from creating letter frames when
10027 // the first real content child of a block is not text (e.g. an
10028 // image, hr, etc.)
10029 *aStopLooking = true;
10030 break;
10031 }
10032
10033 prevFrame = frame;
10034 frame = nextFrame;
10035 }
10036 }
10037
10038 static nsIFrame*
10039 FindFirstLetterFrame(nsIFrame* aFrame, nsIFrame::ChildListID aListID)
10040 {
10041 nsFrameList list = aFrame->GetChildList(aListID);
10042 for (nsFrameList::Enumerator e(list); !e.AtEnd(); e.Next()) {
10043 if (nsGkAtoms::letterFrame == e.get()->GetType()) {
10044 return e.get();
10045 }
10046 }
10047 return nullptr;
10048 }
10049
10050 nsresult
10051 nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames(
10052 nsPresContext* aPresContext,
10053 nsIPresShell* aPresShell,
10054 nsIFrame* aBlockFrame,
10055 bool* aStopLooking)
10056 {
10057 // Look for the first letter frame on the kFloatList, then kPushedFloatsList.
10058 nsIFrame* floatFrame =
10059 ::FindFirstLetterFrame(aBlockFrame, nsIFrame::kFloatList);
10060 if (!floatFrame) {
10061 floatFrame =
10062 ::FindFirstLetterFrame(aBlockFrame, nsIFrame::kPushedFloatsList);
10063 if (!floatFrame) {
10064 return NS_OK;
10065 }
10066 }
10067
10068 // Take the text frame away from the letter frame (so it isn't
10069 // destroyed when we destroy the letter frame).
10070 nsIFrame* textFrame = floatFrame->GetFirstPrincipalChild();
10071 if (!textFrame) {
10072 return NS_OK;
10073 }
10074
10075 // Discover the placeholder frame for the letter frame
10076 nsIFrame* parentFrame;
10077 nsPlaceholderFrame* placeholderFrame = GetPlaceholderFrameFor(floatFrame);
10078
10079 if (!placeholderFrame) {
10080 // Somethings really wrong
10081 return NS_OK;
10082 }
10083 parentFrame = placeholderFrame->GetParent();
10084 if (!parentFrame) {
10085 // Somethings really wrong
10086 return NS_OK;
10087 }
10088
10089 // Create a new text frame with the right style context that maps
10090 // all of the content that was previously part of the letter frame
10091 // (and probably continued elsewhere).
10092 nsStyleContext* parentSC = parentFrame->StyleContext();
10093 nsIContent* textContent = textFrame->GetContent();
10094 if (!textContent) {
10095 return NS_OK;
10096 }
10097 nsRefPtr<nsStyleContext> newSC;
10098 newSC = aPresShell->StyleSet()->ResolveStyleForNonElement(parentSC);
10099 nsIFrame* newTextFrame = NS_NewTextFrame(aPresShell, newSC);
10100 newTextFrame->Init(textContent, parentFrame, nullptr);
10101
10102 // Destroy the old text frame's continuations (the old text frame
10103 // will be destroyed when its letter frame is destroyed).
10104 nsIFrame* frameToDelete = textFrame->LastContinuation();
10105 while (frameToDelete != textFrame) {
10106 nsIFrame* nextFrameToDelete = frameToDelete->GetPrevContinuation();
10107 RemoveFrame(kPrincipalList, frameToDelete);
10108 frameToDelete = nextFrameToDelete;
10109 }
10110
10111 nsIFrame* prevSibling = placeholderFrame->GetPrevSibling();
10112
10113 // Now that everything is set...
10114 #ifdef NOISY_FIRST_LETTER
10115 printf("RemoveFloatingFirstLetterFrames: textContent=%p oldTextFrame=%p newTextFrame=%p\n",
10116 textContent.get(), textFrame, newTextFrame);
10117 #endif
10118
10119 // Remove placeholder frame and the float
10120 RemoveFrame(kPrincipalList, placeholderFrame);
10121
10122 // Now that the old frames are gone, we can start pointing to our
10123 // new primary frame.
10124 textContent->SetPrimaryFrame(newTextFrame);
10125
10126 // Wallpaper bug 822910.
10127 bool offsetsNeedFixing =
10128 prevSibling && prevSibling->GetType() == nsGkAtoms::textFrame;
10129 if (offsetsNeedFixing) {
10130 prevSibling->AddStateBits(TEXT_OFFSETS_NEED_FIXING);
10131 }
10132
10133 // Insert text frame in its place
10134 nsFrameList textList(newTextFrame, newTextFrame);
10135 InsertFrames(parentFrame, kPrincipalList, prevSibling, textList);
10136
10137 if (offsetsNeedFixing) {
10138 prevSibling->RemoveStateBits(TEXT_OFFSETS_NEED_FIXING);
10139 }
10140
10141 return NS_OK;
10142 }
10143
10144 nsresult
10145 nsCSSFrameConstructor::RemoveFirstLetterFrames(nsPresContext* aPresContext,
10146 nsIPresShell* aPresShell,
10147 nsIFrame* aFrame,
10148 nsIFrame* aBlockFrame,
10149 bool* aStopLooking)
10150 {
10151 nsIFrame* prevSibling = nullptr;
10152 nsIFrame* kid = aFrame->GetFirstPrincipalChild();
10153
10154 while (kid) {
10155 if (nsGkAtoms::letterFrame == kid->GetType()) {
10156 // Bingo. Found it. First steal away the text frame.
10157 nsIFrame* textFrame = kid->GetFirstPrincipalChild();
10158 if (!textFrame) {
10159 break;
10160 }
10161
10162 // Create a new textframe
10163 nsStyleContext* parentSC = aFrame->StyleContext();
10164 if (!parentSC) {
10165 break;
10166 }
10167 nsIContent* textContent = textFrame->GetContent();
10168 if (!textContent) {
10169 break;
10170 }
10171 nsRefPtr<nsStyleContext> newSC;
10172 newSC = aPresShell->StyleSet()->ResolveStyleForNonElement(parentSC);
10173 textFrame = NS_NewTextFrame(aPresShell, newSC);
10174 textFrame->Init(textContent, aFrame, nullptr);
10175
10176 // Next rip out the kid and replace it with the text frame
10177 RemoveFrame(kPrincipalList, kid);
10178
10179 // Now that the old frames are gone, we can start pointing to our
10180 // new primary frame.
10181 textContent->SetPrimaryFrame(textFrame);
10182
10183 // Wallpaper bug 822910.
10184 bool offsetsNeedFixing =
10185 prevSibling && prevSibling->GetType() == nsGkAtoms::textFrame;
10186 if (offsetsNeedFixing) {
10187 prevSibling->AddStateBits(TEXT_OFFSETS_NEED_FIXING);
10188 }
10189
10190 // Insert text frame in its place
10191 nsFrameList textList(textFrame, textFrame);
10192 InsertFrames(aFrame, kPrincipalList, prevSibling, textList);
10193
10194 if (offsetsNeedFixing) {
10195 prevSibling->RemoveStateBits(TEXT_OFFSETS_NEED_FIXING);
10196 }
10197
10198 *aStopLooking = true;
10199 NS_ASSERTION(!aBlockFrame->GetPrevContinuation(),
10200 "should have the first continuation here");
10201 aBlockFrame->RemoveStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD);
10202 break;
10203 }
10204 else if (IsInlineFrame(kid)) {
10205 // Look inside child inline frame for the letter frame
10206 RemoveFirstLetterFrames(aPresContext, aPresShell,
10207 kid, aBlockFrame, aStopLooking);
10208 if (*aStopLooking) {
10209 break;
10210 }
10211 }
10212 prevSibling = kid;
10213 kid = kid->GetNextSibling();
10214 }
10215
10216 return NS_OK;
10217 }
10218
10219 nsresult
10220 nsCSSFrameConstructor::RemoveLetterFrames(nsPresContext* aPresContext,
10221 nsIPresShell* aPresShell,
10222 nsIFrame* aBlockFrame)
10223 {
10224 aBlockFrame = aBlockFrame->FirstContinuation();
10225 nsIFrame* continuation = aBlockFrame;
10226
10227 bool stopLooking = false;
10228 nsresult rv;
10229 do {
10230 rv = RemoveFloatingFirstLetterFrames(aPresContext, aPresShell,
10231 continuation, &stopLooking);
10232 if (NS_SUCCEEDED(rv) && !stopLooking) {
10233 rv = RemoveFirstLetterFrames(aPresContext, aPresShell,
10234 continuation, aBlockFrame, &stopLooking);
10235 }
10236 if (stopLooking) {
10237 break;
10238 }
10239 continuation = continuation->GetNextContinuation();
10240 } while (continuation);
10241 return rv;
10242 }
10243
10244 // Fixup the letter frame situation for the given block
10245 void
10246 nsCSSFrameConstructor::RecoverLetterFrames(nsIFrame* aBlockFrame)
10247 {
10248 aBlockFrame = aBlockFrame->FirstContinuation();
10249 nsIFrame* continuation = aBlockFrame;
10250
10251 nsIFrame* parentFrame = nullptr;
10252 nsIFrame* textFrame = nullptr;
10253 nsIFrame* prevFrame = nullptr;
10254 nsFrameItems letterFrames;
10255 bool stopLooking = false;
10256 do {
10257 // XXX shouldn't this bit be set already (bug 408493), assert instead?
10258 continuation->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE);
10259 WrapFramesInFirstLetterFrame(aBlockFrame, continuation, continuation,
10260 continuation->GetFirstPrincipalChild(),
10261 &parentFrame, &textFrame, &prevFrame,
10262 letterFrames, &stopLooking);
10263 if (stopLooking) {
10264 break;
10265 }
10266 continuation = continuation->GetNextContinuation();
10267 } while (continuation);
10268
10269 if (parentFrame) {
10270 // Take the old textFrame out of the parents child list
10271 RemoveFrame(kPrincipalList, textFrame);
10272
10273 // Insert in the letter frame(s)
10274 parentFrame->InsertFrames(kPrincipalList, prevFrame, letterFrames);
10275 }
10276 }
10277
10278 //----------------------------------------------------------------------
10279
10280 // listbox Widget Routines
10281
10282 nsresult
10283 nsCSSFrameConstructor::CreateListBoxContent(nsPresContext* aPresContext,
10284 nsIFrame* aParentFrame,
10285 nsIFrame* aPrevFrame,
10286 nsIContent* aChild,
10287 nsIFrame** aNewFrame,
10288 bool aIsAppend,
10289 bool aIsScrollbar,
10290 nsILayoutHistoryState* aFrameState)
10291 {
10292 #ifdef MOZ_XUL
10293 nsresult rv = NS_OK;
10294
10295 // Construct a new frame
10296 if (nullptr != aParentFrame) {
10297 nsFrameItems frameItems;
10298 nsFrameConstructorState state(mPresShell, GetAbsoluteContainingBlock(aParentFrame, FIXED_POS),
10299 GetAbsoluteContainingBlock(aParentFrame, ABS_POS),
10300 GetFloatContainingBlock(aParentFrame),
10301 mTempFrameTreeState);
10302
10303 // If we ever initialize the ancestor filter on |state|, make sure
10304 // to push the right parent!
10305
10306 nsRefPtr<nsStyleContext> styleContext;
10307 styleContext = ResolveStyleContext(aParentFrame, aChild, &state);
10308
10309 // Pre-check for display "none" - only if we find that, do we create
10310 // any frame at all
10311 const nsStyleDisplay* display = styleContext->StyleDisplay();
10312
10313 if (NS_STYLE_DISPLAY_NONE == display->mDisplay) {
10314 *aNewFrame = nullptr;
10315 return NS_OK;
10316 }
10317
10318 BeginUpdate();
10319
10320 FrameConstructionItemList items;
10321 AddFrameConstructionItemsInternal(state, aChild, aParentFrame,
10322 aChild->Tag(), aChild->GetNameSpaceID(),
10323 true, styleContext,
10324 ITEM_ALLOW_XBL_BASE, nullptr, items);
10325 ConstructFramesFromItemList(state, items, aParentFrame, frameItems);
10326
10327 nsIFrame* newFrame = frameItems.FirstChild();
10328 *aNewFrame = newFrame;
10329
10330 if (newFrame) {
10331 // Notify the parent frame
10332 if (aIsAppend)
10333 rv = ((nsListBoxBodyFrame*)aParentFrame)->ListBoxAppendFrames(frameItems);
10334 else
10335 rv = ((nsListBoxBodyFrame*)aParentFrame)->ListBoxInsertFrames(aPrevFrame, frameItems);
10336 }
10337
10338 EndUpdate();
10339
10340 #ifdef ACCESSIBILITY
10341 if (newFrame) {
10342 nsAccessibilityService* accService = nsIPresShell::AccService();
10343 if (accService) {
10344 accService->ContentRangeInserted(mPresShell, aChild->GetParent(),
10345 aChild, aChild->GetNextSibling());
10346 }
10347 }
10348 #endif
10349 }
10350
10351 return rv;
10352 #else
10353 return NS_ERROR_FAILURE;
10354 #endif
10355 }
10356
10357 //----------------------------------------
10358
10359 void
10360 nsCSSFrameConstructor::ConstructBlock(nsFrameConstructorState& aState,
10361 const nsStyleDisplay* aDisplay,
10362 nsIContent* aContent,
10363 nsIFrame* aParentFrame,
10364 nsIFrame* aContentParentFrame,
10365 nsStyleContext* aStyleContext,
10366 nsIFrame** aNewFrame,
10367 nsFrameItems& aFrameItems,
10368 nsIFrame* aPositionedFrameForAbsPosContainer,
10369 PendingBinding* aPendingBinding)
10370 {
10371 // Create column wrapper if necessary
10372 nsIFrame* blockFrame = *aNewFrame;
10373 NS_ASSERTION(blockFrame->GetType() == nsGkAtoms::blockFrame, "not a block frame?");
10374 nsIFrame* parent = aParentFrame;
10375 nsRefPtr<nsStyleContext> blockStyle = aStyleContext;
10376 const nsStyleColumn* columns = aStyleContext->StyleColumn();
10377
10378 if (columns->mColumnCount != NS_STYLE_COLUMN_COUNT_AUTO
10379 || columns->mColumnWidth.GetUnit() != eStyleUnit_Auto) {
10380 nsIFrame* columnSetFrame = nullptr;
10381 columnSetFrame =
10382 NS_NewColumnSetFrame(mPresShell, aStyleContext, nsFrameState(0));
10383
10384 InitAndRestoreFrame(aState, aContent, aParentFrame, columnSetFrame);
10385 blockStyle = mPresShell->StyleSet()->
10386 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::columnContent, aStyleContext);
10387 parent = columnSetFrame;
10388 *aNewFrame = columnSetFrame;
10389
10390 SetInitialSingleChild(columnSetFrame, blockFrame);
10391 }
10392
10393 blockFrame->SetStyleContextWithoutNotification(blockStyle);
10394 InitAndRestoreFrame(aState, aContent, parent, blockFrame);
10395
10396 aState.AddChild(*aNewFrame, aFrameItems, aContent, aStyleContext,
10397 aContentParentFrame ? aContentParentFrame :
10398 aParentFrame);
10399 if (!mRootElementFrame) {
10400 // The frame we're constructing will be the root element frame.
10401 // Set mRootElementFrame before processing children.
10402 mRootElementFrame = *aNewFrame;
10403 }
10404
10405 // We should make the outer frame be the absolute containing block,
10406 // if one is required. We have to do this because absolute
10407 // positioning must be computed with respect to the CSS dimensions
10408 // of the element, which are the dimensions of the outer block. But
10409 // we can't really do that because only blocks can have absolute
10410 // children. So use the block and try to compensate with hacks
10411 // in nsBlockFrame::CalculateContainingBlockSizeForAbsolutes.
10412 nsFrameConstructorSaveState absoluteSaveState;
10413 (*aNewFrame)->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
10414 if (aPositionedFrameForAbsPosContainer) {
10415 // NS_ASSERTION(aRelPos, "should have made area frame for this");
10416 aState.PushAbsoluteContainingBlock(*aNewFrame, aPositionedFrameForAbsPosContainer, absoluteSaveState);
10417 }
10418
10419 // Process the child content
10420 nsFrameItems childItems;
10421 ProcessChildren(aState, aContent, aStyleContext, blockFrame, true,
10422 childItems, true, aPendingBinding);
10423
10424 // Set the frame's initial child list
10425 blockFrame->SetInitialChildList(kPrincipalList, childItems);
10426 }
10427
10428 nsIFrame*
10429 nsCSSFrameConstructor::ConstructInline(nsFrameConstructorState& aState,
10430 FrameConstructionItem& aItem,
10431 nsIFrame* aParentFrame,
10432 const nsStyleDisplay* aDisplay,
10433 nsFrameItems& aFrameItems)
10434 {
10435 // If an inline frame has non-inline kids, then we chop up the child list
10436 // into runs of blocks and runs of inlines, create anonymous block frames to
10437 // contain the runs of blocks, inline frames with our style context for the
10438 // runs of inlines, and put all these frames, in order, into aFrameItems. We
10439 // return the the first one. The whole setup is called an {ib}
10440 // split; in what follows "frames in the split" refers to the anonymous blocks
10441 // and inlines that contain our children.
10442 //
10443 // {ib} splits maintain the following invariants:
10444 // 1) All frames in the split have the NS_FRAME_PART_OF_IBSPLIT bit
10445 // set.
10446 // 2) Each frame in the split has the nsIFrame::IBSplitSibling
10447 // property pointing to the next frame in the split, except for the last
10448 // one, which does not have it set.
10449 // 3) Each frame in the split has the nsIFrame::IBSplitPrevSibling
10450 // property pointing to the previous frame in the split, except for the
10451 // first one, which does not have it set.
10452 // 4) The first and last frame in the split are always inlines.
10453 //
10454 // An invariant that is NOT maintained is that the wrappers are actually
10455 // linked via GetNextSibling linkage. A simple example is an inline
10456 // containing an inline that contains a block. The three parts of the inner
10457 // inline end up with three different parents.
10458 //
10459 // For example, this HTML:
10460 // <span>
10461 // <div>a</div>
10462 // <span>
10463 // b
10464 // <div>c</div>
10465 // </span>
10466 // d
10467 // <div>e</div>
10468 // f
10469 // </span>
10470 // Gives the following frame tree:
10471 //
10472 // Inline (outer span)
10473 // Block (anonymous, outer span)
10474 // Block (div)
10475 // Text("a")
10476 // Inline (outer span)
10477 // Inline (inner span)
10478 // Text("b")
10479 // Block (anonymous, outer span)
10480 // Block (anonymous, inner span)
10481 // Block (div)
10482 // Text("c")
10483 // Inline (outer span)
10484 // Inline (inner span)
10485 // Text("d")
10486 // Block (anonymous, outer span)
10487 // Block (div)
10488 // Text("e")
10489 // Inline (outer span)
10490 // Text("f")
10491
10492 nsIContent* const content = aItem.mContent;
10493 nsStyleContext* const styleContext = aItem.mStyleContext;
10494
10495 bool positioned =
10496 NS_STYLE_DISPLAY_INLINE == aDisplay->mDisplay &&
10497 aDisplay->IsRelativelyPositionedStyle() &&
10498 !aParentFrame->IsSVGText();
10499
10500 nsIFrame* newFrame = NS_NewInlineFrame(mPresShell, styleContext);
10501
10502 // Initialize the frame
10503 InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
10504
10505 // Inline frames can always have generated content
10506 newFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT);
10507
10508 nsFrameConstructorSaveState absoluteSaveState; // definition cannot be inside next block
10509 // because the object's destructor is significant
10510 // this is part of the fix for bug 42372
10511
10512 newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
10513 if (positioned) {
10514 // Relatively positioned frames becomes a container for child
10515 // frames that are positioned
10516 aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState);
10517 }
10518
10519 // Process the child content
10520 nsFrameItems childItems;
10521 ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame, childItems);
10522
10523 nsFrameList::FrameLinkEnumerator firstBlockEnumerator(childItems);
10524 if (!aItem.mIsAllInline) {
10525 FindFirstBlock(firstBlockEnumerator);
10526 }
10527
10528 if (aItem.mIsAllInline || firstBlockEnumerator.AtEnd()) {
10529 // This part is easy. We either already know we have no non-inline kids,
10530 // or haven't found any when constructing actual frames (the latter can
10531 // happen only if out-of-flows that we thought had no containing block
10532 // acquired one when ancestor inline frames and {ib} splits got
10533 // constructed). Just put all the kids into the single inline frame and
10534 // bail.
10535 newFrame->SetInitialChildList(kPrincipalList, childItems);
10536 aState.AddChild(newFrame, aFrameItems, content, styleContext, aParentFrame);
10537 return newFrame;
10538 }
10539
10540 // This inline frame contains several types of children. Therefore this frame
10541 // has to be chopped into several pieces, as described above.
10542
10543 // Grab the first inline's kids
10544 nsFrameList firstInlineKids = childItems.ExtractHead(firstBlockEnumerator);
10545 newFrame->SetInitialChildList(kPrincipalList, firstInlineKids);
10546
10547 aFrameItems.AddChild(newFrame);
10548
10549 CreateIBSiblings(aState, newFrame, positioned, childItems, aFrameItems);
10550
10551 return newFrame;
10552 }
10553
10554 void
10555 nsCSSFrameConstructor::CreateIBSiblings(nsFrameConstructorState& aState,
10556 nsIFrame* aInitialInline,
10557 bool aIsPositioned,
10558 nsFrameItems& aChildItems,
10559 nsFrameItems& aSiblings)
10560 {
10561 nsIContent* content = aInitialInline->GetContent();
10562 nsStyleContext* styleContext = aInitialInline->StyleContext();
10563 nsIFrame* parentFrame = aInitialInline->GetParent();
10564
10565 // Resolve the right style context for our anonymous blocks.
10566 // The distinction in styles is needed because of CSS 2.1, section
10567 // 9.2.1.1, which says:
10568 // When such an inline box is affected by relative positioning, any
10569 // resulting translation also affects the block-level box contained
10570 // in the inline box.
10571 nsRefPtr<nsStyleContext> blockSC =
10572 mPresShell->StyleSet()->
10573 ResolveAnonymousBoxStyle(aIsPositioned ?
10574 nsCSSAnonBoxes::mozAnonymousPositionedBlock :
10575 nsCSSAnonBoxes::mozAnonymousBlock,
10576 styleContext);
10577
10578 nsIFrame* lastNewInline = aInitialInline->FirstContinuation();
10579 do {
10580 // On entry to this loop aChildItems is not empty and the first frame in it
10581 // is block-level.
10582 NS_PRECONDITION(aChildItems.NotEmpty(), "Should have child items");
10583 NS_PRECONDITION(!aChildItems.FirstChild()->IsInlineOutside(),
10584 "Must have list starting with block");
10585
10586 // The initial run of blocks belongs to an anonymous block that we create
10587 // right now. The anonymous block will be the parent of these block
10588 // children of the inline.
10589 nsIFrame* blockFrame;
10590 blockFrame = NS_NewBlockFrame(mPresShell, blockSC);
10591
10592 InitAndRestoreFrame(aState, content, parentFrame, blockFrame, false);
10593
10594 // Find the first non-block child which defines the end of our block kids
10595 // and the start of our next inline's kids
10596 nsFrameList::FrameLinkEnumerator firstNonBlock =
10597 FindFirstNonBlock(aChildItems);
10598 nsFrameList blockKids = aChildItems.ExtractHead(firstNonBlock);
10599
10600 MoveChildrenTo(aState.mPresContext, aInitialInline, blockFrame, blockKids);
10601
10602 SetFrameIsIBSplit(lastNewInline, blockFrame);
10603 aSiblings.AddChild(blockFrame);
10604
10605 // Now grab the initial inlines in aChildItems and put them into an inline
10606 // frame
10607 nsIFrame* inlineFrame = NS_NewInlineFrame(mPresShell, styleContext);
10608
10609 InitAndRestoreFrame(aState, content, parentFrame, inlineFrame, false);
10610
10611 inlineFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT |
10612 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
10613 if (aIsPositioned) {
10614 inlineFrame->MarkAsAbsoluteContainingBlock();
10615 }
10616
10617 if (aChildItems.NotEmpty()) {
10618 nsFrameList::FrameLinkEnumerator firstBlock(aChildItems);
10619 FindFirstBlock(firstBlock);
10620 nsFrameList inlineKids = aChildItems.ExtractHead(firstBlock);
10621
10622 MoveChildrenTo(aState.mPresContext, aInitialInline, inlineFrame,
10623 inlineKids);
10624 }
10625
10626 SetFrameIsIBSplit(blockFrame, inlineFrame);
10627 aSiblings.AddChild(inlineFrame);
10628 lastNewInline = inlineFrame;
10629 } while (aChildItems.NotEmpty());
10630
10631 SetFrameIsIBSplit(lastNewInline, nullptr);
10632 }
10633
10634 void
10635 nsCSSFrameConstructor::BuildInlineChildItems(nsFrameConstructorState& aState,
10636 FrameConstructionItem& aParentItem,
10637 bool aItemIsWithinSVGText,
10638 bool aItemAllowsTextPathChild)
10639 {
10640 // XXXbz should we preallocate aParentItem.mChildItems to some sane
10641 // length? Maybe even to parentContent->GetChildCount()?
10642 nsFrameConstructorState::PendingBindingAutoPusher
10643 pusher(aState, aParentItem.mPendingBinding);
10644
10645 nsStyleContext* const parentStyleContext = aParentItem.mStyleContext;
10646 nsIContent* const parentContent = aParentItem.mContent;
10647
10648 TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext);
10649 if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
10650 ancestorPusher.PushAncestorAndStyleScope(parentContent->AsElement());
10651 } else {
10652 ancestorPusher.PushStyleScope(parentContent->AsElement());
10653 }
10654
10655 if (!aItemIsWithinSVGText) {
10656 // Probe for generated content before
10657 CreateGeneratedContentItem(aState, nullptr, parentContent, parentStyleContext,
10658 nsCSSPseudoElements::ePseudo_before,
10659 aParentItem.mChildItems);
10660 }
10661
10662 uint32_t flags = ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK;
10663 if (aItemIsWithinSVGText) {
10664 flags |= ITEM_IS_WITHIN_SVG_TEXT;
10665 }
10666 if (aItemAllowsTextPathChild && aParentItem.mIsForSVGAElement) {
10667 flags |= ITEM_ALLOWS_TEXT_PATH_CHILD;
10668 }
10669
10670 if (!aParentItem.mAnonChildren.IsEmpty()) {
10671 // Use the anon-children list instead of the content tree child list so
10672 // that we use any special style context that should be associated with
10673 // the children, and so that we won't try to construct grandchildren frame
10674 // constructor items before the frame is available for their parent.
10675 AddFCItemsForAnonymousContent(aState, nullptr, aParentItem.mAnonChildren,
10676 aParentItem.mChildItems, flags);
10677 } else {
10678 // Use the content tree child list:
10679 FlattenedChildIterator iter(parentContent);
10680 for (nsIContent* content = iter.GetNextChild(); content; content = iter.GetNextChild()) {
10681 // Get the parent of the content and check if it is a XBL children element
10682 // (if the content is a children element then contentParent != parentContent because the
10683 // FlattenedChildIterator will transitively iterate through <xbl:children>
10684 // for default content). Push the children element as an ancestor here because
10685 // it does not have a frame and would not otherwise be pushed as an ancestor.
10686 nsIContent* contentParent = content->GetParent();
10687 MOZ_ASSERT(contentParent, "Parent must be non-null because we are iterating children.");
10688 TreeMatchContext::AutoAncestorPusher insertionPointPusher(aState.mTreeMatchContext);
10689 if (contentParent != parentContent && contentParent->IsElement()) {
10690 if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
10691 insertionPointPusher.PushAncestorAndStyleScope(contentParent->AsElement());
10692 } else {
10693 insertionPointPusher.PushStyleScope(contentParent->AsElement());
10694 }
10695 }
10696
10697 // Manually check for comments/PIs, since we don't have a frame to pass to
10698 // AddFrameConstructionItems. We know our parent is a non-replaced inline,
10699 // so there is no need to do the NeedFrameFor check.
10700 content->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
10701 if (content->IsNodeOfType(nsINode::eCOMMENT) ||
10702 content->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
10703 continue;
10704 }
10705 if (content->IsElement()) {
10706 // See comment explaining why we need to remove the "is possible
10707 // restyle root" flags in AddFrameConstructionItems. But note
10708 // that we can remove all restyle flags, just like in
10709 // ProcessChildren and for the same reason.
10710 content->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
10711 }
10712
10713 nsRefPtr<nsStyleContext> childContext =
10714 ResolveStyleContext(parentStyleContext, content, &aState);
10715
10716 AddFrameConstructionItemsInternal(aState, content, nullptr, content->Tag(),
10717 content->GetNameSpaceID(),
10718 iter.XBLInvolved(), childContext,
10719 flags, nullptr,
10720 aParentItem.mChildItems);
10721 }
10722 }
10723
10724 if (!aItemIsWithinSVGText) {
10725 // Probe for generated content after
10726 CreateGeneratedContentItem(aState, nullptr, parentContent, parentStyleContext,
10727 nsCSSPseudoElements::ePseudo_after,
10728 aParentItem.mChildItems);
10729 }
10730
10731 aParentItem.mIsAllInline = aParentItem.mChildItems.AreAllItemsInline();
10732 }
10733
10734 // return whether it's ok to append (in the AppendFrames sense) to
10735 // aParentFrame if our nextSibling is aNextSibling. aParentFrame must
10736 // be an ib-split inline.
10737 static bool
10738 IsSafeToAppendToIBSplitInline(nsIFrame* aParentFrame, nsIFrame* aNextSibling)
10739 {
10740 NS_PRECONDITION(IsInlineFrame(aParentFrame),
10741 "Must have an inline parent here");
10742 do {
10743 NS_ASSERTION(IsFramePartOfIBSplit(aParentFrame),
10744 "How is this not part of an ib-split?");
10745 if (aNextSibling || aParentFrame->GetNextContinuation() ||
10746 GetIBSplitSibling(aParentFrame)) {
10747 return false;
10748 }
10749
10750 aNextSibling = aParentFrame->GetNextSibling();
10751 aParentFrame = aParentFrame->GetParent();
10752 } while (IsInlineFrame(aParentFrame));
10753
10754 return true;
10755 }
10756
10757 bool
10758 nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState,
10759 nsIFrame* aContainingBlock,
10760 nsIFrame* aFrame,
10761 FrameConstructionItemList& aItems,
10762 bool aIsAppend,
10763 nsIFrame* aPrevSibling)
10764 {
10765 if (aItems.IsEmpty()) {
10766 return false;
10767 }
10768
10769 // Before we go and append the frames, we must check for several
10770 // special situations.
10771
10772 // Situation #1 is a XUL frame that contains frames that are required
10773 // to be wrapped in blocks.
10774 if (aFrame->IsBoxFrame() &&
10775 !(aFrame->GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK) &&
10776 aItems.AnyItemsNeedBlockParent()) {
10777 RecreateFramesForContent(aFrame->GetContent(), true);
10778 return true;
10779 }
10780
10781 nsIFrame* nextSibling = ::GetInsertNextSibling(aFrame, aPrevSibling);
10782
10783 // Situation #2 is a flex container frame into which we're inserting new
10784 // inline non-replaced children, adjacent to an existing anonymous flex item.
10785 if (aFrame->GetType() == nsGkAtoms::flexContainerFrame) {
10786 FCItemIterator iter(aItems);
10787
10788 // Check if we're adding to-be-wrapped content right *after* an existing
10789 // anonymous flex item (which would need to absorb this content).
10790 if (aPrevSibling && IsAnonymousFlexItem(aPrevSibling) &&
10791 iter.item().NeedsAnonFlexItem(aState)) {
10792 RecreateFramesForContent(aFrame->GetContent(), true);
10793 return true;
10794 }
10795
10796 // Check if we're adding to-be-wrapped content right *before* an existing
10797 // anonymous flex item (which would need to absorb this content).
10798 if (nextSibling && IsAnonymousFlexItem(nextSibling)) {
10799 // Jump to the last entry in the list
10800 iter.SetToEnd();
10801 iter.Prev();
10802 if (iter.item().NeedsAnonFlexItem(aState)) {
10803 RecreateFramesForContent(aFrame->GetContent(), true);
10804 return true;
10805 }
10806 }
10807 }
10808
10809 // Situation #3 is an anonymous flex item that's getting new children who
10810 // don't want to be wrapped.
10811 if (IsAnonymousFlexItem(aFrame)) {
10812 nsIFrame* flexContainerFrame = aFrame->GetParent();
10813 NS_ABORT_IF_FALSE(flexContainerFrame &&
10814 flexContainerFrame->GetType() == nsGkAtoms::flexContainerFrame,
10815 "anonymous flex items should only exist as children "
10816 "of flex container frames");
10817
10818 // We need to push a null float containing block to be sure that
10819 // "NeedsAnonFlexItem" will know we're not honoring floats for this
10820 // inserted content. (In particular, this is necessary in order for
10821 // NeedsAnonFlexItem's "GetGeometricParent" call to return the correct
10822 // result.) We're not honoring floats on this content because it has the
10823 // _flex container_ as its parent in the content tree.
10824 nsFrameConstructorSaveState floatSaveState;
10825 aState.PushFloatContainingBlock(nullptr, floatSaveState);
10826
10827 FCItemIterator iter(aItems);
10828 // Skip over things that _do_ need an anonymous flex item, because
10829 // they're perfectly happy to go here -- they won't cause a reframe.
10830 if (!iter.SkipItemsThatNeedAnonFlexItem(aState)) {
10831 // We hit something that _doesn't_ need an anonymous flex item!
10832 // Rebuild the flex container to bust it out.
10833 RecreateFramesForContent(flexContainerFrame->GetContent(), true);
10834 return true;
10835 }
10836
10837 // If we get here, then everything in |aItems| needs to be wrapped in
10838 // an anonymous flex item. That's where it's already going - good!
10839 }
10840
10841 // Situation #4 is a case when table pseudo-frames don't work out right
10842 ParentType parentType = GetParentType(aFrame);
10843 // If all the kids want a parent of the type that aFrame is, then we're all
10844 // set to go. Indeed, there won't be any table pseudo-frames created between
10845 // aFrame and the kids, so those won't need to be merged with any table
10846 // pseudo-frames that might already be kids of aFrame. If aFrame itself is a
10847 // table pseudo-frame, then all the kids in this list would have wanted a
10848 // frame of that type wrapping them anyway, so putting them inside it is ok.
10849 if (!aItems.AllWantParentType(parentType)) {
10850 // Don't give up yet. If parentType is not eTypeBlock and the parent is
10851 // not a generated content frame, then try filtering whitespace out of the
10852 // list.
10853 if (parentType != eTypeBlock && !aFrame->IsGeneratedContentFrame()) {
10854 // For leading whitespace followed by a kid that wants our parent type,
10855 // there are four cases:
10856 // 1) We have a previous sibling which is not a table pseudo. That means
10857 // that previous sibling wanted a (non-block) parent of the type we're
10858 // looking at. Then the whitespace comes between two table-internal
10859 // elements, so should be collapsed out.
10860 // 2) We have a previous sibling which is a table pseudo. It might have
10861 // kids who want this whitespace, so we need to reframe.
10862 // 3) We have no previous sibling and our parent frame is not a table
10863 // pseudo. That means that we'll be at the beginning of our actual
10864 // non-block-type parent, and the whitespace is OK to collapse out.
10865 // If something is ever inserted before us, it'll find our own parent
10866 // as its parent and if it's something that would care about the
10867 // whitespace it'll want a block parent, so it'll trigger a reframe at
10868 // that point.
10869 // 4) We have no previous sibling and our parent frame is a table pseudo.
10870 // Need to reframe.
10871 // All that is predicated on finding the correct previous sibling. We
10872 // might have to walk backwards along continuations from aFrame to do so.
10873 //
10874 // It's always OK to drop whitespace between any two items that want a
10875 // parent of type parentType.
10876 //
10877 // For trailing whitespace preceded by a kid that wants our parent type,
10878 // there are four cases:
10879 // 1) We have a next sibling which is not a table pseudo. That means
10880 // that next sibling wanted a (non-block) parent of the type we're
10881 // looking at. Then the whitespace comes between two table-internal
10882 // elements, so should be collapsed out.
10883 // 2) We have a next sibling which is a table pseudo. It might have
10884 // kids who want this whitespace, so we need to reframe.
10885 // 3) We have no next sibling and our parent frame is not a table
10886 // pseudo. That means that we'll be at the end of our actual
10887 // non-block-type parent, and the whitespace is OK to collapse out.
10888 // If something is ever inserted after us, it'll find our own parent
10889 // as its parent and if it's something that would care about the
10890 // whitespace it'll want a block parent, so it'll trigger a reframe at
10891 // that point.
10892 // 4) We have no next sibling and our parent frame is a table pseudo.
10893 // Need to reframe.
10894 // All that is predicated on finding the correct next sibling. We might
10895 // have to walk forward along continuations from aFrame to do so. That
10896 // said, in the case when nextSibling is null at this point and aIsAppend
10897 // is true, we know we're in case 3. Furthermore, in that case we don't
10898 // even have to worry about the table pseudo situation; we know our
10899 // parent is not a table pseudo there.
10900 FCItemIterator iter(aItems);
10901 FCItemIterator start(iter);
10902 do {
10903 if (iter.SkipItemsWantingParentType(parentType)) {
10904 break;
10905 }
10906
10907 // iter points to an item that wants a different parent. If it's not
10908 // whitespace, we're done; no more point scanning the list.
10909 if (!iter.item().IsWhitespace(aState)) {
10910 break;
10911 }
10912
10913 if (iter == start) {
10914 // Leading whitespace. How to handle this depends on our
10915 // previous sibling and aFrame. See the long comment above.
10916 nsIFrame* prevSibling = aPrevSibling;
10917 if (!prevSibling) {
10918 // Try to find one after all
10919 nsIFrame* parentPrevCont = aFrame->GetPrevContinuation();
10920 while (parentPrevCont) {
10921 prevSibling = parentPrevCont->GetLastChild(kPrincipalList);
10922 if (prevSibling) {
10923 break;
10924 }
10925 parentPrevCont = parentPrevCont->GetPrevContinuation();
10926 }
10927 };
10928 if (prevSibling) {
10929 if (IsTablePseudo(prevSibling)) {
10930 // need to reframe
10931 break;
10932 }
10933 } else if (IsTablePseudo(aFrame)) {
10934 // need to reframe
10935 break;
10936 }
10937 }
10938
10939 FCItemIterator spaceEndIter(iter);
10940 // Advance spaceEndIter past any whitespace
10941 bool trailingSpaces = spaceEndIter.SkipWhitespace(aState);
10942
10943 bool okToDrop;
10944 if (trailingSpaces) {
10945 // Trailing whitespace. How to handle this depeds on aIsAppend, our
10946 // next sibling and aFrame. See the long comment above.
10947 okToDrop = aIsAppend && !nextSibling;
10948 if (!okToDrop) {
10949 if (!nextSibling) {
10950 // Try to find one after all
10951 nsIFrame* parentNextCont = aFrame->GetNextContinuation();
10952 while (parentNextCont) {
10953 nextSibling = parentNextCont->GetFirstPrincipalChild();
10954 if (nextSibling) {
10955 break;
10956 }
10957 parentNextCont = parentNextCont->GetNextContinuation();
10958 }
10959 }
10960
10961 okToDrop = (nextSibling && !IsTablePseudo(nextSibling)) ||
10962 (!nextSibling && !IsTablePseudo(aFrame));
10963 }
10964 #ifdef DEBUG
10965 else {
10966 NS_ASSERTION(!IsTablePseudo(aFrame), "How did that happen?");
10967 }
10968 #endif
10969 } else {
10970 okToDrop = (spaceEndIter.item().DesiredParentType() == parentType);
10971 }
10972
10973 if (okToDrop) {
10974 iter.DeleteItemsTo(spaceEndIter);
10975 } else {
10976 // We're done: we don't want to drop the whitespace, and it has the
10977 // wrong parent type.
10978 break;
10979 }
10980
10981 // Now loop, since |iter| points to item right after the whitespace we
10982 // removed.
10983 } while (!iter.IsDone());
10984 }
10985
10986 // We might be able to figure out some sort of optimizations here, but they
10987 // would have to depend on having a correct aPrevSibling and a correct next
10988 // sibling. For example, we can probably avoid reframing if none of
10989 // aFrame, aPrevSibling, and next sibling are table pseudo-frames. But it
10990 // doesn't seem worth it to worry about that for now, especially since we
10991 // in fact do not have a reliable aPrevSibling, nor any next sibling, in
10992 // this method.
10993
10994 // aItems might have changed, so recheck the parent type thing. In fact,
10995 // it might be empty, so recheck that too.
10996 if (aItems.IsEmpty()) {
10997 return false;
10998 }
10999
11000 if (!aItems.AllWantParentType(parentType)) {
11001 // Reframing aFrame->GetContent() is good enough, since the content of
11002 // table pseudo-frames is the ancestor content.
11003 RecreateFramesForContent(aFrame->GetContent(), true);
11004 return true;
11005 }
11006 }
11007
11008 // Now we have several cases involving {ib} splits. Put them all in a
11009 // do/while with breaks to take us to the "go and reconstruct" code.
11010 do {
11011 if (IsInlineFrame(aFrame)) {
11012 if (aItems.AreAllItemsInline()) {
11013 // We can just put the kids in.
11014 return false;
11015 }
11016
11017 if (!IsFramePartOfIBSplit(aFrame)) {
11018 // Need to go ahead and reconstruct.
11019 break;
11020 }
11021
11022 // Now we're adding kids including some blocks to an inline part of an
11023 // {ib} split. If we plan to call AppendFrames, and don't have a next
11024 // sibling for the new frames, and our parent is the last continuation of
11025 // the last part of the {ib} split, and the same is true of all our
11026 // ancestor inlines (they have no following continuations and they're the
11027 // last part of their {ib} splits and we'd be adding to the end for all
11028 // of them), then AppendFrames will handle things for us. Bail out in
11029 // that case.
11030 if (aIsAppend && IsSafeToAppendToIBSplitInline(aFrame, nextSibling)) {
11031 return false;
11032 }
11033
11034 // Need to reconstruct.
11035 break;
11036 }
11037
11038 // Now we know we have a block parent. If it's not part of an
11039 // ib-split, we're all set.
11040 if (!IsFramePartOfIBSplit(aFrame)) {
11041 return false;
11042 }
11043
11044 // We're adding some kids to a block part of an {ib} split. If all the
11045 // kids are blocks, we don't need to reconstruct.
11046 if (aItems.AreAllItemsBlock()) {
11047 return false;
11048 }
11049
11050 // We might have some inline kids for this block. Just reconstruct.
11051 break;
11052 } while (0);
11053
11054 // If we don't have a containing block, start with aFrame and look for one.
11055 if (!aContainingBlock) {
11056 aContainingBlock = aFrame;
11057 }
11058
11059 // To find the right block to reframe, just walk up the tree until we find a
11060 // frame that is:
11061 // 1) Not part of an IB split
11062 // 2) Not a pseudo-frame
11063 // 3) Not an inline frame
11064 // We're guaranteed to find one, since nsStyleContext::ApplyStyleFixups
11065 // enforces that the root is display:none, display:table, or display:block.
11066 // Note that walking up "too far" is OK in terms of correctness, even if it
11067 // might be a little inefficient. This is why we walk out of all
11068 // pseudo-frames -- telling which ones are or are not OK to walk out of is
11069 // too hard (and I suspect that we do in fact need to walk out of all of
11070 // them).
11071 while (IsFramePartOfIBSplit(aContainingBlock) ||
11072 aContainingBlock->IsInlineOutside() ||
11073 aContainingBlock->StyleContext()->GetPseudo()) {
11074 aContainingBlock = aContainingBlock->GetParent();
11075 NS_ASSERTION(aContainingBlock,
11076 "Must have non-inline, non-ib-split, non-pseudo frame as "
11077 "root (or child of root, for a table root)!");
11078 }
11079
11080 // Tell parent of the containing block to reformulate the
11081 // entire block. This is painful and definitely not optimal
11082 // but it will *always* get the right answer.
11083
11084 nsIContent *blockContent = aContainingBlock->GetContent();
11085 #ifdef DEBUG
11086 if (gNoisyContentUpdates) {
11087 printf("nsCSSFrameConstructor::WipeContainingBlock: blockContent=%p\n",
11088 static_cast<void*>(blockContent));
11089 }
11090 #endif
11091 RecreateFramesForContent(blockContent, true);
11092 return true;
11093 }
11094
11095 nsresult
11096 nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame)
11097 {
11098
11099 #ifdef DEBUG
11100 // ReframeContainingBlock is a NASTY routine, it causes terrible performance problems
11101 // so I want to see when it is happening! Unfortunately, it is happening way to often because
11102 // so much content on the web causes block-in-inline frame situations and we handle them
11103 // very poorly
11104 if (gNoisyContentUpdates) {
11105 printf("nsCSSFrameConstructor::ReframeContainingBlock frame=%p\n",
11106 static_cast<void*>(aFrame));
11107 }
11108 #endif
11109
11110 // XXXbz how exactly would we get here while isReflowing anyway? Should this
11111 // whole test be ifdef DEBUG?
11112 if (mPresShell->IsReflowLocked()) {
11113 // don't ReframeContainingBlock, this will result in a crash
11114 // if we remove a tree that's in reflow - see bug 121368 for testcase
11115 NS_ERROR("Atemptted to nsCSSFrameConstructor::ReframeContainingBlock during a Reflow!!!");
11116 return NS_OK;
11117 }
11118
11119 // Get the first "normal" ancestor of the target frame.
11120 nsIFrame* containingBlock = GetIBContainingBlockFor(aFrame);
11121 if (containingBlock) {
11122 // From here we look for the containing block in case the target
11123 // frame is already a block (which can happen when an inline frame
11124 // wraps some of its content in an anonymous block; see
11125 // ConstructInline)
11126
11127 // NOTE: We used to get the FloatContainingBlock here, but it was often wrong.
11128 // GetIBContainingBlock works much better and provides the correct container in all cases
11129 // so GetFloatContainingBlock(aFrame) has been removed
11130
11131 // And get the containingBlock's content
11132 nsCOMPtr<nsIContent> blockContent = containingBlock->GetContent();
11133 if (blockContent) {
11134 #ifdef DEBUG
11135 if (gNoisyContentUpdates) {
11136 printf(" ==> blockContent=%p\n", static_cast<void*>(blockContent));
11137 }
11138 #endif
11139 return RecreateFramesForContent(blockContent, true);
11140 }
11141 }
11142
11143 // If we get here, we're screwed!
11144 return RecreateFramesForContent(mPresShell->GetDocument()->GetRootElement(),
11145 true);
11146 }
11147
11148 nsresult
11149 nsCSSFrameConstructor::GenerateChildFrames(nsIFrame* aFrame)
11150 {
11151 {
11152 nsAutoScriptBlocker scriptBlocker;
11153 BeginUpdate();
11154
11155 nsFrameItems childItems;
11156 nsFrameConstructorState state(mPresShell, nullptr, nullptr, nullptr);
11157 // We don't have a parent frame with a pending binding constructor here,
11158 // so no need to worry about ordering of the kids' constructors with it.
11159 // Pass null for the PendingBinding.
11160 ProcessChildren(state, aFrame->GetContent(), aFrame->StyleContext(),
11161 aFrame, false, childItems, false,
11162 nullptr);
11163
11164 aFrame->SetInitialChildList(kPrincipalList, childItems);
11165
11166 EndUpdate();
11167 }
11168
11169 #ifdef ACCESSIBILITY
11170 nsAccessibilityService* accService = nsIPresShell::AccService();
11171 if (accService) {
11172 nsIContent* container = aFrame->GetContent();
11173 nsIContent* child = container->GetFirstChild();
11174 if (child) {
11175 accService->ContentRangeInserted(mPresShell, container, child, nullptr);
11176 }
11177 }
11178 #endif
11179
11180 // call XBL constructors after the frames are created
11181 mPresShell->GetDocument()->BindingManager()->ProcessAttachedQueue();
11182
11183 return NS_OK;
11184 }
11185
11186 //////////////////////////////////////////////////////////
11187 // nsCSSFrameConstructor::FrameConstructionItem methods //
11188 //////////////////////////////////////////////////////////
11189 bool
11190 nsCSSFrameConstructor::
11191 FrameConstructionItem::IsWhitespace(nsFrameConstructorState& aState) const
11192 {
11193 NS_PRECONDITION(aState.mCreatingExtraFrames ||
11194 !mContent->GetPrimaryFrame(), "How did that happen?");
11195 if (!mIsText) {
11196 return false;
11197 }
11198 mContent->SetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
11199 NS_REFRAME_IF_WHITESPACE);
11200 return mContent->TextIsOnlyWhitespace();
11201 }
11202
11203 //////////////////////////////////////////////////////////////
11204 // nsCSSFrameConstructor::FrameConstructionItemList methods //
11205 //////////////////////////////////////////////////////////////
11206 void
11207 nsCSSFrameConstructor::FrameConstructionItemList::
11208 AdjustCountsForItem(FrameConstructionItem* aItem, int32_t aDelta)
11209 {
11210 NS_PRECONDITION(aDelta == 1 || aDelta == -1, "Unexpected delta");
11211 mItemCount += aDelta;
11212 if (aItem->mIsAllInline) {
11213 mInlineCount += aDelta;
11214 }
11215 if (aItem->mIsBlock) {
11216 mBlockCount += aDelta;
11217 }
11218 if (aItem->mIsLineParticipant) {
11219 mLineParticipantCount += aDelta;
11220 }
11221 mDesiredParentCounts[aItem->DesiredParentType()] += aDelta;
11222 }
11223
11224 ////////////////////////////////////////////////////////////////////////
11225 // nsCSSFrameConstructor::FrameConstructionItemList::Iterator methods //
11226 ////////////////////////////////////////////////////////////////////////
11227 inline bool
11228 nsCSSFrameConstructor::FrameConstructionItemList::
11229 Iterator::SkipItemsWantingParentType(ParentType aParentType)
11230 {
11231 NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
11232 while (item().DesiredParentType() == aParentType) {
11233 Next();
11234 if (IsDone()) {
11235 return true;
11236 }
11237 }
11238 return false;
11239 }
11240
11241 bool
11242 nsCSSFrameConstructor::FrameConstructionItem::
11243 NeedsAnonFlexItem(const nsFrameConstructorState& aState)
11244 {
11245 if (mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) {
11246 // This will be an inline non-replaced box.
11247 return true;
11248 }
11249
11250 if (!(mFCData->mBits & FCDATA_DISALLOW_OUT_OF_FLOW) &&
11251 aState.GetGeometricParent(mStyleContext->StyleDisplay(), nullptr)) {
11252 // We're abspos or fixedpos, which means we'll spawn a placeholder which
11253 // we'll need to wrap in an anonymous flex item. So, we just treat
11254 // _this_ frame as if _it_ needs to be wrapped in an anonymous flex item,
11255 // and then when we spawn the placeholder, it'll end up in the right spot.
11256 return true;
11257 }
11258
11259 return false;
11260 }
11261
11262 inline bool
11263 nsCSSFrameConstructor::FrameConstructionItemList::
11264 Iterator::SkipItemsThatNeedAnonFlexItem(
11265 const nsFrameConstructorState& aState)
11266 {
11267 NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
11268 while (item().NeedsAnonFlexItem(aState)) {
11269 Next();
11270 if (IsDone()) {
11271 return true;
11272 }
11273 }
11274 return false;
11275 }
11276
11277 inline bool
11278 nsCSSFrameConstructor::FrameConstructionItemList::
11279 Iterator::SkipItemsThatDontNeedAnonFlexItem(
11280 const nsFrameConstructorState& aState)
11281 {
11282 NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
11283 while (!(item().NeedsAnonFlexItem(aState))) {
11284 Next();
11285 if (IsDone()) {
11286 return true;
11287 }
11288 }
11289 return false;
11290 }
11291
11292 inline bool
11293 nsCSSFrameConstructor::FrameConstructionItemList::
11294 Iterator::SkipWhitespace(nsFrameConstructorState& aState)
11295 {
11296 NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
11297 NS_PRECONDITION(item().IsWhitespace(aState), "Not pointing to whitespace?");
11298 do {
11299 Next();
11300 if (IsDone()) {
11301 return true;
11302 }
11303 } while (item().IsWhitespace(aState));
11304
11305 return false;
11306 }
11307
11308 void
11309 nsCSSFrameConstructor::FrameConstructionItemList::
11310 Iterator::AppendItemToList(FrameConstructionItemList& aTargetList)
11311 {
11312 NS_ASSERTION(&aTargetList != &mList, "Unexpected call");
11313 NS_PRECONDITION(!IsDone(), "should not be done");
11314
11315 FrameConstructionItem* item = ToItem(mCurrent);
11316 Next();
11317 PR_REMOVE_LINK(item);
11318 PR_APPEND_LINK(item, &aTargetList.mItems);
11319
11320 mList.AdjustCountsForItem(item, -1);
11321 aTargetList.AdjustCountsForItem(item, 1);
11322 }
11323
11324 void
11325 nsCSSFrameConstructor::FrameConstructionItemList::
11326 Iterator::AppendItemsToList(const Iterator& aEnd,
11327 FrameConstructionItemList& aTargetList)
11328 {
11329 NS_ASSERTION(&aTargetList != &mList, "Unexpected call");
11330 NS_PRECONDITION(mEnd == aEnd.mEnd, "end iterator for some other list?");
11331
11332 // We can't just move our guts to the other list if it already has
11333 // some information or if we're not moving our entire list.
11334 if (!AtStart() || !aEnd.IsDone() || !aTargetList.IsEmpty() ||
11335 !aTargetList.mUndisplayedItems.IsEmpty()) {
11336 do {
11337 AppendItemToList(aTargetList);
11338 } while (*this != aEnd);
11339 return;
11340 }
11341
11342 // move over the list of items
11343 PR_INSERT_AFTER(&aTargetList.mItems, &mList.mItems);
11344 // Need to init when we remove to makd ~FrameConstructionItemList work right.
11345 PR_REMOVE_AND_INIT_LINK(&mList.mItems);
11346
11347 // Copy over the various counters
11348 aTargetList.mInlineCount = mList.mInlineCount;
11349 aTargetList.mBlockCount = mList.mBlockCount;
11350 aTargetList.mLineParticipantCount = mList.mLineParticipantCount;
11351 aTargetList.mItemCount = mList.mItemCount;
11352 memcpy(aTargetList.mDesiredParentCounts, mList.mDesiredParentCounts,
11353 sizeof(aTargetList.mDesiredParentCounts));
11354
11355 // Swap out undisplayed item arrays, before we nuke the array on our end
11356 aTargetList.mUndisplayedItems.SwapElements(mList.mUndisplayedItems);
11357
11358 // reset mList
11359 mList.~FrameConstructionItemList();
11360 new (&mList) FrameConstructionItemList();
11361
11362 // Point ourselves to aEnd, as advertised
11363 mCurrent = mEnd = &mList.mItems;
11364 NS_POSTCONDITION(*this == aEnd, "How did that happen?");
11365 }
11366
11367 void
11368 nsCSSFrameConstructor::FrameConstructionItemList::
11369 Iterator::InsertItem(FrameConstructionItem* aItem)
11370 {
11371 // Just insert the item before us. There's no magic here.
11372 PR_INSERT_BEFORE(aItem, mCurrent);
11373 mList.AdjustCountsForItem(aItem, 1);
11374
11375 NS_POSTCONDITION(PR_NEXT_LINK(aItem) == mCurrent, "How did that happen?");
11376 }
11377
11378 void
11379 nsCSSFrameConstructor::FrameConstructionItemList::
11380 Iterator::DeleteItemsTo(const Iterator& aEnd)
11381 {
11382 NS_PRECONDITION(mEnd == aEnd.mEnd, "end iterator for some other list?");
11383 NS_PRECONDITION(*this != aEnd, "Shouldn't be at aEnd yet");
11384
11385 do {
11386 NS_ASSERTION(!IsDone(), "Ran off end of list?");
11387 FrameConstructionItem* item = ToItem(mCurrent);
11388 Next();
11389 PR_REMOVE_LINK(item);
11390 mList.AdjustCountsForItem(item, -1);
11391 delete item;
11392 } while (*this != aEnd);
11393 }

mercurial