|
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 } |