layout/base/nsCSSFrameConstructor.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 // vim:cindent:ts=2:et:sw=2:
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /*
michael@0 8 * construction of a frame tree that is nearly isomorphic to the content
michael@0 9 * tree and updating of that tree in response to dynamic changes
michael@0 10 */
michael@0 11
michael@0 12 #include "nsCSSFrameConstructor.h"
michael@0 13
michael@0 14 #include "mozilla/AutoRestore.h"
michael@0 15 #include "mozilla/DebugOnly.h"
michael@0 16 #include "mozilla/dom/HTMLSelectElement.h"
michael@0 17 #include "mozilla/EventStates.h"
michael@0 18 #include "mozilla/Likely.h"
michael@0 19 #include "mozilla/LinkedList.h"
michael@0 20 #include "nsAbsoluteContainingBlock.h"
michael@0 21 #include "nsIAtom.h"
michael@0 22 #include "nsIFrameInlines.h"
michael@0 23 #include "nsGkAtoms.h"
michael@0 24 #include "nsPresContext.h"
michael@0 25 #include "nsIDocument.h"
michael@0 26 #include "nsTableFrame.h"
michael@0 27 #include "nsTableColFrame.h"
michael@0 28 #include "nsIDOMHTMLDocument.h"
michael@0 29 #include "nsHTMLParts.h"
michael@0 30 #include "nsIPresShell.h"
michael@0 31 #include "nsUnicharUtils.h"
michael@0 32 #include "nsStyleSet.h"
michael@0 33 #include "nsViewManager.h"
michael@0 34 #include "nsStyleConsts.h"
michael@0 35 #include "nsIDOMXULElement.h"
michael@0 36 #include "nsContainerFrame.h"
michael@0 37 #include "nsNameSpaceManager.h"
michael@0 38 #include "nsIComboboxControlFrame.h"
michael@0 39 #include "nsIListControlFrame.h"
michael@0 40 #include "nsIDOMCharacterData.h"
michael@0 41 #include "nsPlaceholderFrame.h"
michael@0 42 #include "nsTableRowGroupFrame.h"
michael@0 43 #include "nsIFormControl.h"
michael@0 44 #include "nsCSSAnonBoxes.h"
michael@0 45 #include "nsTextFragment.h"
michael@0 46 #include "nsIAnonymousContentCreator.h"
michael@0 47 #include "nsBindingManager.h"
michael@0 48 #include "nsXBLBinding.h"
michael@0 49 #include "nsContentUtils.h"
michael@0 50 #include "nsIScriptError.h"
michael@0 51 #ifdef XP_MACOSX
michael@0 52 #include "nsIDocShell.h"
michael@0 53 #endif
michael@0 54 #include "ChildIterator.h"
michael@0 55 #include "nsError.h"
michael@0 56 #include "nsLayoutUtils.h"
michael@0 57 #include "nsAutoPtr.h"
michael@0 58 #include "nsBoxFrame.h"
michael@0 59 #include "nsBoxLayout.h"
michael@0 60 #include "nsFlexContainerFrame.h"
michael@0 61 #include "nsGridContainerFrame.h"
michael@0 62 #include "nsImageFrame.h"
michael@0 63 #include "nsIObjectLoadingContent.h"
michael@0 64 #include "nsTArray.h"
michael@0 65 #include "nsGenericDOMDataNode.h"
michael@0 66 #include "mozilla/dom/Element.h"
michael@0 67 #include "nsAutoLayoutPhase.h"
michael@0 68 #include "nsStyleStructInlines.h"
michael@0 69 #include "nsPageContentFrame.h"
michael@0 70 #include "RestyleManager.h"
michael@0 71 #include "StickyScrollContainer.h"
michael@0 72 #include "nsFieldSetFrame.h"
michael@0 73
michael@0 74 #ifdef MOZ_XUL
michael@0 75 #include "nsIRootBox.h"
michael@0 76 #endif
michael@0 77 #ifdef ACCESSIBILITY
michael@0 78 #include "nsAccessibilityService.h"
michael@0 79 #endif
michael@0 80
michael@0 81 #include "nsBlockFrame.h"
michael@0 82
michael@0 83 #include "nsIScrollableFrame.h"
michael@0 84
michael@0 85 #include "nsXBLService.h"
michael@0 86
michael@0 87 #undef NOISY_FIRST_LETTER
michael@0 88
michael@0 89 #include "nsMathMLParts.h"
michael@0 90 #include "mozilla/dom/SVGTests.h"
michael@0 91 #include "nsSVGUtils.h"
michael@0 92
michael@0 93 #include "nsRefreshDriver.h"
michael@0 94 #include "nsRuleProcessorData.h"
michael@0 95 #include "nsTextNode.h"
michael@0 96
michael@0 97 using namespace mozilla;
michael@0 98 using namespace mozilla::dom;
michael@0 99
michael@0 100 // An alias for convenience.
michael@0 101 static const nsIFrame::ChildListID kPrincipalList = nsIFrame::kPrincipalList;
michael@0 102
michael@0 103 nsIFrame*
michael@0 104 NS_NewHTMLCanvasFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 105
michael@0 106 nsIFrame*
michael@0 107 NS_NewHTMLVideoFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 108
michael@0 109 nsIFrame*
michael@0 110 NS_NewSVGOuterSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 111 nsIFrame*
michael@0 112 NS_NewSVGOuterSVGAnonChildFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 113 nsIFrame*
michael@0 114 NS_NewSVGInnerSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 115 nsIFrame*
michael@0 116 NS_NewSVGPathGeometryFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 117 nsIFrame*
michael@0 118 NS_NewSVGGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 119 nsIFrame*
michael@0 120 NS_NewSVGGenericContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 121 nsIFrame*
michael@0 122 NS_NewSVGForeignObjectFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 123 nsIFrame*
michael@0 124 NS_NewSVGAFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 125 nsIFrame*
michael@0 126 NS_NewSVGSwitchFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 127 nsIFrame*
michael@0 128 NS_NewSVGTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 129 nsIFrame*
michael@0 130 NS_NewSVGContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 131 nsIFrame*
michael@0 132 NS_NewSVGUseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 133 nsIFrame*
michael@0 134 NS_NewSVGViewFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 135 extern nsIFrame*
michael@0 136 NS_NewSVGLinearGradientFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
michael@0 137 extern nsIFrame*
michael@0 138 NS_NewSVGRadialGradientFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
michael@0 139 extern nsIFrame*
michael@0 140 NS_NewSVGStopFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
michael@0 141 nsIFrame*
michael@0 142 NS_NewSVGMarkerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 143 nsIFrame*
michael@0 144 NS_NewSVGMarkerAnonChildFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 145 extern nsIFrame*
michael@0 146 NS_NewSVGImageFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
michael@0 147 nsIFrame*
michael@0 148 NS_NewSVGClipPathFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 149 nsIFrame*
michael@0 150 NS_NewSVGFilterFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
michael@0 151 nsIFrame*
michael@0 152 NS_NewSVGPatternFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 153 nsIFrame*
michael@0 154 NS_NewSVGMaskFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 155 nsIFrame*
michael@0 156 NS_NewSVGFEContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 157 nsIFrame*
michael@0 158 NS_NewSVGFELeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 159 nsIFrame*
michael@0 160 NS_NewSVGFEImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 161 nsIFrame*
michael@0 162 NS_NewSVGFEUnstyledLeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 163
michael@0 164 #include "nsINodeInfo.h"
michael@0 165 #include "prenv.h"
michael@0 166 #include "nsNodeInfoManager.h"
michael@0 167 #include "nsContentCreatorFunctions.h"
michael@0 168
michael@0 169 #ifdef DEBUG
michael@0 170 // Set the environment variable GECKO_FRAMECTOR_DEBUG_FLAGS to one or
michael@0 171 // more of the following flags (comma separated) for handy debug
michael@0 172 // output.
michael@0 173 static bool gNoisyContentUpdates = false;
michael@0 174 static bool gReallyNoisyContentUpdates = false;
michael@0 175 static bool gNoisyInlineConstruction = false;
michael@0 176
michael@0 177 struct FrameCtorDebugFlags {
michael@0 178 const char* name;
michael@0 179 bool* on;
michael@0 180 };
michael@0 181
michael@0 182 static FrameCtorDebugFlags gFlags[] = {
michael@0 183 { "content-updates", &gNoisyContentUpdates },
michael@0 184 { "really-noisy-content-updates", &gReallyNoisyContentUpdates },
michael@0 185 { "noisy-inline", &gNoisyInlineConstruction }
michael@0 186 };
michael@0 187
michael@0 188 #define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
michael@0 189 #endif
michael@0 190
michael@0 191
michael@0 192 #ifdef MOZ_XUL
michael@0 193 #include "nsMenuFrame.h"
michael@0 194 #include "nsPopupSetFrame.h"
michael@0 195 #include "nsTreeColFrame.h"
michael@0 196 #include "nsIBoxObject.h"
michael@0 197 #include "nsPIListBoxObject.h"
michael@0 198 #include "nsListBoxBodyFrame.h"
michael@0 199 #include "nsListItemFrame.h"
michael@0 200 #include "nsXULLabelFrame.h"
michael@0 201
michael@0 202 //------------------------------------------------------------------
michael@0 203
michael@0 204 nsIFrame*
michael@0 205 NS_NewAutoRepeatBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 206
michael@0 207 nsIFrame*
michael@0 208 NS_NewRootBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 209
michael@0 210 nsIFrame*
michael@0 211 NS_NewDocElementBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 212
michael@0 213 nsIFrame*
michael@0 214 NS_NewThumbFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 215
michael@0 216 nsIFrame*
michael@0 217 NS_NewDeckFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 218
michael@0 219 nsIFrame*
michael@0 220 NS_NewLeafBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 221
michael@0 222 nsIFrame*
michael@0 223 NS_NewStackFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 224
michael@0 225 nsIFrame*
michael@0 226 NS_NewProgressMeterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 227
michael@0 228 nsIFrame*
michael@0 229 NS_NewRangeFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 230
michael@0 231 nsIFrame*
michael@0 232 NS_NewImageBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 233
michael@0 234 nsIFrame*
michael@0 235 NS_NewTextBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 236
michael@0 237 nsIFrame*
michael@0 238 NS_NewGroupBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 239
michael@0 240 nsIFrame*
michael@0 241 NS_NewButtonBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 242
michael@0 243 nsIFrame*
michael@0 244 NS_NewSplitterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 245
michael@0 246 nsIFrame*
michael@0 247 NS_NewMenuPopupFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 248
michael@0 249 nsIFrame*
michael@0 250 NS_NewPopupSetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 251
michael@0 252 nsIFrame*
michael@0 253 NS_NewMenuFrame (nsIPresShell* aPresShell, nsStyleContext* aContext, uint32_t aFlags);
michael@0 254
michael@0 255 nsIFrame*
michael@0 256 NS_NewMenuBarFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 257
michael@0 258 nsIFrame*
michael@0 259 NS_NewTreeBodyFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 260
michael@0 261 // grid
michael@0 262 nsresult
michael@0 263 NS_NewGridLayout2 ( nsIPresShell* aPresShell, nsBoxLayout** aNewLayout );
michael@0 264 nsIFrame*
michael@0 265 NS_NewGridRowLeafFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 266 nsIFrame*
michael@0 267 NS_NewGridRowGroupFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 268
michael@0 269 // end grid
michael@0 270
michael@0 271 nsIFrame*
michael@0 272 NS_NewTitleBarFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 273
michael@0 274 nsIFrame*
michael@0 275 NS_NewResizerFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 276
michael@0 277
michael@0 278 #endif
michael@0 279
michael@0 280 nsIFrame*
michael@0 281 NS_NewHTMLScrollFrame (nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot);
michael@0 282
michael@0 283 nsIFrame*
michael@0 284 NS_NewXULScrollFrame (nsIPresShell* aPresShell, nsStyleContext* aContext,
michael@0 285 bool aIsRoot, bool aClipAllDescendants);
michael@0 286
michael@0 287 nsIFrame*
michael@0 288 NS_NewSliderFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 289
michael@0 290 nsIFrame*
michael@0 291 NS_NewScrollbarFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 292
michael@0 293 nsIFrame*
michael@0 294 NS_NewScrollbarButtonFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 295
michael@0 296
michael@0 297 #ifdef NOISY_FINDFRAME
michael@0 298 static int32_t FFWC_totalCount=0;
michael@0 299 static int32_t FFWC_doLoop=0;
michael@0 300 static int32_t FFWC_doSibling=0;
michael@0 301 static int32_t FFWC_recursions=0;
michael@0 302 static int32_t FFWC_nextInFlows=0;
michael@0 303 #endif
michael@0 304
michael@0 305 // Returns true if aFrame is an anonymous flex item
michael@0 306 static inline bool
michael@0 307 IsAnonymousFlexItem(const nsIFrame* aFrame)
michael@0 308 {
michael@0 309 const nsIAtom* pseudoType = aFrame->StyleContext()->GetPseudo();
michael@0 310 return pseudoType == nsCSSAnonBoxes::anonymousFlexItem;
michael@0 311 }
michael@0 312
michael@0 313 static inline nsIFrame*
michael@0 314 GetFieldSetBlockFrame(nsIFrame* aFieldsetFrame)
michael@0 315 {
michael@0 316 // Depends on the fieldset child frame order - see ConstructFieldSetFrame() below.
michael@0 317 nsIFrame* firstChild = aFieldsetFrame->GetFirstPrincipalChild();
michael@0 318 nsIFrame* inner = firstChild && firstChild->GetNextSibling() ? firstChild->GetNextSibling() : firstChild;
michael@0 319 return inner ? inner->GetContentInsertionFrame() : nullptr;
michael@0 320 }
michael@0 321
michael@0 322 #define FCDATA_DECL(_flags, _func) \
michael@0 323 { _flags, { (FrameCreationFunc)_func }, nullptr, nullptr }
michael@0 324 #define FCDATA_WITH_WRAPPING_BLOCK(_flags, _func, _anon_box) \
michael@0 325 { _flags | FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS, \
michael@0 326 { (FrameCreationFunc)_func }, nullptr, &_anon_box }
michael@0 327
michael@0 328 //----------------------------------------------------------------------
michael@0 329
michael@0 330 /**
michael@0 331 * True if aFrame is an actual inline frame in the sense of non-replaced
michael@0 332 * display:inline CSS boxes. In other words, it can be affected by {ib}
michael@0 333 * splitting and can contain first-letter frames. Basically, this is either an
michael@0 334 * inline frame (positioned or otherwise) or an line frame (this last because
michael@0 335 * it can contain first-letter and because inserting blocks in the middle of it
michael@0 336 * needs to terminate it).
michael@0 337 */
michael@0 338 static bool
michael@0 339 IsInlineFrame(const nsIFrame* aFrame)
michael@0 340 {
michael@0 341 return aFrame->IsFrameOfType(nsIFrame::eLineParticipant);
michael@0 342 }
michael@0 343
michael@0 344 /**
michael@0 345 * True if aFrame is an instance of an SVG frame class or is an inline/block
michael@0 346 * frame being used for SVG text.
michael@0 347 */
michael@0 348 static bool
michael@0 349 IsFrameForSVG(const nsIFrame* aFrame)
michael@0 350 {
michael@0 351 return aFrame->IsFrameOfType(nsIFrame::eSVG) ||
michael@0 352 aFrame->IsSVGText();
michael@0 353 }
michael@0 354
michael@0 355 /**
michael@0 356 * Returns true iff aFrame explicitly prevents its descendants from floating
michael@0 357 * (at least, down to the level of descendants which themselves are
michael@0 358 * float-containing blocks -- those will manage the floating status of any
michael@0 359 * lower-level descendents inside them, of course).
michael@0 360 */
michael@0 361 static bool
michael@0 362 ShouldSuppressFloatingOfDescendants(nsIFrame* aFrame)
michael@0 363 {
michael@0 364 return aFrame->IsFrameOfType(nsIFrame::eMathML) ||
michael@0 365 aFrame->IsBoxFrame() ||
michael@0 366 aFrame->GetType() == nsGkAtoms::flexContainerFrame ||
michael@0 367 aFrame->GetType() == nsGkAtoms::gridContainerFrame;
michael@0 368 }
michael@0 369
michael@0 370 /**
michael@0 371 * If any children require a block parent, return the first such child.
michael@0 372 * Otherwise return null.
michael@0 373 */
michael@0 374 static nsIContent*
michael@0 375 AnyKidsNeedBlockParent(nsIFrame *aFrameList)
michael@0 376 {
michael@0 377 for (nsIFrame *k = aFrameList; k; k = k->GetNextSibling()) {
michael@0 378 // Line participants, such as text and inline frames, can't be
michael@0 379 // directly inside a XUL box; they must be wrapped in an
michael@0 380 // intermediate block.
michael@0 381 if (k->IsFrameOfType(nsIFrame::eLineParticipant)) {
michael@0 382 return k->GetContent();
michael@0 383 }
michael@0 384 }
michael@0 385 return nullptr;
michael@0 386 }
michael@0 387
michael@0 388 // Reparent a frame into a wrapper frame that is a child of its old parent.
michael@0 389 static void
michael@0 390 ReparentFrame(RestyleManager* aRestyleManager,
michael@0 391 nsIFrame* aNewParentFrame,
michael@0 392 nsIFrame* aFrame)
michael@0 393 {
michael@0 394 aFrame->SetParent(aNewParentFrame);
michael@0 395 aRestyleManager->ReparentStyleContext(aFrame);
michael@0 396 }
michael@0 397
michael@0 398 static void
michael@0 399 ReparentFrames(nsCSSFrameConstructor* aFrameConstructor,
michael@0 400 nsIFrame* aNewParentFrame,
michael@0 401 const nsFrameList& aFrameList)
michael@0 402 {
michael@0 403 RestyleManager* restyleManager = aFrameConstructor->RestyleManager();
michael@0 404 for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
michael@0 405 ReparentFrame(restyleManager, aNewParentFrame, e.get());
michael@0 406 }
michael@0 407 }
michael@0 408
michael@0 409 //----------------------------------------------------------------------
michael@0 410 //
michael@0 411 // When inline frames get weird and have block frames in them, we
michael@0 412 // annotate them to help us respond to incremental content changes
michael@0 413 // more easily.
michael@0 414
michael@0 415 static inline bool
michael@0 416 IsFramePartOfIBSplit(nsIFrame* aFrame)
michael@0 417 {
michael@0 418 return (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) != 0;
michael@0 419 }
michael@0 420
michael@0 421 static nsIFrame* GetIBSplitSibling(nsIFrame* aFrame)
michael@0 422 {
michael@0 423 NS_PRECONDITION(IsFramePartOfIBSplit(aFrame), "Shouldn't call this");
michael@0 424
michael@0 425 // We only store the "ib-split sibling" annotation with the first
michael@0 426 // frame in the continuation chain. Walk back to find that frame now.
michael@0 427 return static_cast<nsIFrame*>
michael@0 428 (aFrame->FirstContinuation()->
michael@0 429 Properties().Get(nsIFrame::IBSplitSibling()));
michael@0 430 }
michael@0 431
michael@0 432 static nsIFrame* GetIBSplitPrevSibling(nsIFrame* aFrame)
michael@0 433 {
michael@0 434 NS_PRECONDITION(IsFramePartOfIBSplit(aFrame), "Shouldn't call this");
michael@0 435
michael@0 436 // We only store the ib-split sibling annotation with the first
michael@0 437 // frame in the continuation chain. Walk back to find that frame now.
michael@0 438 return static_cast<nsIFrame*>
michael@0 439 (aFrame->FirstContinuation()->
michael@0 440 Properties().Get(nsIFrame::IBSplitPrevSibling()));
michael@0 441 }
michael@0 442
michael@0 443 static nsIFrame*
michael@0 444 GetLastIBSplitSibling(nsIFrame* aFrame, bool aReturnEmptyTrailingInline)
michael@0 445 {
michael@0 446 for (nsIFrame *frame = aFrame, *next; ; frame = next) {
michael@0 447 next = GetIBSplitSibling(frame);
michael@0 448 if (!next ||
michael@0 449 (!aReturnEmptyTrailingInline && !next->GetFirstPrincipalChild() &&
michael@0 450 !GetIBSplitSibling(next))) {
michael@0 451 NS_ASSERTION(!next || !frame->IsInlineOutside(),
michael@0 452 "Should have a block here!");
michael@0 453 return frame;
michael@0 454 }
michael@0 455 }
michael@0 456 NS_NOTREACHED("unreachable code");
michael@0 457 return nullptr;
michael@0 458 }
michael@0 459
michael@0 460 static void
michael@0 461 SetFrameIsIBSplit(nsIFrame* aFrame, nsIFrame* aIBSplitSibling)
michael@0 462 {
michael@0 463 NS_PRECONDITION(aFrame, "bad args!");
michael@0 464
michael@0 465 // We should be the only continuation
michael@0 466 NS_ASSERTION(!aFrame->GetPrevContinuation(),
michael@0 467 "assigning ib-split sibling to other than first continuation!");
michael@0 468 NS_ASSERTION(!aFrame->GetNextContinuation() ||
michael@0 469 IsFramePartOfIBSplit(aFrame->GetNextContinuation()),
michael@0 470 "should have no non-ib-split continuations here");
michael@0 471
michael@0 472 // Mark the frame as ib-split.
michael@0 473 aFrame->AddStateBits(NS_FRAME_PART_OF_IBSPLIT);
michael@0 474
michael@0 475 if (aIBSplitSibling) {
michael@0 476 NS_ASSERTION(!aIBSplitSibling->GetPrevContinuation(),
michael@0 477 "assigning something other than the first continuation as the "
michael@0 478 "ib-split sibling");
michael@0 479
michael@0 480 // Store the ib-split sibling (if we were given one) with the
michael@0 481 // first frame in the flow.
michael@0 482 FramePropertyTable* props = aFrame->PresContext()->PropertyTable();
michael@0 483 props->Set(aFrame, nsIFrame::IBSplitSibling(), aIBSplitSibling);
michael@0 484 props->Set(aIBSplitSibling, nsIFrame::IBSplitPrevSibling(), aFrame);
michael@0 485 }
michael@0 486 }
michael@0 487
michael@0 488 static nsIFrame*
michael@0 489 GetIBContainingBlockFor(nsIFrame* aFrame)
michael@0 490 {
michael@0 491 NS_PRECONDITION(IsFramePartOfIBSplit(aFrame),
michael@0 492 "GetIBContainingBlockFor() should only be called on known IB frames");
michael@0 493
michael@0 494 // Get the first "normal" ancestor of the target frame.
michael@0 495 nsIFrame* parentFrame;
michael@0 496 do {
michael@0 497 parentFrame = aFrame->GetParent();
michael@0 498
michael@0 499 if (! parentFrame) {
michael@0 500 NS_ERROR("no unsplit block frame in IB hierarchy");
michael@0 501 return aFrame;
michael@0 502 }
michael@0 503
michael@0 504 // Note that we ignore non-ib-split frames which have a pseudo on their
michael@0 505 // style context -- they're not the frames we're looking for! In
michael@0 506 // particular, they may be hiding a real parent that _is_ in an ib-split.
michael@0 507 if (!IsFramePartOfIBSplit(parentFrame) &&
michael@0 508 !parentFrame->StyleContext()->GetPseudo())
michael@0 509 break;
michael@0 510
michael@0 511 aFrame = parentFrame;
michael@0 512 } while (1);
michael@0 513
michael@0 514 // post-conditions
michael@0 515 NS_ASSERTION(parentFrame, "no normal ancestor found for ib-split frame "
michael@0 516 "in GetIBContainingBlockFor");
michael@0 517 NS_ASSERTION(parentFrame != aFrame, "parentFrame is actually the child frame - bogus reslt");
michael@0 518
michael@0 519 return parentFrame;
michael@0 520 }
michael@0 521
michael@0 522 //----------------------------------------------------------------------
michael@0 523
michael@0 524 // Block/inline frame construction logic. We maintain a few invariants here:
michael@0 525 //
michael@0 526 // 1. Block frames contain block and inline frames.
michael@0 527 //
michael@0 528 // 2. Inline frames only contain inline frames. If an inline parent has a block
michael@0 529 // child then the block child is migrated upward until it lands in a block
michael@0 530 // parent (the inline frames containing block is where it will end up).
michael@0 531
michael@0 532 // After this function returns, aLink is pointing to the first link at or
michael@0 533 // after its starting position for which the next frame is a block. If there
michael@0 534 // is no such link, it points to the end of the list.
michael@0 535 static void
michael@0 536 FindFirstBlock(nsFrameList::FrameLinkEnumerator& aLink)
michael@0 537 {
michael@0 538 for ( ; !aLink.AtEnd(); aLink.Next()) {
michael@0 539 if (!aLink.NextFrame()->IsInlineOutside()) {
michael@0 540 return;
michael@0 541 }
michael@0 542 }
michael@0 543 }
michael@0 544
michael@0 545 // This function returns a frame link enumerator pointing to the first link in
michael@0 546 // the list for which the next frame is not block. If there is no such link,
michael@0 547 // it points to the end of the list.
michael@0 548 static nsFrameList::FrameLinkEnumerator
michael@0 549 FindFirstNonBlock(const nsFrameList& aList)
michael@0 550 {
michael@0 551 nsFrameList::FrameLinkEnumerator link(aList);
michael@0 552 for (; !link.AtEnd(); link.Next()) {
michael@0 553 if (link.NextFrame()->IsInlineOutside()) {
michael@0 554 break;
michael@0 555 }
michael@0 556 }
michael@0 557 return link;
michael@0 558 }
michael@0 559
michael@0 560 inline void
michael@0 561 SetInitialSingleChild(nsIFrame* aParent, nsIFrame* aFrame)
michael@0 562 {
michael@0 563 NS_PRECONDITION(!aFrame->GetNextSibling(), "Should be using a frame list");
michael@0 564 nsFrameList temp(aFrame, aFrame);
michael@0 565 aParent->SetInitialChildList(kPrincipalList, temp);
michael@0 566 }
michael@0 567
michael@0 568 // -----------------------------------------------------------
michael@0 569
michael@0 570 // Structure used when constructing formatting object trees.
michael@0 571 struct nsFrameItems : public nsFrameList
michael@0 572 {
michael@0 573 // Appends the frame to the end of the list
michael@0 574 void AddChild(nsIFrame* aChild);
michael@0 575 };
michael@0 576
michael@0 577 void
michael@0 578 nsFrameItems::AddChild(nsIFrame* aChild)
michael@0 579 {
michael@0 580 NS_PRECONDITION(aChild, "nsFrameItems::AddChild");
michael@0 581
michael@0 582 // It'd be really nice if we could just AppendFrames(kPrincipalList, aChild) here,
michael@0 583 // but some of our callers put frames that have different
michael@0 584 // parents (caption, I'm looking at you) on the same framelist, and
michael@0 585 // nsFrameList asserts if you try to do that.
michael@0 586 if (IsEmpty()) {
michael@0 587 SetFrames(aChild);
michael@0 588 }
michael@0 589 else {
michael@0 590 NS_ASSERTION(aChild != mLastChild,
michael@0 591 "Same frame being added to frame list twice?");
michael@0 592 mLastChild->SetNextSibling(aChild);
michael@0 593 mLastChild = nsLayoutUtils::GetLastSibling(aChild);
michael@0 594 }
michael@0 595 }
michael@0 596
michael@0 597 // -----------------------------------------------------------
michael@0 598
michael@0 599 // Structure used when constructing formatting object trees. Contains
michael@0 600 // state information needed for absolutely positioned elements
michael@0 601 struct nsAbsoluteItems : nsFrameItems {
michael@0 602 // containing block for absolutely positioned elements
michael@0 603 nsIFrame* containingBlock;
michael@0 604
michael@0 605 nsAbsoluteItems(nsIFrame* aContainingBlock);
michael@0 606 #ifdef DEBUG
michael@0 607 // XXXbz Does this need a debug-only assignment operator that nulls out the
michael@0 608 // childList in the nsAbsoluteItems we're copying? Introducing a difference
michael@0 609 // between debug and non-debug behavior seems bad, so I guess not...
michael@0 610 ~nsAbsoluteItems() {
michael@0 611 NS_ASSERTION(!FirstChild(),
michael@0 612 "Dangling child list. Someone forgot to insert it?");
michael@0 613 }
michael@0 614 #endif
michael@0 615
michael@0 616 // Appends the frame to the end of the list
michael@0 617 void AddChild(nsIFrame* aChild);
michael@0 618 };
michael@0 619
michael@0 620 nsAbsoluteItems::nsAbsoluteItems(nsIFrame* aContainingBlock)
michael@0 621 : containingBlock(aContainingBlock)
michael@0 622 {
michael@0 623 }
michael@0 624
michael@0 625 // Additional behavior is that it sets the frame's NS_FRAME_OUT_OF_FLOW flag
michael@0 626 void
michael@0 627 nsAbsoluteItems::AddChild(nsIFrame* aChild)
michael@0 628 {
michael@0 629 NS_ASSERTION(aChild->PresContext()->FrameManager()->
michael@0 630 GetPlaceholderFrameFor(aChild),
michael@0 631 "Child without placeholder being added to nsAbsoluteItems?");
michael@0 632 aChild->AddStateBits(NS_FRAME_OUT_OF_FLOW);
michael@0 633 nsFrameItems::AddChild(aChild);
michael@0 634 }
michael@0 635
michael@0 636 // -----------------------------------------------------------
michael@0 637
michael@0 638 // Structure for saving the existing state when pushing/poping containing
michael@0 639 // blocks. The destructor restores the state to its previous state
michael@0 640 class MOZ_STACK_CLASS nsFrameConstructorSaveState {
michael@0 641 public:
michael@0 642 typedef nsIFrame::ChildListID ChildListID;
michael@0 643 nsFrameConstructorSaveState();
michael@0 644 ~nsFrameConstructorSaveState();
michael@0 645
michael@0 646 private:
michael@0 647 nsAbsoluteItems* mItems; // pointer to struct whose data we save/restore
michael@0 648 nsAbsoluteItems mSavedItems; // copy of original data
michael@0 649
michael@0 650 // The name of the child list in which our frames would belong
michael@0 651 ChildListID mChildListID;
michael@0 652 nsFrameConstructorState* mState;
michael@0 653
michael@0 654 // State used only when we're saving the abs-pos state for a transformed
michael@0 655 // element.
michael@0 656 nsAbsoluteItems mSavedFixedItems;
michael@0 657
michael@0 658 bool mSavedFixedPosIsAbsPos;
michael@0 659
michael@0 660 friend class nsFrameConstructorState;
michael@0 661 };
michael@0 662
michael@0 663 // Structure used to keep track of a list of bindings we need to call
michael@0 664 // AddToAttachedQueue on. These should be in post-order depth-first
michael@0 665 // flattened tree traversal order.
michael@0 666 struct PendingBinding : public LinkedListElement<PendingBinding>
michael@0 667 {
michael@0 668 #ifdef NS_BUILD_REFCNT_LOGGING
michael@0 669 PendingBinding() {
michael@0 670 MOZ_COUNT_CTOR(PendingBinding);
michael@0 671 }
michael@0 672 ~PendingBinding() {
michael@0 673 MOZ_COUNT_DTOR(PendingBinding);
michael@0 674 }
michael@0 675 #endif
michael@0 676
michael@0 677 nsRefPtr<nsXBLBinding> mBinding;
michael@0 678 };
michael@0 679
michael@0 680 // Structure used for maintaining state information during the
michael@0 681 // frame construction process
michael@0 682 class MOZ_STACK_CLASS nsFrameConstructorState {
michael@0 683 public:
michael@0 684 typedef nsIFrame::ChildListID ChildListID;
michael@0 685
michael@0 686 nsPresContext *mPresContext;
michael@0 687 nsIPresShell *mPresShell;
michael@0 688 nsFrameManager *mFrameManager;
michael@0 689
michael@0 690 #ifdef MOZ_XUL
michael@0 691 // Frames destined for the kPopupList.
michael@0 692 nsAbsoluteItems mPopupItems;
michael@0 693 #endif
michael@0 694
michael@0 695 // Containing block information for out-of-flow frames.
michael@0 696 nsAbsoluteItems mFixedItems;
michael@0 697 nsAbsoluteItems mAbsoluteItems;
michael@0 698 nsAbsoluteItems mFloatedItems;
michael@0 699
michael@0 700 nsCOMPtr<nsILayoutHistoryState> mFrameState;
michael@0 701 // These bits will be added to the state bits of any frame we construct
michael@0 702 // using this state.
michael@0 703 nsFrameState mAdditionalStateBits;
michael@0 704
michael@0 705 // When working with the -moz-transform property, we want to hook
michael@0 706 // the abs-pos and fixed-pos lists together, since transformed
michael@0 707 // elements are fixed-pos containing blocks. This flag determines
michael@0 708 // whether or not we want to wire the fixed-pos and abs-pos lists
michael@0 709 // together.
michael@0 710 bool mFixedPosIsAbsPos;
michael@0 711
michael@0 712 // A boolean to indicate whether we have a "pending" popupgroup. That is, we
michael@0 713 // have already created the FrameConstructionItem for the root popupgroup but
michael@0 714 // we have not yet created the relevant frame.
michael@0 715 bool mHavePendingPopupgroup;
michael@0 716
michael@0 717 // If false (which is the default) then call SetPrimaryFrame() as needed
michael@0 718 // during frame construction. If true, don't make any SetPrimaryFrame()
michael@0 719 // calls, except for generated content which doesn't have a primary frame
michael@0 720 // yet. The mCreatingExtraFrames == true mode is meant to be used for
michael@0 721 // construction of random "extra" frames for elements via normal frame
michael@0 722 // construction APIs (e.g. replication of things across pages in paginated
michael@0 723 // mode).
michael@0 724 bool mCreatingExtraFrames;
michael@0 725
michael@0 726 nsCOMArray<nsIContent> mGeneratedTextNodesWithInitializer;
michael@0 727
michael@0 728 TreeMatchContext mTreeMatchContext;
michael@0 729
michael@0 730 // Constructor
michael@0 731 // Use the passed-in history state.
michael@0 732 nsFrameConstructorState(nsIPresShell* aPresShell,
michael@0 733 nsIFrame* aFixedContainingBlock,
michael@0 734 nsIFrame* aAbsoluteContainingBlock,
michael@0 735 nsIFrame* aFloatContainingBlock,
michael@0 736 nsILayoutHistoryState* aHistoryState);
michael@0 737 // Get the history state from the pres context's pres shell.
michael@0 738 nsFrameConstructorState(nsIPresShell* aPresShell,
michael@0 739 nsIFrame* aFixedContainingBlock,
michael@0 740 nsIFrame* aAbsoluteContainingBlock,
michael@0 741 nsIFrame* aFloatContainingBlock);
michael@0 742
michael@0 743 ~nsFrameConstructorState();
michael@0 744
michael@0 745 // Function to push the existing absolute containing block state and
michael@0 746 // create a new scope. Code that uses this function should get matching
michael@0 747 // logic in GetAbsoluteContainingBlock.
michael@0 748 // Also makes aNewAbsoluteContainingBlock the containing block for
michael@0 749 // fixed-pos elements if necessary.
michael@0 750 // aPositionedFrame is the frame whose style actually makes
michael@0 751 // aNewAbsoluteContainingBlock a containing block. E.g. for a scrollable element
michael@0 752 // aPositionedFrame is the element's primary frame and
michael@0 753 // aNewAbsoluteContainingBlock is the scrolled frame.
michael@0 754 void PushAbsoluteContainingBlock(nsIFrame* aNewAbsoluteContainingBlock,
michael@0 755 nsIFrame* aPositionedFrame,
michael@0 756 nsFrameConstructorSaveState& aSaveState);
michael@0 757
michael@0 758 // Function to push the existing float containing block state and
michael@0 759 // create a new scope. Code that uses this function should get matching
michael@0 760 // logic in GetFloatContainingBlock.
michael@0 761 // Pushing a null float containing block forbids any frames from being
michael@0 762 // floated until a new float containing block is pushed.
michael@0 763 // XXX we should get rid of null float containing blocks and teach the
michael@0 764 // various frame classes to deal with floats instead.
michael@0 765 void PushFloatContainingBlock(nsIFrame* aNewFloatContainingBlock,
michael@0 766 nsFrameConstructorSaveState& aSaveState);
michael@0 767
michael@0 768 // Function to return the proper geometric parent for a frame with display
michael@0 769 // struct given by aStyleDisplay and parent's frame given by
michael@0 770 // aContentParentFrame.
michael@0 771 nsIFrame* GetGeometricParent(const nsStyleDisplay* aStyleDisplay,
michael@0 772 nsIFrame* aContentParentFrame) const;
michael@0 773
michael@0 774 /**
michael@0 775 * Function to add a new frame to the right frame list. This MUST be called
michael@0 776 * on frames before their children have been processed if the frames might
michael@0 777 * conceivably be out-of-flow; otherwise cleanup in error cases won't work
michael@0 778 * right. Also, this MUST be called on frames after they have been
michael@0 779 * initialized.
michael@0 780 * @param aNewFrame the frame to add
michael@0 781 * @param aFrameItems the list to add in-flow frames to
michael@0 782 * @param aContent the content pointer for aNewFrame
michael@0 783 * @param aStyleContext the style context resolved for aContent
michael@0 784 * @param aParentFrame the parent frame for the content if it were in-flow
michael@0 785 * @param aCanBePositioned pass false if the frame isn't allowed to be
michael@0 786 * positioned
michael@0 787 * @param aCanBeFloated pass false if the frame isn't allowed to be
michael@0 788 * floated
michael@0 789 * @param aIsOutOfFlowPopup pass true if the frame is an out-of-flow popup
michael@0 790 * (XUL-only)
michael@0 791 */
michael@0 792 void AddChild(nsIFrame* aNewFrame,
michael@0 793 nsFrameItems& aFrameItems,
michael@0 794 nsIContent* aContent,
michael@0 795 nsStyleContext* aStyleContext,
michael@0 796 nsIFrame* aParentFrame,
michael@0 797 bool aCanBePositioned = true,
michael@0 798 bool aCanBeFloated = true,
michael@0 799 bool aIsOutOfFlowPopup = false,
michael@0 800 bool aInsertAfter = false,
michael@0 801 nsIFrame* aInsertAfterFrame = nullptr);
michael@0 802
michael@0 803 /**
michael@0 804 * Function to return the fixed-pos element list. Normally this will just hand back the
michael@0 805 * fixed-pos element list, but in case we're dealing with a transformed element that's
michael@0 806 * acting as an abs-pos and fixed-pos container, we'll hand back the abs-pos list. Callers should
michael@0 807 * use this function if they want to get the list acting as the fixed-pos item parent.
michael@0 808 */
michael@0 809 nsAbsoluteItems& GetFixedItems()
michael@0 810 {
michael@0 811 return mFixedPosIsAbsPos ? mAbsoluteItems : mFixedItems;
michael@0 812 }
michael@0 813 const nsAbsoluteItems& GetFixedItems() const
michael@0 814 {
michael@0 815 return mFixedPosIsAbsPos ? mAbsoluteItems : mFixedItems;
michael@0 816 }
michael@0 817
michael@0 818
michael@0 819 /**
michael@0 820 * class to automatically push and pop a pending binding in the frame
michael@0 821 * constructor state. See nsCSSFrameConstructor::FrameConstructionItem
michael@0 822 * mPendingBinding documentation.
michael@0 823 */
michael@0 824 class PendingBindingAutoPusher;
michael@0 825 friend class PendingBindingAutoPusher;
michael@0 826 class MOZ_STACK_CLASS PendingBindingAutoPusher {
michael@0 827 public:
michael@0 828 PendingBindingAutoPusher(nsFrameConstructorState& aState,
michael@0 829 PendingBinding* aPendingBinding) :
michael@0 830 mState(aState),
michael@0 831 mPendingBinding(aState.mCurrentPendingBindingInsertionPoint)
michael@0 832 {
michael@0 833 if (aPendingBinding) {
michael@0 834 aState.mCurrentPendingBindingInsertionPoint = aPendingBinding;
michael@0 835 }
michael@0 836 }
michael@0 837
michael@0 838 ~PendingBindingAutoPusher()
michael@0 839 {
michael@0 840 mState.mCurrentPendingBindingInsertionPoint = mPendingBinding;
michael@0 841 }
michael@0 842
michael@0 843 private:
michael@0 844 nsFrameConstructorState& mState;
michael@0 845 PendingBinding* mPendingBinding;
michael@0 846 };
michael@0 847
michael@0 848 /**
michael@0 849 * Add a new pending binding to the list
michael@0 850 */
michael@0 851 void AddPendingBinding(PendingBinding* aPendingBinding) {
michael@0 852 if (mCurrentPendingBindingInsertionPoint) {
michael@0 853 mCurrentPendingBindingInsertionPoint->setPrevious(aPendingBinding);
michael@0 854 } else {
michael@0 855 mPendingBindings.insertBack(aPendingBinding);
michael@0 856 }
michael@0 857 }
michael@0 858
michael@0 859 protected:
michael@0 860 friend class nsFrameConstructorSaveState;
michael@0 861
michael@0 862 /**
michael@0 863 * ProcessFrameInsertions takes the frames in aFrameItems and adds them as
michael@0 864 * kids to the aChildListID child list of |aFrameItems.containingBlock|.
michael@0 865 */
michael@0 866 void ProcessFrameInsertions(nsAbsoluteItems& aFrameItems,
michael@0 867 ChildListID aChildListID);
michael@0 868
michael@0 869 // Our list of all pending bindings. When we're done, we need to call
michael@0 870 // AddToAttachedQueue on all of them, in order.
michael@0 871 LinkedList<PendingBinding> mPendingBindings;
michael@0 872
michael@0 873 PendingBinding* mCurrentPendingBindingInsertionPoint;
michael@0 874 };
michael@0 875
michael@0 876 nsFrameConstructorState::nsFrameConstructorState(nsIPresShell* aPresShell,
michael@0 877 nsIFrame* aFixedContainingBlock,
michael@0 878 nsIFrame* aAbsoluteContainingBlock,
michael@0 879 nsIFrame* aFloatContainingBlock,
michael@0 880 nsILayoutHistoryState* aHistoryState)
michael@0 881 : mPresContext(aPresShell->GetPresContext()),
michael@0 882 mPresShell(aPresShell),
michael@0 883 mFrameManager(aPresShell->FrameManager()),
michael@0 884 #ifdef MOZ_XUL
michael@0 885 mPopupItems(nullptr),
michael@0 886 #endif
michael@0 887 mFixedItems(aFixedContainingBlock),
michael@0 888 mAbsoluteItems(aAbsoluteContainingBlock),
michael@0 889 mFloatedItems(aFloatContainingBlock),
michael@0 890 // See PushAbsoluteContaningBlock below
michael@0 891 mFrameState(aHistoryState),
michael@0 892 mAdditionalStateBits(nsFrameState(0)),
michael@0 893 // If the fixed-pos containing block is equal to the abs-pos containing
michael@0 894 // block, use the abs-pos containing block's abs-pos list for fixed-pos
michael@0 895 // frames.
michael@0 896 mFixedPosIsAbsPos(aFixedContainingBlock == aAbsoluteContainingBlock),
michael@0 897 mHavePendingPopupgroup(false),
michael@0 898 mCreatingExtraFrames(false),
michael@0 899 mTreeMatchContext(true, nsRuleWalker::eRelevantLinkUnvisited,
michael@0 900 aPresShell->GetDocument()),
michael@0 901 mCurrentPendingBindingInsertionPoint(nullptr)
michael@0 902 {
michael@0 903 #ifdef MOZ_XUL
michael@0 904 nsIRootBox* rootBox = nsIRootBox::GetRootBox(aPresShell);
michael@0 905 if (rootBox) {
michael@0 906 mPopupItems.containingBlock = rootBox->GetPopupSetFrame();
michael@0 907 }
michael@0 908 #endif
michael@0 909 MOZ_COUNT_CTOR(nsFrameConstructorState);
michael@0 910 }
michael@0 911
michael@0 912 nsFrameConstructorState::nsFrameConstructorState(nsIPresShell* aPresShell,
michael@0 913 nsIFrame* aFixedContainingBlock,
michael@0 914 nsIFrame* aAbsoluteContainingBlock,
michael@0 915 nsIFrame* aFloatContainingBlock)
michael@0 916 : mPresContext(aPresShell->GetPresContext()),
michael@0 917 mPresShell(aPresShell),
michael@0 918 mFrameManager(aPresShell->FrameManager()),
michael@0 919 #ifdef MOZ_XUL
michael@0 920 mPopupItems(nullptr),
michael@0 921 #endif
michael@0 922 mFixedItems(aFixedContainingBlock),
michael@0 923 mAbsoluteItems(aAbsoluteContainingBlock),
michael@0 924 mFloatedItems(aFloatContainingBlock),
michael@0 925 // See PushAbsoluteContaningBlock below
michael@0 926 mAdditionalStateBits(nsFrameState(0)),
michael@0 927 // If the fixed-pos containing block is equal to the abs-pos containing
michael@0 928 // block, use the abs-pos containing block's abs-pos list for fixed-pos
michael@0 929 // frames.
michael@0 930 mFixedPosIsAbsPos(aFixedContainingBlock == aAbsoluteContainingBlock),
michael@0 931 mHavePendingPopupgroup(false),
michael@0 932 mCreatingExtraFrames(false),
michael@0 933 mTreeMatchContext(true, nsRuleWalker::eRelevantLinkUnvisited,
michael@0 934 aPresShell->GetDocument()),
michael@0 935 mCurrentPendingBindingInsertionPoint(nullptr)
michael@0 936 {
michael@0 937 #ifdef MOZ_XUL
michael@0 938 nsIRootBox* rootBox = nsIRootBox::GetRootBox(aPresShell);
michael@0 939 if (rootBox) {
michael@0 940 mPopupItems.containingBlock = rootBox->GetPopupSetFrame();
michael@0 941 }
michael@0 942 #endif
michael@0 943 MOZ_COUNT_CTOR(nsFrameConstructorState);
michael@0 944 mFrameState = aPresShell->GetDocument()->GetLayoutHistoryState();
michael@0 945 }
michael@0 946
michael@0 947 nsFrameConstructorState::~nsFrameConstructorState()
michael@0 948 {
michael@0 949 // Frame order comparison functions only work properly when the placeholders
michael@0 950 // have been inserted into the frame tree. So for example if we have a new float
michael@0 951 // containing the placeholder for a new abs-pos frame, and we process the abs-pos
michael@0 952 // insertion first, then we won't be able to find the right place to insert in
michael@0 953 // in the abs-pos list. So put floats in first, because they can contain placeholders
michael@0 954 // for abs-pos and fixed-pos items whose containing blocks are outside the floats.
michael@0 955 // Then put abs-pos frames in, because they can contain placeholders for fixed-pos
michael@0 956 // items whose containing block is outside the abs-pos frames.
michael@0 957 MOZ_COUNT_DTOR(nsFrameConstructorState);
michael@0 958 ProcessFrameInsertions(mFloatedItems, nsIFrame::kFloatList);
michael@0 959 ProcessFrameInsertions(mAbsoluteItems, nsIFrame::kAbsoluteList);
michael@0 960 ProcessFrameInsertions(mFixedItems, nsIFrame::kFixedList);
michael@0 961 #ifdef MOZ_XUL
michael@0 962 ProcessFrameInsertions(mPopupItems, nsIFrame::kPopupList);
michael@0 963 #endif
michael@0 964 for (int32_t i = mGeneratedTextNodesWithInitializer.Count() - 1; i >= 0; --i) {
michael@0 965 mGeneratedTextNodesWithInitializer[i]->
michael@0 966 DeleteProperty(nsGkAtoms::genConInitializerProperty);
michael@0 967 }
michael@0 968 if (!mPendingBindings.isEmpty()) {
michael@0 969 nsBindingManager* bindingManager = mPresShell->GetDocument()->BindingManager();
michael@0 970 do {
michael@0 971 nsAutoPtr<PendingBinding> pendingBinding;
michael@0 972 pendingBinding = mPendingBindings.popFirst();
michael@0 973 bindingManager->AddToAttachedQueue(pendingBinding->mBinding);
michael@0 974 } while (!mPendingBindings.isEmpty());
michael@0 975 mCurrentPendingBindingInsertionPoint = nullptr;
michael@0 976 }
michael@0 977 }
michael@0 978
michael@0 979 static nsIFrame*
michael@0 980 AdjustAbsoluteContainingBlock(nsIFrame* aContainingBlockIn)
michael@0 981 {
michael@0 982 if (!aContainingBlockIn) {
michael@0 983 return nullptr;
michael@0 984 }
michael@0 985
michael@0 986 // Always use the container's first continuation. (Inline frames can have
michael@0 987 // non-fluid bidi continuations...)
michael@0 988 return aContainingBlockIn->FirstContinuation();
michael@0 989 }
michael@0 990
michael@0 991 void
michael@0 992 nsFrameConstructorState::PushAbsoluteContainingBlock(nsIFrame* aNewAbsoluteContainingBlock,
michael@0 993 nsIFrame* aPositionedFrame,
michael@0 994 nsFrameConstructorSaveState& aSaveState)
michael@0 995 {
michael@0 996 aSaveState.mItems = &mAbsoluteItems;
michael@0 997 aSaveState.mSavedItems = mAbsoluteItems;
michael@0 998 aSaveState.mChildListID = nsIFrame::kAbsoluteList;
michael@0 999 aSaveState.mState = this;
michael@0 1000 aSaveState.mSavedFixedPosIsAbsPos = mFixedPosIsAbsPos;
michael@0 1001
michael@0 1002 if (mFixedPosIsAbsPos) {
michael@0 1003 // Since we're going to replace mAbsoluteItems, we need to save it into
michael@0 1004 // mFixedItems now (and save the current value of mFixedItems).
michael@0 1005 aSaveState.mSavedFixedItems = mFixedItems;
michael@0 1006 mFixedItems = mAbsoluteItems;
michael@0 1007 }
michael@0 1008
michael@0 1009 mAbsoluteItems =
michael@0 1010 nsAbsoluteItems(AdjustAbsoluteContainingBlock(aNewAbsoluteContainingBlock));
michael@0 1011
michael@0 1012 /* See if we're wiring the fixed-pos and abs-pos lists together. This happens iff
michael@0 1013 * we're a transformed element.
michael@0 1014 */
michael@0 1015 mFixedPosIsAbsPos = aPositionedFrame &&
michael@0 1016 (aPositionedFrame->StyleDisplay()->HasTransform(aPositionedFrame) ||
michael@0 1017 aPositionedFrame->StyleDisplay()->HasPerspectiveStyle());
michael@0 1018
michael@0 1019 if (aNewAbsoluteContainingBlock) {
michael@0 1020 aNewAbsoluteContainingBlock->MarkAsAbsoluteContainingBlock();
michael@0 1021 }
michael@0 1022 }
michael@0 1023
michael@0 1024 void
michael@0 1025 nsFrameConstructorState::PushFloatContainingBlock(nsIFrame* aNewFloatContainingBlock,
michael@0 1026 nsFrameConstructorSaveState& aSaveState)
michael@0 1027 {
michael@0 1028 NS_PRECONDITION(!aNewFloatContainingBlock ||
michael@0 1029 aNewFloatContainingBlock->IsFloatContainingBlock(),
michael@0 1030 "Please push a real float containing block!");
michael@0 1031 NS_ASSERTION(!aNewFloatContainingBlock ||
michael@0 1032 !ShouldSuppressFloatingOfDescendants(aNewFloatContainingBlock),
michael@0 1033 "We should not push a frame that is supposed to _suppress_ "
michael@0 1034 "floats as a float containing block!");
michael@0 1035 aSaveState.mItems = &mFloatedItems;
michael@0 1036 aSaveState.mSavedItems = mFloatedItems;
michael@0 1037 aSaveState.mChildListID = nsIFrame::kFloatList;
michael@0 1038 aSaveState.mState = this;
michael@0 1039 mFloatedItems = nsAbsoluteItems(aNewFloatContainingBlock);
michael@0 1040 }
michael@0 1041
michael@0 1042 nsIFrame*
michael@0 1043 nsFrameConstructorState::GetGeometricParent(const nsStyleDisplay* aStyleDisplay,
michael@0 1044 nsIFrame* aContentParentFrame) const
michael@0 1045 {
michael@0 1046 NS_PRECONDITION(aStyleDisplay, "Must have display struct!");
michael@0 1047
michael@0 1048 // If there is no container for a fixed, absolute, or floating root
michael@0 1049 // frame, we will ignore the positioning. This hack is originally
michael@0 1050 // brought to you by the letter T: tables, since other roots don't
michael@0 1051 // even call into this code. See bug 178855.
michael@0 1052 //
michael@0 1053 // XXX Disabling positioning in this case is a hack. If one was so inclined,
michael@0 1054 // one could support this either by (1) inserting a dummy block between the
michael@0 1055 // table and the canvas or (2) teaching the canvas how to reflow positioned
michael@0 1056 // elements. (1) has the usual problems when multiple frames share the same
michael@0 1057 // content (notice all the special cases in this file dealing with inner
michael@0 1058 // tables and outer tables which share the same content). (2) requires some
michael@0 1059 // work and possible factoring.
michael@0 1060 //
michael@0 1061 // XXXbz couldn't we just force position to "static" on roots and
michael@0 1062 // float to "none"? That's OK per CSS 2.1, as far as I can tell.
michael@0 1063
michael@0 1064 if (aContentParentFrame && aContentParentFrame->IsSVGText()) {
michael@0 1065 return aContentParentFrame;
michael@0 1066 }
michael@0 1067
michael@0 1068 if (aStyleDisplay->IsFloatingStyle() && mFloatedItems.containingBlock) {
michael@0 1069 NS_ASSERTION(!aStyleDisplay->IsAbsolutelyPositionedStyle(),
michael@0 1070 "Absolutely positioned _and_ floating?");
michael@0 1071 return mFloatedItems.containingBlock;
michael@0 1072 }
michael@0 1073
michael@0 1074 if (aStyleDisplay->mPosition == NS_STYLE_POSITION_ABSOLUTE &&
michael@0 1075 mAbsoluteItems.containingBlock) {
michael@0 1076 return mAbsoluteItems.containingBlock;
michael@0 1077 }
michael@0 1078
michael@0 1079 if (aStyleDisplay->mPosition == NS_STYLE_POSITION_FIXED &&
michael@0 1080 GetFixedItems().containingBlock) {
michael@0 1081 return GetFixedItems().containingBlock;
michael@0 1082 }
michael@0 1083
michael@0 1084 return aContentParentFrame;
michael@0 1085 }
michael@0 1086
michael@0 1087 void
michael@0 1088 nsFrameConstructorState::AddChild(nsIFrame* aNewFrame,
michael@0 1089 nsFrameItems& aFrameItems,
michael@0 1090 nsIContent* aContent,
michael@0 1091 nsStyleContext* aStyleContext,
michael@0 1092 nsIFrame* aParentFrame,
michael@0 1093 bool aCanBePositioned,
michael@0 1094 bool aCanBeFloated,
michael@0 1095 bool aIsOutOfFlowPopup,
michael@0 1096 bool aInsertAfter,
michael@0 1097 nsIFrame* aInsertAfterFrame)
michael@0 1098 {
michael@0 1099 NS_PRECONDITION(!aNewFrame->GetNextSibling(), "Shouldn't happen");
michael@0 1100
michael@0 1101 const nsStyleDisplay* disp = aNewFrame->StyleDisplay();
michael@0 1102
michael@0 1103 // The comments in GetGeometricParent regarding root table frames
michael@0 1104 // all apply here, unfortunately.
michael@0 1105
michael@0 1106 bool needPlaceholder = false;
michael@0 1107 nsFrameState placeholderType;
michael@0 1108 nsFrameItems* frameItems = &aFrameItems;
michael@0 1109 #ifdef MOZ_XUL
michael@0 1110 if (MOZ_UNLIKELY(aIsOutOfFlowPopup)) {
michael@0 1111 NS_ASSERTION(aNewFrame->GetParent() == mPopupItems.containingBlock,
michael@0 1112 "Popup whose parent is not the popup containing block?");
michael@0 1113 NS_ASSERTION(mPopupItems.containingBlock, "Must have a popup set frame!");
michael@0 1114 needPlaceholder = true;
michael@0 1115 frameItems = &mPopupItems;
michael@0 1116 placeholderType = PLACEHOLDER_FOR_POPUP;
michael@0 1117 }
michael@0 1118 else
michael@0 1119 #endif // MOZ_XUL
michael@0 1120 if (aCanBeFloated && aNewFrame->IsFloating() &&
michael@0 1121 mFloatedItems.containingBlock) {
michael@0 1122 NS_ASSERTION(aNewFrame->GetParent() == mFloatedItems.containingBlock,
michael@0 1123 "Float whose parent is not the float containing block?");
michael@0 1124 needPlaceholder = true;
michael@0 1125 frameItems = &mFloatedItems;
michael@0 1126 placeholderType = PLACEHOLDER_FOR_FLOAT;
michael@0 1127 }
michael@0 1128 else if (aCanBePositioned) {
michael@0 1129 if (disp->mPosition == NS_STYLE_POSITION_ABSOLUTE &&
michael@0 1130 mAbsoluteItems.containingBlock) {
michael@0 1131 NS_ASSERTION(aNewFrame->GetParent() == mAbsoluteItems.containingBlock,
michael@0 1132 "Abs pos whose parent is not the abs pos containing block?");
michael@0 1133 needPlaceholder = true;
michael@0 1134 frameItems = &mAbsoluteItems;
michael@0 1135 placeholderType = PLACEHOLDER_FOR_ABSPOS;
michael@0 1136 }
michael@0 1137 if (disp->mPosition == NS_STYLE_POSITION_FIXED &&
michael@0 1138 GetFixedItems().containingBlock) {
michael@0 1139 NS_ASSERTION(aNewFrame->GetParent() == GetFixedItems().containingBlock,
michael@0 1140 "Fixed pos whose parent is not the fixed pos containing block?");
michael@0 1141 needPlaceholder = true;
michael@0 1142 frameItems = &GetFixedItems();
michael@0 1143 placeholderType = PLACEHOLDER_FOR_FIXEDPOS;
michael@0 1144 }
michael@0 1145 }
michael@0 1146
michael@0 1147 if (needPlaceholder) {
michael@0 1148 NS_ASSERTION(frameItems != &aFrameItems,
michael@0 1149 "Putting frame in-flow _and_ want a placeholder?");
michael@0 1150 nsIFrame* placeholderFrame =
michael@0 1151 nsCSSFrameConstructor::CreatePlaceholderFrameFor(mPresShell,
michael@0 1152 aContent,
michael@0 1153 aNewFrame,
michael@0 1154 aStyleContext,
michael@0 1155 aParentFrame,
michael@0 1156 nullptr,
michael@0 1157 placeholderType);
michael@0 1158
michael@0 1159 placeholderFrame->AddStateBits(mAdditionalStateBits);
michael@0 1160 // Add the placeholder frame to the flow
michael@0 1161 aFrameItems.AddChild(placeholderFrame);
michael@0 1162 }
michael@0 1163 #ifdef DEBUG
michael@0 1164 else {
michael@0 1165 NS_ASSERTION(aNewFrame->GetParent() == aParentFrame,
michael@0 1166 "In-flow frame has wrong parent");
michael@0 1167 }
michael@0 1168 #endif
michael@0 1169
michael@0 1170 if (aInsertAfter) {
michael@0 1171 frameItems->InsertFrame(nullptr, aInsertAfterFrame, aNewFrame);
michael@0 1172 } else {
michael@0 1173 frameItems->AddChild(aNewFrame);
michael@0 1174 }
michael@0 1175 }
michael@0 1176
michael@0 1177 void
michael@0 1178 nsFrameConstructorState::ProcessFrameInsertions(nsAbsoluteItems& aFrameItems,
michael@0 1179 ChildListID aChildListID)
michael@0 1180 {
michael@0 1181 #define NS_NONXUL_LIST_TEST (&aFrameItems == &mFloatedItems && \
michael@0 1182 aChildListID == nsIFrame::kFloatList) || \
michael@0 1183 (&aFrameItems == &mAbsoluteItems && \
michael@0 1184 aChildListID == nsIFrame::kAbsoluteList) || \
michael@0 1185 (&aFrameItems == &mFixedItems && \
michael@0 1186 aChildListID == nsIFrame::kFixedList)
michael@0 1187 #ifdef MOZ_XUL
michael@0 1188 NS_PRECONDITION(NS_NONXUL_LIST_TEST ||
michael@0 1189 (&aFrameItems == &mPopupItems &&
michael@0 1190 aChildListID == nsIFrame::kPopupList),
michael@0 1191 "Unexpected aFrameItems/aChildListID combination");
michael@0 1192 #else
michael@0 1193 NS_PRECONDITION(NS_NONXUL_LIST_TEST,
michael@0 1194 "Unexpected aFrameItems/aChildListID combination");
michael@0 1195 #endif
michael@0 1196
michael@0 1197 if (aFrameItems.IsEmpty()) {
michael@0 1198 return;
michael@0 1199 }
michael@0 1200
michael@0 1201 nsIFrame* containingBlock = aFrameItems.containingBlock;
michael@0 1202
michael@0 1203 NS_ASSERTION(containingBlock,
michael@0 1204 "Child list without containing block?");
michael@0 1205
michael@0 1206 if (aChildListID == nsIFrame::kFixedList) {
michael@0 1207 // Put this frame on the transformed-frame's abs-pos list instead, if
michael@0 1208 // it has abs-pos children instead of fixed-pos children.
michael@0 1209 aChildListID = containingBlock->GetAbsoluteListID();
michael@0 1210 }
michael@0 1211
michael@0 1212 // Insert the frames hanging out in aItems. We can use SetInitialChildList()
michael@0 1213 // if the containing block hasn't been reflowed yet (so NS_FRAME_FIRST_REFLOW
michael@0 1214 // is set) and doesn't have any frames in the aChildListID child list yet.
michael@0 1215 const nsFrameList& childList = containingBlock->GetChildList(aChildListID);
michael@0 1216 DebugOnly<nsresult> rv = NS_OK;
michael@0 1217 if (childList.IsEmpty() &&
michael@0 1218 (containingBlock->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
michael@0 1219 // If we're injecting absolutely positioned frames, inject them on the
michael@0 1220 // absolute containing block
michael@0 1221 if (aChildListID == containingBlock->GetAbsoluteListID()) {
michael@0 1222 rv = containingBlock->GetAbsoluteContainingBlock()->
michael@0 1223 SetInitialChildList(containingBlock, aChildListID, aFrameItems);
michael@0 1224 } else {
michael@0 1225 rv = containingBlock->SetInitialChildList(aChildListID, aFrameItems);
michael@0 1226 }
michael@0 1227 } else {
michael@0 1228 // Note that whether the frame construction context is doing an append or
michael@0 1229 // not is not helpful here, since it could be appending to some frame in
michael@0 1230 // the middle of the document, which means we're not necessarily
michael@0 1231 // appending to the children of the containing block.
michael@0 1232 //
michael@0 1233 // We need to make sure the 'append to the end of document' case is fast.
michael@0 1234 // So first test the last child of the containing block
michael@0 1235 nsIFrame* lastChild = childList.LastChild();
michael@0 1236
michael@0 1237 // CompareTreePosition uses placeholder hierarchy for out of flow frames,
michael@0 1238 // so this will make out-of-flows respect the ordering of placeholders,
michael@0 1239 // which is great because it takes care of anonymous content.
michael@0 1240 nsIFrame* firstNewFrame = aFrameItems.FirstChild();
michael@0 1241
michael@0 1242 // Cache the ancestor chain so that we can reuse it if needed.
michael@0 1243 nsAutoTArray<nsIFrame*, 20> firstNewFrameAncestors;
michael@0 1244 nsIFrame* notCommonAncestor = nullptr;
michael@0 1245 if (lastChild) {
michael@0 1246 notCommonAncestor = nsLayoutUtils::FillAncestors(firstNewFrame,
michael@0 1247 containingBlock,
michael@0 1248 &firstNewFrameAncestors);
michael@0 1249 }
michael@0 1250
michael@0 1251 if (!lastChild ||
michael@0 1252 nsLayoutUtils::CompareTreePosition(lastChild, firstNewFrame,
michael@0 1253 firstNewFrameAncestors,
michael@0 1254 notCommonAncestor ?
michael@0 1255 containingBlock : nullptr) < 0) {
michael@0 1256 // no lastChild, or lastChild comes before the new children, so just append
michael@0 1257 rv = mFrameManager->AppendFrames(containingBlock, aChildListID, aFrameItems);
michael@0 1258 } else {
michael@0 1259 // Try the other children. First collect them to an array so that a
michael@0 1260 // reasonable fast binary search can be used to find the insertion point.
michael@0 1261 nsAutoTArray<nsIFrame*, 128> children;
michael@0 1262 for (nsIFrame* f = childList.FirstChild(); f != lastChild;
michael@0 1263 f = f->GetNextSibling()) {
michael@0 1264 children.AppendElement(f);
michael@0 1265 }
michael@0 1266
michael@0 1267 nsIFrame* insertionPoint = nullptr;
michael@0 1268 int32_t imin = 0;
michael@0 1269 int32_t max = children.Length();
michael@0 1270 while (max > imin) {
michael@0 1271 int32_t imid = imin + ((max - imin) / 2);
michael@0 1272 nsIFrame* f = children[imid];
michael@0 1273 int32_t compare =
michael@0 1274 nsLayoutUtils::CompareTreePosition(f, firstNewFrame, firstNewFrameAncestors,
michael@0 1275 notCommonAncestor ? containingBlock : nullptr);
michael@0 1276 if (compare > 0) {
michael@0 1277 // f is after the new frame.
michael@0 1278 max = imid;
michael@0 1279 insertionPoint = imid > 0 ? children[imid - 1] : nullptr;
michael@0 1280 } else if (compare < 0) {
michael@0 1281 // f is before the new frame.
michael@0 1282 imin = imid + 1;
michael@0 1283 insertionPoint = f;
michael@0 1284 } else {
michael@0 1285 // This is for the old behavior. Should be removed once it is
michael@0 1286 // guaranteed that CompareTreePosition can't return 0!
michael@0 1287 // See bug 928645.
michael@0 1288 NS_WARNING("Something odd happening???");
michael@0 1289 insertionPoint = nullptr;
michael@0 1290 for (uint32_t i = 0; i < children.Length(); ++i) {
michael@0 1291 nsIFrame* f = children[i];
michael@0 1292 if (nsLayoutUtils::CompareTreePosition(f, firstNewFrame,
michael@0 1293 firstNewFrameAncestors,
michael@0 1294 notCommonAncestor ?
michael@0 1295 containingBlock : nullptr) > 0) {
michael@0 1296 break;
michael@0 1297 }
michael@0 1298 insertionPoint = f;
michael@0 1299 }
michael@0 1300 break;
michael@0 1301 }
michael@0 1302 }
michael@0 1303 rv = mFrameManager->InsertFrames(containingBlock, aChildListID,
michael@0 1304 insertionPoint, aFrameItems);
michael@0 1305 }
michael@0 1306 }
michael@0 1307
michael@0 1308 NS_POSTCONDITION(aFrameItems.IsEmpty(), "How did that happen?");
michael@0 1309
michael@0 1310 // XXXbz And if NS_FAILED(rv), what? I guess we need to clean up the list
michael@0 1311 // and deal with all the placeholders... but what if the placeholders aren't
michael@0 1312 // in the document yet? Could that happen?
michael@0 1313 NS_ASSERTION(NS_SUCCEEDED(rv), "Frames getting lost!");
michael@0 1314 }
michael@0 1315
michael@0 1316
michael@0 1317 nsFrameConstructorSaveState::nsFrameConstructorSaveState()
michael@0 1318 : mItems(nullptr),
michael@0 1319 mSavedItems(nullptr),
michael@0 1320 mChildListID(kPrincipalList),
michael@0 1321 mState(nullptr),
michael@0 1322 mSavedFixedItems(nullptr),
michael@0 1323 mSavedFixedPosIsAbsPos(false)
michael@0 1324 {
michael@0 1325 }
michael@0 1326
michael@0 1327 nsFrameConstructorSaveState::~nsFrameConstructorSaveState()
michael@0 1328 {
michael@0 1329 // Restore the state
michael@0 1330 if (mItems) {
michael@0 1331 NS_ASSERTION(mState, "Can't have mItems set without having a state!");
michael@0 1332 mState->ProcessFrameInsertions(*mItems, mChildListID);
michael@0 1333 *mItems = mSavedItems;
michael@0 1334 #ifdef DEBUG
michael@0 1335 // We've transferred the child list, so drop the pointer we held to it.
michael@0 1336 // Note that this only matters for the assert in ~nsAbsoluteItems.
michael@0 1337 mSavedItems.Clear();
michael@0 1338 #endif
michael@0 1339 if (mItems == &mState->mAbsoluteItems) {
michael@0 1340 mState->mFixedPosIsAbsPos = mSavedFixedPosIsAbsPos;
michael@0 1341 if (mSavedFixedPosIsAbsPos) {
michael@0 1342 // mAbsoluteItems was moved to mFixedItems, so move mFixedItems back
michael@0 1343 // and repair the old mFixedItems now.
michael@0 1344 mState->mAbsoluteItems = mState->mFixedItems;
michael@0 1345 mState->mFixedItems = mSavedFixedItems;
michael@0 1346 #ifdef DEBUG
michael@0 1347 mSavedFixedItems.Clear();
michael@0 1348 #endif
michael@0 1349 }
michael@0 1350 }
michael@0 1351 NS_ASSERTION(!mItems->LastChild() || !mItems->LastChild()->GetNextSibling(),
michael@0 1352 "Something corrupted our list");
michael@0 1353 }
michael@0 1354 }
michael@0 1355
michael@0 1356 static
michael@0 1357 bool IsBorderCollapse(nsIFrame* aFrame)
michael@0 1358 {
michael@0 1359 for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
michael@0 1360 if (nsGkAtoms::tableFrame == frame->GetType()) {
michael@0 1361 return ((nsTableFrame*)frame)->IsBorderCollapse();
michael@0 1362 }
michael@0 1363 }
michael@0 1364 NS_ASSERTION(false, "program error");
michael@0 1365 return false;
michael@0 1366 }
michael@0 1367
michael@0 1368 /**
michael@0 1369 * Moves aFrameList from aOldParent to aNewParent. This updates the parent
michael@0 1370 * pointer of the frames in the list, and reparents their views as needed.
michael@0 1371 * nsFrame::SetParent sets the NS_FRAME_HAS_VIEW bit on aNewParent and its
michael@0 1372 * ancestors as needed. Then it sets the list as the initial child list
michael@0 1373 * on aNewParent, unless aNewParent either already has kids or has been
michael@0 1374 * reflowed; in that case it appends the new frames. Note that this
michael@0 1375 * method differs from ReparentFrames in that it doesn't change the kids'
michael@0 1376 * style contexts.
michael@0 1377 */
michael@0 1378 // XXXbz Since this is only used for {ib} splits, could we just copy the view
michael@0 1379 // bits from aOldParent to aNewParent and then use the
michael@0 1380 // nsFrameList::ApplySetParent? That would still leave us doing two passes
michael@0 1381 // over the list, of course; if we really wanted to we could factor out the
michael@0 1382 // relevant part of ReparentFrameViewList, I suppose... Or just get rid of
michael@0 1383 // views, which would make most of this function go away.
michael@0 1384 static void
michael@0 1385 MoveChildrenTo(nsPresContext* aPresContext,
michael@0 1386 nsIFrame* aOldParent,
michael@0 1387 nsIFrame* aNewParent,
michael@0 1388 nsFrameList& aFrameList)
michael@0 1389 {
michael@0 1390 bool sameGrandParent = aOldParent->GetParent() == aNewParent->GetParent();
michael@0 1391
michael@0 1392 if (aNewParent->HasView() || aOldParent->HasView() || !sameGrandParent) {
michael@0 1393 // Move the frames into the new view
michael@0 1394 nsContainerFrame::ReparentFrameViewList(aFrameList, aOldParent, aNewParent);
michael@0 1395 }
michael@0 1396
michael@0 1397 for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
michael@0 1398 e.get()->SetParent(aNewParent);
michael@0 1399 }
michael@0 1400
michael@0 1401 if (aNewParent->PrincipalChildList().IsEmpty() &&
michael@0 1402 (aNewParent->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
michael@0 1403 aNewParent->SetInitialChildList(kPrincipalList, aFrameList);
michael@0 1404 } else {
michael@0 1405 aNewParent->AppendFrames(kPrincipalList, aFrameList);
michael@0 1406 }
michael@0 1407 }
michael@0 1408
michael@0 1409 //----------------------------------------------------------------------
michael@0 1410
michael@0 1411 nsCSSFrameConstructor::nsCSSFrameConstructor(nsIDocument *aDocument,
michael@0 1412 nsIPresShell *aPresShell,
michael@0 1413 nsStyleSet* aStyleSet)
michael@0 1414 : nsFrameManager(aPresShell, aStyleSet)
michael@0 1415 , mDocument(aDocument)
michael@0 1416 , mRootElementFrame(nullptr)
michael@0 1417 , mRootElementStyleFrame(nullptr)
michael@0 1418 , mFixedContainingBlock(nullptr)
michael@0 1419 , mDocElementContainingBlock(nullptr)
michael@0 1420 , mGfxScrollFrame(nullptr)
michael@0 1421 , mPageSequenceFrame(nullptr)
michael@0 1422 , mCurrentDepth(0)
michael@0 1423 , mUpdateCount(0)
michael@0 1424 , mQuotesDirty(false)
michael@0 1425 , mCountersDirty(false)
michael@0 1426 , mIsDestroyingFrameTree(false)
michael@0 1427 , mHasRootAbsPosContainingBlock(false)
michael@0 1428 , mAlwaysCreateFramesForIgnorableWhitespace(false)
michael@0 1429 {
michael@0 1430 #ifdef DEBUG
michael@0 1431 static bool gFirstTime = true;
michael@0 1432 if (gFirstTime) {
michael@0 1433 gFirstTime = false;
michael@0 1434 char* flags = PR_GetEnv("GECKO_FRAMECTOR_DEBUG_FLAGS");
michael@0 1435 if (flags) {
michael@0 1436 bool error = false;
michael@0 1437 for (;;) {
michael@0 1438 char* comma = PL_strchr(flags, ',');
michael@0 1439 if (comma)
michael@0 1440 *comma = '\0';
michael@0 1441
michael@0 1442 bool found = false;
michael@0 1443 FrameCtorDebugFlags* flag = gFlags;
michael@0 1444 FrameCtorDebugFlags* limit = gFlags + NUM_DEBUG_FLAGS;
michael@0 1445 while (flag < limit) {
michael@0 1446 if (PL_strcasecmp(flag->name, flags) == 0) {
michael@0 1447 *(flag->on) = true;
michael@0 1448 printf("nsCSSFrameConstructor: setting %s debug flag on\n", flag->name);
michael@0 1449 found = true;
michael@0 1450 break;
michael@0 1451 }
michael@0 1452 ++flag;
michael@0 1453 }
michael@0 1454
michael@0 1455 if (! found)
michael@0 1456 error = true;
michael@0 1457
michael@0 1458 if (! comma)
michael@0 1459 break;
michael@0 1460
michael@0 1461 *comma = ',';
michael@0 1462 flags = comma + 1;
michael@0 1463 }
michael@0 1464
michael@0 1465 if (error) {
michael@0 1466 printf("Here are the available GECKO_FRAMECTOR_DEBUG_FLAGS:\n");
michael@0 1467 FrameCtorDebugFlags* flag = gFlags;
michael@0 1468 FrameCtorDebugFlags* limit = gFlags + NUM_DEBUG_FLAGS;
michael@0 1469 while (flag < limit) {
michael@0 1470 printf(" %s\n", flag->name);
michael@0 1471 ++flag;
michael@0 1472 }
michael@0 1473 printf("Note: GECKO_FRAMECTOR_DEBUG_FLAGS is a comma separated list of flag\n");
michael@0 1474 printf("names (no whitespace)\n");
michael@0 1475 }
michael@0 1476 }
michael@0 1477 }
michael@0 1478 #endif
michael@0 1479 }
michael@0 1480
michael@0 1481 void
michael@0 1482 nsCSSFrameConstructor::NotifyDestroyingFrame(nsIFrame* aFrame)
michael@0 1483 {
michael@0 1484 NS_PRECONDITION(mUpdateCount != 0,
michael@0 1485 "Should be in an update while destroying frames");
michael@0 1486
michael@0 1487 if (aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT) {
michael@0 1488 if (mQuoteList.DestroyNodesFor(aFrame))
michael@0 1489 QuotesDirty();
michael@0 1490 }
michael@0 1491
michael@0 1492 if (mCounterManager.DestroyNodesFor(aFrame)) {
michael@0 1493 // Technically we don't need to update anything if we destroyed only
michael@0 1494 // USE nodes. However, this is unlikely to happen in the real world
michael@0 1495 // since USE nodes generally go along with INCREMENT nodes.
michael@0 1496 CountersDirty();
michael@0 1497 }
michael@0 1498
michael@0 1499 RestyleManager()->NotifyDestroyingFrame(aFrame);
michael@0 1500
michael@0 1501 nsFrameManager::NotifyDestroyingFrame(aFrame);
michael@0 1502 }
michael@0 1503
michael@0 1504 struct nsGenConInitializer {
michael@0 1505 nsAutoPtr<nsGenConNode> mNode;
michael@0 1506 nsGenConList* mList;
michael@0 1507 void (nsCSSFrameConstructor::*mDirtyAll)();
michael@0 1508
michael@0 1509 nsGenConInitializer(nsGenConNode* aNode, nsGenConList* aList,
michael@0 1510 void (nsCSSFrameConstructor::*aDirtyAll)())
michael@0 1511 : mNode(aNode), mList(aList), mDirtyAll(aDirtyAll) {}
michael@0 1512 };
michael@0 1513
michael@0 1514 already_AddRefed<nsIContent>
michael@0 1515 nsCSSFrameConstructor::CreateGenConTextNode(nsFrameConstructorState& aState,
michael@0 1516 const nsString& aString,
michael@0 1517 nsCOMPtr<nsIDOMCharacterData>* aText,
michael@0 1518 nsGenConInitializer* aInitializer)
michael@0 1519 {
michael@0 1520 nsRefPtr<nsTextNode> content = new nsTextNode(mDocument->NodeInfoManager());
michael@0 1521 content->SetText(aString, false);
michael@0 1522 if (aText) {
michael@0 1523 *aText = content;
michael@0 1524 }
michael@0 1525 if (aInitializer) {
michael@0 1526 content->SetProperty(nsGkAtoms::genConInitializerProperty, aInitializer,
michael@0 1527 nsINode::DeleteProperty<nsGenConInitializer>);
michael@0 1528 aState.mGeneratedTextNodesWithInitializer.AppendObject(content);
michael@0 1529 }
michael@0 1530 return content.forget();
michael@0 1531 }
michael@0 1532
michael@0 1533 already_AddRefed<nsIContent>
michael@0 1534 nsCSSFrameConstructor::CreateGeneratedContent(nsFrameConstructorState& aState,
michael@0 1535 nsIContent* aParentContent,
michael@0 1536 nsStyleContext* aStyleContext,
michael@0 1537 uint32_t aContentIndex)
michael@0 1538 {
michael@0 1539 // Get the content value
michael@0 1540 const nsStyleContentData &data =
michael@0 1541 aStyleContext->StyleContent()->ContentAt(aContentIndex);
michael@0 1542 nsStyleContentType type = data.mType;
michael@0 1543
michael@0 1544 if (eStyleContentType_Image == type) {
michael@0 1545 if (!data.mContent.mImage) {
michael@0 1546 // CSS had something specified that couldn't be converted to an
michael@0 1547 // image object
michael@0 1548 return nullptr;
michael@0 1549 }
michael@0 1550
michael@0 1551 // Create an image content object and pass it the image request.
michael@0 1552 // XXX Check if it's an image type we can handle...
michael@0 1553
michael@0 1554 nsCOMPtr<nsINodeInfo> nodeInfo;
michael@0 1555 nodeInfo = mDocument->NodeInfoManager()->
michael@0 1556 GetNodeInfo(nsGkAtoms::mozgeneratedcontentimage, nullptr,
michael@0 1557 kNameSpaceID_XHTML, nsIDOMNode::ELEMENT_NODE);
michael@0 1558
michael@0 1559 nsCOMPtr<nsIContent> content;
michael@0 1560 NS_NewGenConImageContent(getter_AddRefs(content), nodeInfo.forget(),
michael@0 1561 data.mContent.mImage);
michael@0 1562 return content.forget();
michael@0 1563 }
michael@0 1564
michael@0 1565 switch (type) {
michael@0 1566 case eStyleContentType_String:
michael@0 1567 return CreateGenConTextNode(aState,
michael@0 1568 nsDependentString(data.mContent.mString),
michael@0 1569 nullptr, nullptr);
michael@0 1570
michael@0 1571 case eStyleContentType_Attr:
michael@0 1572 {
michael@0 1573 nsCOMPtr<nsIAtom> attrName;
michael@0 1574 int32_t attrNameSpace = kNameSpaceID_None;
michael@0 1575 nsAutoString contentString(data.mContent.mString);
michael@0 1576
michael@0 1577 int32_t barIndex = contentString.FindChar('|'); // CSS namespace delimiter
michael@0 1578 if (-1 != barIndex) {
michael@0 1579 nsAutoString nameSpaceVal;
michael@0 1580 contentString.Left(nameSpaceVal, barIndex);
michael@0 1581 nsresult error;
michael@0 1582 attrNameSpace = nameSpaceVal.ToInteger(&error);
michael@0 1583 contentString.Cut(0, barIndex + 1);
michael@0 1584 if (contentString.Length()) {
michael@0 1585 if (mDocument->IsHTML() && aParentContent->IsHTML()) {
michael@0 1586 ToLowerCase(contentString);
michael@0 1587 }
michael@0 1588 attrName = do_GetAtom(contentString);
michael@0 1589 }
michael@0 1590 }
michael@0 1591 else {
michael@0 1592 if (mDocument->IsHTML() && aParentContent->IsHTML()) {
michael@0 1593 ToLowerCase(contentString);
michael@0 1594 }
michael@0 1595 attrName = do_GetAtom(contentString);
michael@0 1596 }
michael@0 1597
michael@0 1598 if (!attrName) {
michael@0 1599 return nullptr;
michael@0 1600 }
michael@0 1601
michael@0 1602 nsCOMPtr<nsIContent> content;
michael@0 1603 NS_NewAttributeContent(mDocument->NodeInfoManager(),
michael@0 1604 attrNameSpace, attrName, getter_AddRefs(content));
michael@0 1605 return content.forget();
michael@0 1606 }
michael@0 1607
michael@0 1608 case eStyleContentType_Counter:
michael@0 1609 case eStyleContentType_Counters:
michael@0 1610 {
michael@0 1611 nsCSSValue::Array* counters = data.mContent.mCounters;
michael@0 1612 nsCounterList* counterList = mCounterManager.CounterListFor(
michael@0 1613 nsDependentString(counters->Item(0).GetStringBufferValue()));
michael@0 1614 if (!counterList)
michael@0 1615 return nullptr;
michael@0 1616
michael@0 1617 nsCounterUseNode* node =
michael@0 1618 new nsCounterUseNode(counters, aContentIndex,
michael@0 1619 type == eStyleContentType_Counters);
michael@0 1620
michael@0 1621 nsGenConInitializer* initializer =
michael@0 1622 new nsGenConInitializer(node, counterList,
michael@0 1623 &nsCSSFrameConstructor::CountersDirty);
michael@0 1624 return CreateGenConTextNode(aState, EmptyString(), &node->mText,
michael@0 1625 initializer);
michael@0 1626 }
michael@0 1627
michael@0 1628 case eStyleContentType_Image:
michael@0 1629 NS_NOTREACHED("handled by if above");
michael@0 1630 return nullptr;
michael@0 1631
michael@0 1632 case eStyleContentType_OpenQuote:
michael@0 1633 case eStyleContentType_CloseQuote:
michael@0 1634 case eStyleContentType_NoOpenQuote:
michael@0 1635 case eStyleContentType_NoCloseQuote:
michael@0 1636 {
michael@0 1637 nsQuoteNode* node =
michael@0 1638 new nsQuoteNode(type, aContentIndex);
michael@0 1639
michael@0 1640 nsGenConInitializer* initializer =
michael@0 1641 new nsGenConInitializer(node, &mQuoteList,
michael@0 1642 &nsCSSFrameConstructor::QuotesDirty);
michael@0 1643 return CreateGenConTextNode(aState, EmptyString(), &node->mText,
michael@0 1644 initializer);
michael@0 1645 }
michael@0 1646
michael@0 1647 case eStyleContentType_AltContent:
michael@0 1648 {
michael@0 1649 // Use the "alt" attribute; if that fails and the node is an HTML
michael@0 1650 // <input>, try the value attribute and then fall back to some default
michael@0 1651 // localized text we have.
michael@0 1652 // XXX what if the 'alt' attribute is added later, how will we
michael@0 1653 // detect that and do the right thing here?
michael@0 1654 if (aParentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::alt)) {
michael@0 1655 nsCOMPtr<nsIContent> content;
michael@0 1656 NS_NewAttributeContent(mDocument->NodeInfoManager(),
michael@0 1657 kNameSpaceID_None, nsGkAtoms::alt, getter_AddRefs(content));
michael@0 1658 return content.forget();
michael@0 1659 }
michael@0 1660
michael@0 1661 if (aParentContent->IsHTML() &&
michael@0 1662 aParentContent->NodeInfo()->Equals(nsGkAtoms::input)) {
michael@0 1663 if (aParentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
michael@0 1664 nsCOMPtr<nsIContent> content;
michael@0 1665 NS_NewAttributeContent(mDocument->NodeInfoManager(),
michael@0 1666 kNameSpaceID_None, nsGkAtoms::value, getter_AddRefs(content));
michael@0 1667 return content.forget();
michael@0 1668 }
michael@0 1669
michael@0 1670 nsXPIDLString temp;
michael@0 1671 nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
michael@0 1672 "Submit", temp);
michael@0 1673 return CreateGenConTextNode(aState, temp, nullptr, nullptr);
michael@0 1674 }
michael@0 1675
michael@0 1676 break;
michael@0 1677 }
michael@0 1678
michael@0 1679 case eStyleContentType_Uninitialized:
michael@0 1680 NS_NOTREACHED("uninitialized content type");
michael@0 1681 return nullptr;
michael@0 1682 } // switch
michael@0 1683
michael@0 1684 return nullptr;
michael@0 1685 }
michael@0 1686
michael@0 1687 /*
michael@0 1688 * aParentFrame - the frame that should be the parent of the generated
michael@0 1689 * content. This is the frame for the corresponding content node,
michael@0 1690 * which must not be a leaf frame.
michael@0 1691 *
michael@0 1692 * Any items created are added to aItems.
michael@0 1693 *
michael@0 1694 * We create an XML element (tag _moz_generated_content_before or
michael@0 1695 * _moz_generated_content_after) representing the pseudoelement. We
michael@0 1696 * create a DOM node for each 'content' item and make those nodes the
michael@0 1697 * children of the XML element. Then we create a frame subtree for
michael@0 1698 * the XML element as if it were a regular child of
michael@0 1699 * aParentFrame/aParentContent, giving the XML element the ::before or
michael@0 1700 * ::after style.
michael@0 1701 */
michael@0 1702 void
michael@0 1703 nsCSSFrameConstructor::CreateGeneratedContentItem(nsFrameConstructorState& aState,
michael@0 1704 nsIFrame* aParentFrame,
michael@0 1705 nsIContent* aParentContent,
michael@0 1706 nsStyleContext* aStyleContext,
michael@0 1707 nsCSSPseudoElements::Type aPseudoElement,
michael@0 1708 FrameConstructionItemList& aItems)
michael@0 1709 {
michael@0 1710 // XXXbz is this ever true?
michael@0 1711 if (!aParentContent->IsElement()) {
michael@0 1712 NS_ERROR("Bogus generated content parent");
michael@0 1713 return;
michael@0 1714 }
michael@0 1715
michael@0 1716 nsStyleSet *styleSet = mPresShell->StyleSet();
michael@0 1717
michael@0 1718 // Probe for the existence of the pseudo-element
michael@0 1719 nsRefPtr<nsStyleContext> pseudoStyleContext;
michael@0 1720 pseudoStyleContext =
michael@0 1721 styleSet->ProbePseudoElementStyle(aParentContent->AsElement(),
michael@0 1722 aPseudoElement,
michael@0 1723 aStyleContext,
michael@0 1724 aState.mTreeMatchContext);
michael@0 1725 if (!pseudoStyleContext)
michael@0 1726 return;
michael@0 1727 // |ProbePseudoStyleFor| checked the 'display' property and the
michael@0 1728 // |ContentCount()| of the 'content' property for us.
michael@0 1729 nsCOMPtr<nsINodeInfo> nodeInfo;
michael@0 1730 nsIAtom* elemName = aPseudoElement == nsCSSPseudoElements::ePseudo_before ?
michael@0 1731 nsGkAtoms::mozgeneratedcontentbefore : nsGkAtoms::mozgeneratedcontentafter;
michael@0 1732 nodeInfo = mDocument->NodeInfoManager()->GetNodeInfo(elemName, nullptr,
michael@0 1733 kNameSpaceID_None,
michael@0 1734 nsIDOMNode::ELEMENT_NODE);
michael@0 1735 nsCOMPtr<Element> container;
michael@0 1736 nsresult rv = NS_NewXMLElement(getter_AddRefs(container), nodeInfo.forget());
michael@0 1737 if (NS_FAILED(rv))
michael@0 1738 return;
michael@0 1739 container->SetIsNativeAnonymousRoot();
michael@0 1740
michael@0 1741 rv = container->BindToTree(mDocument, aParentContent, aParentContent, true);
michael@0 1742 if (NS_FAILED(rv)) {
michael@0 1743 container->UnbindFromTree();
michael@0 1744 return;
michael@0 1745 }
michael@0 1746
michael@0 1747 uint32_t contentCount = pseudoStyleContext->StyleContent()->ContentCount();
michael@0 1748 for (uint32_t contentIndex = 0; contentIndex < contentCount; contentIndex++) {
michael@0 1749 nsCOMPtr<nsIContent> content =
michael@0 1750 CreateGeneratedContent(aState, aParentContent, pseudoStyleContext,
michael@0 1751 contentIndex);
michael@0 1752 if (content) {
michael@0 1753 container->AppendChildTo(content, false);
michael@0 1754 }
michael@0 1755 }
michael@0 1756
michael@0 1757 AddFrameConstructionItemsInternal(aState, container, aParentFrame, elemName,
michael@0 1758 kNameSpaceID_None, true,
michael@0 1759 pseudoStyleContext,
michael@0 1760 ITEM_IS_GENERATED_CONTENT, nullptr,
michael@0 1761 aItems);
michael@0 1762 }
michael@0 1763
michael@0 1764 /****************************************************
michael@0 1765 ** BEGIN TABLE SECTION
michael@0 1766 ****************************************************/
michael@0 1767
michael@0 1768 // The term pseudo frame is being used instead of anonymous frame, since anonymous
michael@0 1769 // frame has been used elsewhere to refer to frames that have generated content
michael@0 1770
michael@0 1771 // Return whether the given frame is a table pseudo-frame. Note that
michael@0 1772 // cell-content and table-outer frames have pseudo-types, but are always
michael@0 1773 // created, even for non-anonymous cells and tables respectively. So for those
michael@0 1774 // we have to examine the cell or table frame to see whether it's a pseudo
michael@0 1775 // frame. In particular, a lone table caption will have an outer table as its
michael@0 1776 // parent, but will also trigger construction of an empty inner table, which
michael@0 1777 // will be the one we can examine to see whether the outer was a pseudo-frame.
michael@0 1778 static bool
michael@0 1779 IsTablePseudo(nsIFrame* aFrame)
michael@0 1780 {
michael@0 1781 nsIAtom* pseudoType = aFrame->StyleContext()->GetPseudo();
michael@0 1782 return pseudoType &&
michael@0 1783 (pseudoType == nsCSSAnonBoxes::table ||
michael@0 1784 pseudoType == nsCSSAnonBoxes::inlineTable ||
michael@0 1785 pseudoType == nsCSSAnonBoxes::tableColGroup ||
michael@0 1786 pseudoType == nsCSSAnonBoxes::tableRowGroup ||
michael@0 1787 pseudoType == nsCSSAnonBoxes::tableRow ||
michael@0 1788 pseudoType == nsCSSAnonBoxes::tableCell ||
michael@0 1789 (pseudoType == nsCSSAnonBoxes::cellContent &&
michael@0 1790 aFrame->GetParent()->StyleContext()->GetPseudo() ==
michael@0 1791 nsCSSAnonBoxes::tableCell) ||
michael@0 1792 (pseudoType == nsCSSAnonBoxes::tableOuter &&
michael@0 1793 (aFrame->GetFirstPrincipalChild()->StyleContext()->GetPseudo() ==
michael@0 1794 nsCSSAnonBoxes::table ||
michael@0 1795 aFrame->GetFirstPrincipalChild()->StyleContext()->GetPseudo() ==
michael@0 1796 nsCSSAnonBoxes::inlineTable)));
michael@0 1797 }
michael@0 1798
michael@0 1799 /* static */
michael@0 1800 nsCSSFrameConstructor::ParentType
michael@0 1801 nsCSSFrameConstructor::GetParentType(nsIAtom* aFrameType)
michael@0 1802 {
michael@0 1803 if (aFrameType == nsGkAtoms::tableFrame) {
michael@0 1804 return eTypeTable;
michael@0 1805 }
michael@0 1806 if (aFrameType == nsGkAtoms::tableRowGroupFrame) {
michael@0 1807 return eTypeRowGroup;
michael@0 1808 }
michael@0 1809 if (aFrameType == nsGkAtoms::tableRowFrame) {
michael@0 1810 return eTypeRow;
michael@0 1811 }
michael@0 1812 if (aFrameType == nsGkAtoms::tableColGroupFrame) {
michael@0 1813 return eTypeColGroup;
michael@0 1814 }
michael@0 1815
michael@0 1816 return eTypeBlock;
michael@0 1817 }
michael@0 1818
michael@0 1819 static nsIFrame*
michael@0 1820 AdjustCaptionParentFrame(nsIFrame* aParentFrame)
michael@0 1821 {
michael@0 1822 if (nsGkAtoms::tableFrame == aParentFrame->GetType()) {
michael@0 1823 return aParentFrame->GetParent();;
michael@0 1824 }
michael@0 1825 return aParentFrame;
michael@0 1826 }
michael@0 1827
michael@0 1828 /**
michael@0 1829 * If the parent frame is a |tableFrame| and the child is a
michael@0 1830 * |captionFrame|, then we want to insert the frames beneath the
michael@0 1831 * |tableFrame|'s parent frame. Returns |true| if the parent frame
michael@0 1832 * needed to be fixed up.
michael@0 1833 */
michael@0 1834 static bool
michael@0 1835 GetCaptionAdjustedParent(nsIFrame* aParentFrame,
michael@0 1836 const nsIFrame* aChildFrame,
michael@0 1837 nsIFrame** aAdjParentFrame)
michael@0 1838 {
michael@0 1839 *aAdjParentFrame = aParentFrame;
michael@0 1840 bool haveCaption = false;
michael@0 1841
michael@0 1842 if (nsGkAtoms::tableCaptionFrame == aChildFrame->GetType()) {
michael@0 1843 haveCaption = true;
michael@0 1844 *aAdjParentFrame = AdjustCaptionParentFrame(aParentFrame);
michael@0 1845 }
michael@0 1846 return haveCaption;
michael@0 1847 }
michael@0 1848
michael@0 1849 void
michael@0 1850 nsCSSFrameConstructor::AdjustParentFrame(nsIFrame* & aParentFrame,
michael@0 1851 const FrameConstructionData* aFCData,
michael@0 1852 nsStyleContext* aStyleContext)
michael@0 1853 {
michael@0 1854 NS_PRECONDITION(aStyleContext, "Must have child's style context");
michael@0 1855 NS_PRECONDITION(aFCData, "Must have frame construction data");
michael@0 1856
michael@0 1857 bool tablePart = ((aFCData->mBits & FCDATA_IS_TABLE_PART) != 0);
michael@0 1858
michael@0 1859 if (tablePart && aStyleContext->StyleDisplay()->mDisplay ==
michael@0 1860 NS_STYLE_DISPLAY_TABLE_CAPTION) {
michael@0 1861 aParentFrame = AdjustCaptionParentFrame(aParentFrame);
michael@0 1862 }
michael@0 1863 }
michael@0 1864
michael@0 1865 // Pull all the captions present in aItems out into aCaptions
michael@0 1866 static void
michael@0 1867 PullOutCaptionFrames(nsFrameItems& aItems, nsFrameItems& aCaptions)
michael@0 1868 {
michael@0 1869 nsIFrame *child = aItems.FirstChild();
michael@0 1870 while (child) {
michael@0 1871 nsIFrame *nextSibling = child->GetNextSibling();
michael@0 1872 if (nsGkAtoms::tableCaptionFrame == child->GetType()) {
michael@0 1873 aItems.RemoveFrame(child);
michael@0 1874 aCaptions.AddChild(child);
michael@0 1875 }
michael@0 1876 child = nextSibling;
michael@0 1877 }
michael@0 1878 }
michael@0 1879
michael@0 1880
michael@0 1881 // Construct the outer, inner table frames and the children frames for the table.
michael@0 1882 // XXX Page break frames for pseudo table frames are not constructed to avoid the risk
michael@0 1883 // associated with revising the pseudo frame mechanism. The long term solution
michael@0 1884 // of having frames handle page-break-before/after will solve the problem.
michael@0 1885 nsIFrame*
michael@0 1886 nsCSSFrameConstructor::ConstructTable(nsFrameConstructorState& aState,
michael@0 1887 FrameConstructionItem& aItem,
michael@0 1888 nsIFrame* aParentFrame,
michael@0 1889 const nsStyleDisplay* aDisplay,
michael@0 1890 nsFrameItems& aFrameItems)
michael@0 1891 {
michael@0 1892 NS_PRECONDITION(aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE ||
michael@0 1893 aDisplay->mDisplay == NS_STYLE_DISPLAY_INLINE_TABLE,
michael@0 1894 "Unexpected call");
michael@0 1895
michael@0 1896 nsIContent* const content = aItem.mContent;
michael@0 1897 nsStyleContext* const styleContext = aItem.mStyleContext;
michael@0 1898 const uint32_t nameSpaceID = aItem.mNameSpaceID;
michael@0 1899
michael@0 1900 // create the pseudo SC for the outer table as a child of the inner SC
michael@0 1901 nsRefPtr<nsStyleContext> outerStyleContext;
michael@0 1902 outerStyleContext = mPresShell->StyleSet()->
michael@0 1903 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::tableOuter, styleContext);
michael@0 1904
michael@0 1905 // Create the outer table frame which holds the caption and inner table frame
michael@0 1906 nsIFrame* newFrame;
michael@0 1907 if (kNameSpaceID_MathML == nameSpaceID)
michael@0 1908 newFrame = NS_NewMathMLmtableOuterFrame(mPresShell, outerStyleContext);
michael@0 1909 else
michael@0 1910 newFrame = NS_NewTableOuterFrame(mPresShell, outerStyleContext);
michael@0 1911
michael@0 1912 nsIFrame* geometricParent =
michael@0 1913 aState.GetGeometricParent(outerStyleContext->StyleDisplay(),
michael@0 1914 aParentFrame);
michael@0 1915
michael@0 1916 // Init the table outer frame
michael@0 1917 InitAndRestoreFrame(aState, content, geometricParent, newFrame);
michael@0 1918
michael@0 1919 // Create the inner table frame
michael@0 1920 nsIFrame* innerFrame;
michael@0 1921 if (kNameSpaceID_MathML == nameSpaceID)
michael@0 1922 innerFrame = NS_NewMathMLmtableFrame(mPresShell, styleContext);
michael@0 1923 else
michael@0 1924 innerFrame = NS_NewTableFrame(mPresShell, styleContext);
michael@0 1925
michael@0 1926 InitAndRestoreFrame(aState, content, newFrame, innerFrame);
michael@0 1927
michael@0 1928 // Put the newly created frames into the right child list
michael@0 1929 SetInitialSingleChild(newFrame, innerFrame);
michael@0 1930
michael@0 1931 aState.AddChild(newFrame, aFrameItems, content, styleContext, aParentFrame);
michael@0 1932
michael@0 1933 if (!mRootElementFrame) {
michael@0 1934 // The frame we're constructing will be the root element frame.
michael@0 1935 // Set mRootElementFrame before processing children.
michael@0 1936 mRootElementFrame = newFrame;
michael@0 1937 }
michael@0 1938
michael@0 1939 nsFrameItems childItems;
michael@0 1940
michael@0 1941 // Process children
michael@0 1942 nsFrameConstructorSaveState absoluteSaveState;
michael@0 1943 const nsStyleDisplay* display = outerStyleContext->StyleDisplay();
michael@0 1944
michael@0 1945 // Mark the table frame as an absolute container if needed
michael@0 1946 newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
michael@0 1947 if (display->IsPositioned(newFrame)) {
michael@0 1948 aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState);
michael@0 1949 }
michael@0 1950 NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
michael@0 1951 "nsIAnonymousContentCreator::CreateAnonymousContent "
michael@0 1952 "implementations for table frames are not currently expected "
michael@0 1953 "to output a list where the items have their own children");
michael@0 1954 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
michael@0 1955 ConstructFramesFromItemList(aState, aItem.mChildItems,
michael@0 1956 innerFrame, childItems);
michael@0 1957 } else {
michael@0 1958 ProcessChildren(aState, content, styleContext, innerFrame,
michael@0 1959 true, childItems, false, aItem.mPendingBinding);
michael@0 1960 }
michael@0 1961
michael@0 1962 nsFrameItems captionItems;
michael@0 1963 PullOutCaptionFrames(childItems, captionItems);
michael@0 1964
michael@0 1965 // Set the inner table frame's initial primary list
michael@0 1966 innerFrame->SetInitialChildList(kPrincipalList, childItems);
michael@0 1967
michael@0 1968 // Set the outer table frame's secondary childlist lists
michael@0 1969 if (captionItems.NotEmpty()) {
michael@0 1970 newFrame->SetInitialChildList(nsIFrame::kCaptionList, captionItems);
michael@0 1971 }
michael@0 1972
michael@0 1973 return newFrame;
michael@0 1974 }
michael@0 1975
michael@0 1976 static void
michael@0 1977 MakeTablePartAbsoluteContainingBlockIfNeeded(nsFrameConstructorState& aState,
michael@0 1978 const nsStyleDisplay* aDisplay,
michael@0 1979 nsFrameConstructorSaveState& aAbsSaveState,
michael@0 1980 nsIFrame* aFrame)
michael@0 1981 {
michael@0 1982 // If we're positioned, then we need to become an absolute containing block
michael@0 1983 // for any absolutely positioned children and register for post-reflow fixup.
michael@0 1984 //
michael@0 1985 // Note that usually if a frame type can be an absolute containing block, we
michael@0 1986 // always set NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN, whether it actually is or not.
michael@0 1987 // However, in this case flag serves the additional purpose of indicating that
michael@0 1988 // the frame was registered with its table frame. This allows us to avoid the
michael@0 1989 // overhead of unregistering the frame in most cases.
michael@0 1990 if (aDisplay->IsPositioned(aFrame)) {
michael@0 1991 aFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
michael@0 1992 aState.PushAbsoluteContainingBlock(aFrame, aFrame, aAbsSaveState);
michael@0 1993 nsTableFrame::RegisterPositionedTablePart(aFrame);
michael@0 1994 }
michael@0 1995 }
michael@0 1996
michael@0 1997 nsIFrame*
michael@0 1998 nsCSSFrameConstructor::ConstructTableRowOrRowGroup(nsFrameConstructorState& aState,
michael@0 1999 FrameConstructionItem& aItem,
michael@0 2000 nsIFrame* aParentFrame,
michael@0 2001 const nsStyleDisplay* aDisplay,
michael@0 2002 nsFrameItems& aFrameItems)
michael@0 2003 {
michael@0 2004 NS_PRECONDITION(aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_ROW ||
michael@0 2005 aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_ROW_GROUP ||
michael@0 2006 aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP ||
michael@0 2007 aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_HEADER_GROUP,
michael@0 2008 "Unexpected call");
michael@0 2009 MOZ_ASSERT(aItem.mStyleContext->StyleDisplay() == aDisplay,
michael@0 2010 "Display style doesn't match style context");
michael@0 2011 nsIContent* const content = aItem.mContent;
michael@0 2012 nsStyleContext* const styleContext = aItem.mStyleContext;
michael@0 2013 const uint32_t nameSpaceID = aItem.mNameSpaceID;
michael@0 2014
michael@0 2015 nsIFrame* newFrame;
michael@0 2016 if (aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_ROW) {
michael@0 2017 if (kNameSpaceID_MathML == nameSpaceID)
michael@0 2018 newFrame = NS_NewMathMLmtrFrame(mPresShell, styleContext);
michael@0 2019 else
michael@0 2020 newFrame = NS_NewTableRowFrame(mPresShell, styleContext);
michael@0 2021 } else {
michael@0 2022 newFrame = NS_NewTableRowGroupFrame(mPresShell, styleContext);
michael@0 2023 }
michael@0 2024
michael@0 2025 InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
michael@0 2026
michael@0 2027 nsFrameConstructorSaveState absoluteSaveState;
michael@0 2028 MakeTablePartAbsoluteContainingBlockIfNeeded(aState, aDisplay,
michael@0 2029 absoluteSaveState,
michael@0 2030 newFrame);
michael@0 2031
michael@0 2032 nsFrameItems childItems;
michael@0 2033 NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
michael@0 2034 "nsIAnonymousContentCreator::CreateAnonymousContent "
michael@0 2035 "implementations for table frames are not currently expected "
michael@0 2036 "to output a list where the items have their own children");
michael@0 2037 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
michael@0 2038 ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame,
michael@0 2039 childItems);
michael@0 2040 } else {
michael@0 2041 ProcessChildren(aState, content, styleContext, newFrame,
michael@0 2042 true, childItems, false, aItem.mPendingBinding);
michael@0 2043 }
michael@0 2044
michael@0 2045 newFrame->SetInitialChildList(kPrincipalList, childItems);
michael@0 2046 aFrameItems.AddChild(newFrame);
michael@0 2047 return newFrame;
michael@0 2048 }
michael@0 2049
michael@0 2050 nsIFrame*
michael@0 2051 nsCSSFrameConstructor::ConstructTableCol(nsFrameConstructorState& aState,
michael@0 2052 FrameConstructionItem& aItem,
michael@0 2053 nsIFrame* aParentFrame,
michael@0 2054 const nsStyleDisplay* aStyleDisplay,
michael@0 2055 nsFrameItems& aFrameItems)
michael@0 2056 {
michael@0 2057 nsIContent* const content = aItem.mContent;
michael@0 2058 nsStyleContext* const styleContext = aItem.mStyleContext;
michael@0 2059
michael@0 2060 nsTableColFrame* colFrame = NS_NewTableColFrame(mPresShell, styleContext);
michael@0 2061 InitAndRestoreFrame(aState, content, aParentFrame, colFrame);
michael@0 2062
michael@0 2063 NS_ASSERTION(colFrame->StyleContext() == styleContext,
michael@0 2064 "Unexpected style context");
michael@0 2065
michael@0 2066 aFrameItems.AddChild(colFrame);
michael@0 2067
michael@0 2068 // construct additional col frames if the col frame has a span > 1
michael@0 2069 int32_t span = colFrame->GetSpan();
michael@0 2070 for (int32_t spanX = 1; spanX < span; spanX++) {
michael@0 2071 nsTableColFrame* newCol = NS_NewTableColFrame(mPresShell, styleContext);
michael@0 2072 InitAndRestoreFrame(aState, content, aParentFrame, newCol, false);
michael@0 2073 aFrameItems.LastChild()->SetNextContinuation(newCol);
michael@0 2074 newCol->SetPrevContinuation(aFrameItems.LastChild());
michael@0 2075 aFrameItems.AddChild(newCol);
michael@0 2076 newCol->SetColType(eColAnonymousCol);
michael@0 2077 }
michael@0 2078
michael@0 2079 return colFrame;
michael@0 2080 }
michael@0 2081
michael@0 2082 nsIFrame*
michael@0 2083 nsCSSFrameConstructor::ConstructTableCell(nsFrameConstructorState& aState,
michael@0 2084 FrameConstructionItem& aItem,
michael@0 2085 nsIFrame* aParentFrame,
michael@0 2086 const nsStyleDisplay* aDisplay,
michael@0 2087 nsFrameItems& aFrameItems)
michael@0 2088 {
michael@0 2089 NS_PRECONDITION(aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_CELL,
michael@0 2090 "Unexpected call");
michael@0 2091
michael@0 2092 nsIContent* const content = aItem.mContent;
michael@0 2093 nsStyleContext* const styleContext = aItem.mStyleContext;
michael@0 2094 const uint32_t nameSpaceID = aItem.mNameSpaceID;
michael@0 2095
michael@0 2096 bool borderCollapse = IsBorderCollapse(aParentFrame);
michael@0 2097 nsIFrame* newFrame;
michael@0 2098 // <mtable> is border separate in mathml.css and the MathML code doesn't implement
michael@0 2099 // border collapse. For those users who style <mtable> with border collapse,
michael@0 2100 // give them the default non-MathML table frames that understand border collapse.
michael@0 2101 // This won't break us because MathML table frames are all subclasses of the default
michael@0 2102 // table code, and so we can freely mix <mtable> with <mtr> or <tr>, <mtd> or <td>.
michael@0 2103 // What will happen is just that non-MathML frames won't understand MathML attributes
michael@0 2104 // and will therefore miss the special handling that the MathML code does.
michael@0 2105 if (kNameSpaceID_MathML == nameSpaceID && !borderCollapse)
michael@0 2106 newFrame = NS_NewMathMLmtdFrame(mPresShell, styleContext);
michael@0 2107 else
michael@0 2108 // Warning: If you change this and add a wrapper frame around table cell
michael@0 2109 // frames, make sure Bug 368554 doesn't regress!
michael@0 2110 // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp.
michael@0 2111 newFrame = NS_NewTableCellFrame(mPresShell, styleContext, borderCollapse);
michael@0 2112
michael@0 2113 // Initialize the table cell frame
michael@0 2114 InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
michael@0 2115
michael@0 2116 // Resolve pseudo style and initialize the body cell frame
michael@0 2117 nsRefPtr<nsStyleContext> innerPseudoStyle;
michael@0 2118 innerPseudoStyle = mPresShell->StyleSet()->
michael@0 2119 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::cellContent, styleContext);
michael@0 2120
michael@0 2121 // Create a block frame that will format the cell's content
michael@0 2122 bool isBlock;
michael@0 2123 nsIFrame* cellInnerFrame;
michael@0 2124 if (kNameSpaceID_MathML == nameSpaceID) {
michael@0 2125 cellInnerFrame = NS_NewMathMLmtdInnerFrame(mPresShell, innerPseudoStyle);
michael@0 2126 isBlock = false;
michael@0 2127 } else {
michael@0 2128 cellInnerFrame = NS_NewBlockFormattingContext(mPresShell, innerPseudoStyle);
michael@0 2129 isBlock = true;
michael@0 2130 }
michael@0 2131
michael@0 2132 InitAndRestoreFrame(aState, content, newFrame, cellInnerFrame);
michael@0 2133
michael@0 2134 nsFrameConstructorSaveState absoluteSaveState;
michael@0 2135 MakeTablePartAbsoluteContainingBlockIfNeeded(aState, aDisplay,
michael@0 2136 absoluteSaveState,
michael@0 2137 newFrame);
michael@0 2138
michael@0 2139 nsFrameItems childItems;
michael@0 2140 NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
michael@0 2141 "nsIAnonymousContentCreator::CreateAnonymousContent "
michael@0 2142 "implementations for table frames are not currently expected "
michael@0 2143 "to output a list where the items have their own children");
michael@0 2144 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
michael@0 2145 // Need to push ourselves as a float containing block.
michael@0 2146 // XXXbz it might be nice to work on getting the parent
michael@0 2147 // FrameConstructionItem down into ProcessChildren and just making use of
michael@0 2148 // the push there, but that's a bit of work.
michael@0 2149 nsFrameConstructorSaveState floatSaveState;
michael@0 2150 if (!isBlock) { /* MathML case */
michael@0 2151 aState.PushFloatContainingBlock(nullptr, floatSaveState);
michael@0 2152 } else {
michael@0 2153 aState.PushFloatContainingBlock(cellInnerFrame, floatSaveState);
michael@0 2154 }
michael@0 2155
michael@0 2156 ConstructFramesFromItemList(aState, aItem.mChildItems, cellInnerFrame,
michael@0 2157 childItems);
michael@0 2158 } else {
michael@0 2159 // Process the child content
michael@0 2160 ProcessChildren(aState, content, styleContext, cellInnerFrame,
michael@0 2161 true, childItems, isBlock, aItem.mPendingBinding);
michael@0 2162 }
michael@0 2163
michael@0 2164 cellInnerFrame->SetInitialChildList(kPrincipalList, childItems);
michael@0 2165 SetInitialSingleChild(newFrame, cellInnerFrame);
michael@0 2166 aFrameItems.AddChild(newFrame);
michael@0 2167 return newFrame;
michael@0 2168 }
michael@0 2169
michael@0 2170 static inline bool
michael@0 2171 NeedFrameFor(const nsFrameConstructorState& aState,
michael@0 2172 nsIFrame* aParentFrame,
michael@0 2173 nsIContent* aChildContent)
michael@0 2174 {
michael@0 2175 // XXX the GetContent() != aChildContent check is needed due to bug 135040.
michael@0 2176 // Remove it once that's fixed.
michael@0 2177 NS_PRECONDITION(!aChildContent->GetPrimaryFrame() ||
michael@0 2178 aState.mCreatingExtraFrames ||
michael@0 2179 aChildContent->GetPrimaryFrame()->GetContent() != aChildContent,
michael@0 2180 "Why did we get called?");
michael@0 2181
michael@0 2182 // don't create a whitespace frame if aParentFrame doesn't want it.
michael@0 2183 // always create frames for children in generated content. counter(),
michael@0 2184 // quotes, and attr() content can easily change dynamically and we don't
michael@0 2185 // want to be reconstructing frames. It's not even clear that these
michael@0 2186 // should be considered ignorable just because they evaluate to
michael@0 2187 // whitespace.
michael@0 2188
michael@0 2189 // We could handle all this in CreateNeededTablePseudos or some other place
michael@0 2190 // after we build our frame construction items, but that would involve
michael@0 2191 // creating frame construction items for whitespace kids of
michael@0 2192 // eExcludesIgnorableWhitespace frames, where we know we'll be dropping them
michael@0 2193 // all anyway, and involve an extra walk down the frame construction item
michael@0 2194 // list.
michael@0 2195 if (!aParentFrame->IsFrameOfType(nsIFrame::eExcludesIgnorableWhitespace) ||
michael@0 2196 aParentFrame->IsGeneratedContentFrame() ||
michael@0 2197 !aChildContent->IsNodeOfType(nsINode::eTEXT)) {
michael@0 2198 return true;
michael@0 2199 }
michael@0 2200
michael@0 2201 aChildContent->SetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
michael@0 2202 NS_REFRAME_IF_WHITESPACE);
michael@0 2203 return !aChildContent->TextIsOnlyWhitespace();
michael@0 2204 }
michael@0 2205
michael@0 2206 /***********************************************
michael@0 2207 * END TABLE SECTION
michael@0 2208 ***********************************************/
michael@0 2209
michael@0 2210 static bool CheckOverflow(nsPresContext* aPresContext,
michael@0 2211 const nsStyleDisplay* aDisplay)
michael@0 2212 {
michael@0 2213 if (aDisplay->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE)
michael@0 2214 return false;
michael@0 2215
michael@0 2216 if (aDisplay->mOverflowX == NS_STYLE_OVERFLOW_CLIP)
michael@0 2217 aPresContext->SetViewportOverflowOverride(NS_STYLE_OVERFLOW_HIDDEN,
michael@0 2218 NS_STYLE_OVERFLOW_HIDDEN);
michael@0 2219 else
michael@0 2220 aPresContext->SetViewportOverflowOverride(aDisplay->mOverflowX,
michael@0 2221 aDisplay->mOverflowY);
michael@0 2222 return true;
michael@0 2223 }
michael@0 2224
michael@0 2225 /**
michael@0 2226 * This checks the root element and the HTML BODY, if any, for an "overflow" property
michael@0 2227 * that should be applied to the viewport. If one is found then we return the
michael@0 2228 * element that we took the overflow from (which should then be treated as
michael@0 2229 * "overflow:visible"), and we store the overflow style in the prescontext.
michael@0 2230 * @return if scroll was propagated from some content node, the content node it
michael@0 2231 * was propagated from.
michael@0 2232 */
michael@0 2233 nsIContent*
michael@0 2234 nsCSSFrameConstructor::PropagateScrollToViewport()
michael@0 2235 {
michael@0 2236 // Set default
michael@0 2237 nsPresContext* presContext = mPresShell->GetPresContext();
michael@0 2238 presContext->SetViewportOverflowOverride(NS_STYLE_OVERFLOW_AUTO,
michael@0 2239 NS_STYLE_OVERFLOW_AUTO);
michael@0 2240
michael@0 2241 // We never mess with the viewport scroll state
michael@0 2242 // when printing or in print preview
michael@0 2243 if (presContext->IsPaginated()) {
michael@0 2244 return nullptr;
michael@0 2245 }
michael@0 2246
michael@0 2247 Element* docElement = mDocument->GetRootElement();
michael@0 2248
michael@0 2249 // Check the style on the document root element
michael@0 2250 nsStyleSet *styleSet = mPresShell->StyleSet();
michael@0 2251 nsRefPtr<nsStyleContext> rootStyle;
michael@0 2252 rootStyle = styleSet->ResolveStyleFor(docElement, nullptr);
michael@0 2253 if (CheckOverflow(presContext, rootStyle->StyleDisplay())) {
michael@0 2254 // tell caller we stole the overflow style from the root element
michael@0 2255 return docElement;
michael@0 2256 }
michael@0 2257
michael@0 2258 // Don't look in the BODY for non-HTML documents or HTML documents
michael@0 2259 // with non-HTML roots
michael@0 2260 // XXX this should be earlier; we shouldn't even look at the document root
michael@0 2261 // for non-HTML documents. Fix this once we support explicit CSS styling
michael@0 2262 // of the viewport
michael@0 2263 // XXX what about XHTML?
michael@0 2264 nsCOMPtr<nsIDOMHTMLDocument> htmlDoc(do_QueryInterface(mDocument));
michael@0 2265 if (!htmlDoc || !docElement->IsHTML()) {
michael@0 2266 return nullptr;
michael@0 2267 }
michael@0 2268
michael@0 2269 nsCOMPtr<nsIDOMHTMLElement> body;
michael@0 2270 htmlDoc->GetBody(getter_AddRefs(body));
michael@0 2271 nsCOMPtr<nsIContent> bodyElement = do_QueryInterface(body);
michael@0 2272
michael@0 2273 if (!bodyElement ||
michael@0 2274 !bodyElement->NodeInfo()->Equals(nsGkAtoms::body)) {
michael@0 2275 // The body is not a <body> tag, it's a <frameset>.
michael@0 2276 return nullptr;
michael@0 2277 }
michael@0 2278
michael@0 2279 nsRefPtr<nsStyleContext> bodyStyle;
michael@0 2280 bodyStyle = styleSet->ResolveStyleFor(bodyElement->AsElement(), rootStyle);
michael@0 2281
michael@0 2282 if (CheckOverflow(presContext, bodyStyle->StyleDisplay())) {
michael@0 2283 // tell caller we stole the overflow style from the body element
michael@0 2284 return bodyElement;
michael@0 2285 }
michael@0 2286
michael@0 2287 return nullptr;
michael@0 2288 }
michael@0 2289
michael@0 2290 nsIFrame*
michael@0 2291 nsCSSFrameConstructor::ConstructDocElementFrame(Element* aDocElement,
michael@0 2292 nsILayoutHistoryState* aFrameState)
michael@0 2293 {
michael@0 2294 NS_PRECONDITION(mFixedContainingBlock,
michael@0 2295 "No viewport? Someone forgot to call ConstructRootFrame!");
michael@0 2296 NS_PRECONDITION(mFixedContainingBlock == GetRootFrame(),
michael@0 2297 "Unexpected mFixedContainingBlock");
michael@0 2298 NS_PRECONDITION(!mDocElementContainingBlock,
michael@0 2299 "Shouldn't have a doc element containing block here");
michael@0 2300
michael@0 2301 // Make sure to call PropagateScrollToViewport before
michael@0 2302 // SetUpDocElementContainingBlock, since it sets up our scrollbar state
michael@0 2303 // properly.
michael@0 2304 #ifdef DEBUG
michael@0 2305 nsIContent* propagatedScrollFrom =
michael@0 2306 #endif
michael@0 2307 PropagateScrollToViewport();
michael@0 2308
michael@0 2309 SetUpDocElementContainingBlock(aDocElement);
michael@0 2310
michael@0 2311 NS_ASSERTION(mDocElementContainingBlock, "Should have parent by now");
michael@0 2312
michael@0 2313 nsFrameConstructorState state(mPresShell, mFixedContainingBlock, nullptr,
michael@0 2314 nullptr, aFrameState);
michael@0 2315 // Initialize the ancestor filter with null for now; we'll push
michael@0 2316 // aDocElement once we finish resolving style for it.
michael@0 2317 state.mTreeMatchContext.InitAncestors(nullptr);
michael@0 2318
michael@0 2319 // XXXbz why, exactly?
michael@0 2320 if (!mTempFrameTreeState)
michael@0 2321 state.mPresShell->CaptureHistoryState(getter_AddRefs(mTempFrameTreeState));
michael@0 2322
michael@0 2323 // Make sure that we'll handle restyles for this document element in
michael@0 2324 // the future. We need this, because the document element might
michael@0 2325 // have stale restyle bits from a previous frame constructor for
michael@0 2326 // this document. Unlike in AddFrameConstructionItems, it's safe to
michael@0 2327 // unset all element restyle flags, since we don't have any
michael@0 2328 // siblings.
michael@0 2329 aDocElement->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
michael@0 2330
michael@0 2331 // --------- CREATE AREA OR BOX FRAME -------
michael@0 2332 nsRefPtr<nsStyleContext> styleContext;
michael@0 2333 styleContext = mPresShell->StyleSet()->ResolveStyleFor(aDocElement,
michael@0 2334 nullptr);
michael@0 2335
michael@0 2336 const nsStyleDisplay* display = styleContext->StyleDisplay();
michael@0 2337
michael@0 2338 // Ensure that our XBL bindings are installed.
michael@0 2339 if (display->mBinding) {
michael@0 2340 // Get the XBL loader.
michael@0 2341 nsresult rv;
michael@0 2342 bool resolveStyle;
michael@0 2343
michael@0 2344 nsXBLService* xblService = nsXBLService::GetInstance();
michael@0 2345 if (!xblService) {
michael@0 2346 return nullptr;
michael@0 2347 }
michael@0 2348
michael@0 2349 nsRefPtr<nsXBLBinding> binding;
michael@0 2350 rv = xblService->LoadBindings(aDocElement, display->mBinding->GetURI(),
michael@0 2351 display->mBinding->mOriginPrincipal,
michael@0 2352 getter_AddRefs(binding), &resolveStyle);
michael@0 2353 if (NS_FAILED(rv) && rv != NS_ERROR_XBL_BLOCKED)
michael@0 2354 return nullptr; // Binding will load asynchronously.
michael@0 2355
michael@0 2356 if (binding) {
michael@0 2357 // For backwards compat, keep firing the root's constructor
michael@0 2358 // after all of its kids' constructors. So tell the binding
michael@0 2359 // manager about it right now.
michael@0 2360 mDocument->BindingManager()->AddToAttachedQueue(binding);
michael@0 2361 }
michael@0 2362
michael@0 2363 if (resolveStyle) {
michael@0 2364 styleContext = mPresShell->StyleSet()->ResolveStyleFor(aDocElement,
michael@0 2365 nullptr);
michael@0 2366 display = styleContext->StyleDisplay();
michael@0 2367 }
michael@0 2368 }
michael@0 2369
michael@0 2370 // --------- IF SCROLLABLE WRAP IN SCROLLFRAME --------
michael@0 2371
michael@0 2372 #ifdef DEBUG
michael@0 2373 NS_ASSERTION(!display->IsScrollableOverflow() ||
michael@0 2374 state.mPresContext->IsPaginated() ||
michael@0 2375 propagatedScrollFrom == aDocElement,
michael@0 2376 "Scrollbars should have been propagated to the viewport");
michael@0 2377 #endif
michael@0 2378
michael@0 2379 if (MOZ_UNLIKELY(display->mDisplay == NS_STYLE_DISPLAY_NONE)) {
michael@0 2380 SetUndisplayedContent(aDocElement, styleContext);
michael@0 2381 return nullptr;
michael@0 2382 }
michael@0 2383
michael@0 2384 TreeMatchContext::AutoAncestorPusher ancestorPusher(state.mTreeMatchContext);
michael@0 2385 ancestorPusher.PushAncestorAndStyleScope(aDocElement);
michael@0 2386
michael@0 2387 // Make sure to start any background image loads for the root element now.
michael@0 2388 styleContext->StartBackgroundImageLoads();
michael@0 2389
michael@0 2390 nsFrameConstructorSaveState absoluteSaveState;
michael@0 2391 if (mHasRootAbsPosContainingBlock) {
michael@0 2392 // Push the absolute containing block now so we can absolutely position
michael@0 2393 // the root element
michael@0 2394 mDocElementContainingBlock->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
michael@0 2395 state.PushAbsoluteContainingBlock(mDocElementContainingBlock,
michael@0 2396 mDocElementContainingBlock,
michael@0 2397 absoluteSaveState);
michael@0 2398 }
michael@0 2399
michael@0 2400 // The rules from CSS 2.1, section 9.2.4, have already been applied
michael@0 2401 // by the style system, so we can assume that display->mDisplay is
michael@0 2402 // either NONE, BLOCK, or TABLE.
michael@0 2403
michael@0 2404 // contentFrame is the primary frame for the root element. newFrame
michael@0 2405 // is the frame that will be the child of the initial containing block.
michael@0 2406 // These are usually the same frame but they can be different, in
michael@0 2407 // particular if the root frame is positioned, in which case
michael@0 2408 // contentFrame is the out-of-flow frame and newFrame is the
michael@0 2409 // placeholder.
michael@0 2410 nsIFrame* contentFrame;
michael@0 2411 nsIFrame* newFrame;
michael@0 2412 bool processChildren = false;
michael@0 2413
michael@0 2414 // Check whether we need to build a XUL box or SVG root frame
michael@0 2415 #ifdef MOZ_XUL
michael@0 2416 if (aDocElement->IsXUL()) {
michael@0 2417 contentFrame = NS_NewDocElementBoxFrame(mPresShell, styleContext);
michael@0 2418 InitAndRestoreFrame(state, aDocElement, mDocElementContainingBlock,
michael@0 2419 contentFrame);
michael@0 2420 newFrame = contentFrame;
michael@0 2421 processChildren = true;
michael@0 2422 }
michael@0 2423 else
michael@0 2424 #endif
michael@0 2425 if (aDocElement->IsSVG()) {
michael@0 2426 if (aDocElement->Tag() != nsGkAtoms::svg) {
michael@0 2427 return nullptr;
michael@0 2428 }
michael@0 2429 // We're going to call the right function ourselves, so no need to give a
michael@0 2430 // function to this FrameConstructionData.
michael@0 2431
michael@0 2432 // XXXbz on the other hand, if we converted this whole function to
michael@0 2433 // FrameConstructionData/Item, then we'd need the right function
michael@0 2434 // here... but would probably be able to get away with less code in this
michael@0 2435 // function in general.
michael@0 2436 // Use a null PendingBinding, since our binding is not in fact pending.
michael@0 2437 static const FrameConstructionData rootSVGData = FCDATA_DECL(0, nullptr);
michael@0 2438 already_AddRefed<nsStyleContext> extraRef =
michael@0 2439 nsRefPtr<nsStyleContext>(styleContext).forget();
michael@0 2440 FrameConstructionItem item(&rootSVGData, aDocElement,
michael@0 2441 aDocElement->Tag(), kNameSpaceID_SVG,
michael@0 2442 nullptr, extraRef, true, nullptr);
michael@0 2443
michael@0 2444 nsFrameItems frameItems;
michael@0 2445 contentFrame = ConstructOuterSVG(state, item, mDocElementContainingBlock,
michael@0 2446 styleContext->StyleDisplay(),
michael@0 2447 frameItems);
michael@0 2448 newFrame = frameItems.FirstChild();
michael@0 2449 NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
michael@0 2450 } else if (display->mDisplay == NS_STYLE_DISPLAY_FLEX) {
michael@0 2451 contentFrame = NS_NewFlexContainerFrame(mPresShell, styleContext);
michael@0 2452 InitAndRestoreFrame(state, aDocElement, mDocElementContainingBlock,
michael@0 2453 contentFrame);
michael@0 2454 newFrame = contentFrame;
michael@0 2455 processChildren = true;
michael@0 2456 } else if (display->mDisplay == NS_STYLE_DISPLAY_GRID) {
michael@0 2457 contentFrame = NS_NewGridContainerFrame(mPresShell, styleContext);
michael@0 2458 InitAndRestoreFrame(state, aDocElement, mDocElementContainingBlock,
michael@0 2459 contentFrame);
michael@0 2460 newFrame = contentFrame;
michael@0 2461 processChildren = true;
michael@0 2462 } else if (display->mDisplay == NS_STYLE_DISPLAY_TABLE) {
michael@0 2463 // We're going to call the right function ourselves, so no need to give a
michael@0 2464 // function to this FrameConstructionData.
michael@0 2465
michael@0 2466 // XXXbz on the other hand, if we converted this whole function to
michael@0 2467 // FrameConstructionData/Item, then we'd need the right function
michael@0 2468 // here... but would probably be able to get away with less code in this
michael@0 2469 // function in general.
michael@0 2470 // Use a null PendingBinding, since our binding is not in fact pending.
michael@0 2471 static const FrameConstructionData rootTableData = FCDATA_DECL(0, nullptr);
michael@0 2472 already_AddRefed<nsStyleContext> extraRef =
michael@0 2473 nsRefPtr<nsStyleContext>(styleContext).forget();
michael@0 2474 FrameConstructionItem item(&rootTableData, aDocElement,
michael@0 2475 aDocElement->Tag(), kNameSpaceID_None,
michael@0 2476 nullptr, extraRef, true, nullptr);
michael@0 2477
michael@0 2478 nsFrameItems frameItems;
michael@0 2479 // if the document is a table then just populate it.
michael@0 2480 contentFrame = ConstructTable(state, item, mDocElementContainingBlock,
michael@0 2481 styleContext->StyleDisplay(),
michael@0 2482 frameItems);
michael@0 2483 newFrame = frameItems.FirstChild();
michael@0 2484 NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
michael@0 2485 } else {
michael@0 2486 MOZ_ASSERT(display->mDisplay == NS_STYLE_DISPLAY_BLOCK,
michael@0 2487 "Unhandled display type for root element");
michael@0 2488 contentFrame = NS_NewBlockFormattingContext(mPresShell, styleContext);
michael@0 2489 nsFrameItems frameItems;
michael@0 2490 // Use a null PendingBinding, since our binding is not in fact pending.
michael@0 2491 ConstructBlock(state, display, aDocElement,
michael@0 2492 state.GetGeometricParent(display,
michael@0 2493 mDocElementContainingBlock),
michael@0 2494 mDocElementContainingBlock, styleContext,
michael@0 2495 &contentFrame, frameItems,
michael@0 2496 display->IsPositioned(contentFrame) ? contentFrame : nullptr,
michael@0 2497 nullptr);
michael@0 2498 newFrame = frameItems.FirstChild();
michael@0 2499 NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
michael@0 2500 }
michael@0 2501
michael@0 2502 MOZ_ASSERT(newFrame);
michael@0 2503 MOZ_ASSERT(contentFrame);
michael@0 2504
michael@0 2505 NS_ASSERTION(processChildren ? !mRootElementFrame :
michael@0 2506 mRootElementFrame == contentFrame,
michael@0 2507 "unexpected mRootElementFrame");
michael@0 2508 mRootElementFrame = contentFrame;
michael@0 2509
michael@0 2510 // Figure out which frame has the main style for the document element,
michael@0 2511 // assigning it to mRootElementStyleFrame.
michael@0 2512 // Backgrounds should be propagated from that frame to the viewport.
michael@0 2513 mRootElementStyleFrame = contentFrame->GetParentStyleContextFrame();
michael@0 2514 bool isChild = mRootElementStyleFrame &&
michael@0 2515 mRootElementStyleFrame->GetParent() == contentFrame;
michael@0 2516 if (!isChild) {
michael@0 2517 mRootElementStyleFrame = mRootElementFrame;
michael@0 2518 }
michael@0 2519
michael@0 2520 if (processChildren) {
michael@0 2521 // Still need to process the child content
michael@0 2522 nsFrameItems childItems;
michael@0 2523
michael@0 2524 NS_ASSERTION(!nsLayoutUtils::GetAsBlock(contentFrame) &&
michael@0 2525 !contentFrame->IsFrameOfType(nsIFrame::eSVG),
michael@0 2526 "Only XUL frames should reach here");
michael@0 2527 // Use a null PendingBinding, since our binding is not in fact pending.
michael@0 2528 ProcessChildren(state, aDocElement, styleContext, contentFrame, true,
michael@0 2529 childItems, false, nullptr);
michael@0 2530
michael@0 2531 // Set the initial child lists
michael@0 2532 contentFrame->SetInitialChildList(kPrincipalList, childItems);
michael@0 2533 }
michael@0 2534
michael@0 2535 // set the primary frame
michael@0 2536 aDocElement->SetPrimaryFrame(contentFrame);
michael@0 2537
michael@0 2538 SetInitialSingleChild(mDocElementContainingBlock, newFrame);
michael@0 2539
michael@0 2540 return newFrame;
michael@0 2541 }
michael@0 2542
michael@0 2543
michael@0 2544 nsIFrame*
michael@0 2545 nsCSSFrameConstructor::ConstructRootFrame()
michael@0 2546 {
michael@0 2547 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
michael@0 2548
michael@0 2549 nsStyleSet *styleSet = mPresShell->StyleSet();
michael@0 2550
michael@0 2551 // Set up our style rule observer.
michael@0 2552 // XXXbz wouldn't this make more sense as part of presshell init?
michael@0 2553 {
michael@0 2554 styleSet->SetBindingManager(mDocument->BindingManager());
michael@0 2555 }
michael@0 2556
michael@0 2557 // --------- BUILD VIEWPORT -----------
michael@0 2558 nsIFrame* viewportFrame = nullptr;
michael@0 2559 nsRefPtr<nsStyleContext> viewportPseudoStyle;
michael@0 2560
michael@0 2561 viewportPseudoStyle =
michael@0 2562 styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::viewport, nullptr);
michael@0 2563
michael@0 2564 viewportFrame = NS_NewViewportFrame(mPresShell, viewportPseudoStyle);
michael@0 2565
michael@0 2566 // XXXbz do we _have_ to pass a null content pointer to that frame?
michael@0 2567 // Would it really kill us to pass in the root element or something?
michael@0 2568 // What would that break?
michael@0 2569 viewportFrame->Init(nullptr, nullptr, nullptr);
michael@0 2570
michael@0 2571 // Bind the viewport frame to the root view
michael@0 2572 nsView* rootView = mPresShell->GetViewManager()->GetRootView();
michael@0 2573 viewportFrame->SetView(rootView);
michael@0 2574
michael@0 2575 nsContainerFrame::SyncFrameViewProperties(mPresShell->GetPresContext(), viewportFrame,
michael@0 2576 viewportPseudoStyle, rootView);
michael@0 2577 nsContainerFrame::SyncWindowProperties(mPresShell->GetPresContext(), viewportFrame,
michael@0 2578 rootView);
michael@0 2579
michael@0 2580 // The viewport is the containing block for 'fixed' elements
michael@0 2581 mFixedContainingBlock = viewportFrame;
michael@0 2582 // Make it an absolute container for fixed-pos elements
michael@0 2583 mFixedContainingBlock->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
michael@0 2584 mFixedContainingBlock->MarkAsAbsoluteContainingBlock();
michael@0 2585
michael@0 2586 return viewportFrame;
michael@0 2587 }
michael@0 2588
michael@0 2589 void
michael@0 2590 nsCSSFrameConstructor::SetUpDocElementContainingBlock(nsIContent* aDocElement)
michael@0 2591 {
michael@0 2592 NS_PRECONDITION(aDocElement, "No element?");
michael@0 2593 NS_PRECONDITION(!aDocElement->GetParent(), "Not root content?");
michael@0 2594 NS_PRECONDITION(aDocElement->GetCurrentDoc(), "Not in a document?");
michael@0 2595 NS_PRECONDITION(aDocElement->GetCurrentDoc()->GetRootElement() ==
michael@0 2596 aDocElement, "Not the root of the document?");
michael@0 2597
michael@0 2598 /*
michael@0 2599 how the root frame hierarchy should look
michael@0 2600
michael@0 2601 Galley presentation, non-XUL, with scrolling:
michael@0 2602
michael@0 2603 ViewportFrame [fixed-cb]
michael@0 2604 nsHTMLScrollFrame
michael@0 2605 nsCanvasFrame [abs-cb]
michael@0 2606 root element frame (nsBlockFrame, nsSVGOuterSVGFrame,
michael@0 2607 nsTableOuterFrame, nsPlaceholderFrame)
michael@0 2608
michael@0 2609 Galley presentation, XUL
michael@0 2610
michael@0 2611 ViewportFrame [fixed-cb]
michael@0 2612 nsRootBoxFrame
michael@0 2613 root element frame (nsDocElementBoxFrame)
michael@0 2614
michael@0 2615 Print presentation, non-XUL
michael@0 2616
michael@0 2617 ViewportFrame
michael@0 2618 nsSimplePageSequenceFrame
michael@0 2619 nsPageFrame [fixed-cb]
michael@0 2620 nsPageContentFrame
michael@0 2621 nsCanvasFrame [abs-cb]
michael@0 2622 root element frame (nsBlockFrame, nsSVGOuterSVGFrame,
michael@0 2623 nsTableOuterFrame, nsPlaceholderFrame)
michael@0 2624
michael@0 2625 Print-preview presentation, non-XUL
michael@0 2626
michael@0 2627 ViewportFrame
michael@0 2628 nsHTMLScrollFrame
michael@0 2629 nsSimplePageSequenceFrame
michael@0 2630 nsPageFrame [fixed-cb]
michael@0 2631 nsPageContentFrame
michael@0 2632 nsCanvasFrame [abs-cb]
michael@0 2633 root element frame (nsBlockFrame, nsSVGOuterSVGFrame,
michael@0 2634 nsTableOuterFrame, nsPlaceholderFrame)
michael@0 2635
michael@0 2636 Print/print preview of XUL is not supported.
michael@0 2637 [fixed-cb]: the default containing block for fixed-pos content
michael@0 2638 [abs-cb]: the default containing block for abs-pos content
michael@0 2639
michael@0 2640 Meaning of nsCSSFrameConstructor fields:
michael@0 2641 mRootElementFrame is "root element frame". This is the primary frame for
michael@0 2642 the root element.
michael@0 2643 mDocElementContainingBlock is the parent of mRootElementFrame
michael@0 2644 (i.e. nsCanvasFrame or nsRootBoxFrame)
michael@0 2645 mFixedContainingBlock is the [fixed-cb]
michael@0 2646 mGfxScrollFrame is the nsHTMLScrollFrame mentioned above, or null if there isn't one
michael@0 2647 mPageSequenceFrame is the nsSimplePageSequenceFrame, or null if there isn't one
michael@0 2648 */
michael@0 2649
michael@0 2650 // --------- CREATE ROOT FRAME -------
michael@0 2651
michael@0 2652
michael@0 2653 // Create the root frame. The document element's frame is a child of the
michael@0 2654 // root frame.
michael@0 2655 //
michael@0 2656 // The root frame serves two purposes:
michael@0 2657 // - reserves space for any margins needed for the document element's frame
michael@0 2658 // - renders the document element's background. This ensures the background covers
michael@0 2659 // the entire canvas as specified by the CSS2 spec
michael@0 2660
michael@0 2661 nsPresContext* presContext = mPresShell->GetPresContext();
michael@0 2662 bool isPaginated = presContext->IsRootPaginatedDocument();
michael@0 2663 nsIFrame* viewportFrame = mFixedContainingBlock;
michael@0 2664 nsStyleContext* viewportPseudoStyle = viewportFrame->StyleContext();
michael@0 2665
michael@0 2666 nsIFrame* rootFrame = nullptr;
michael@0 2667 nsIAtom* rootPseudo;
michael@0 2668
michael@0 2669 if (!isPaginated) {
michael@0 2670 #ifdef MOZ_XUL
michael@0 2671 if (aDocElement->IsXUL())
michael@0 2672 {
michael@0 2673 // pass a temporary stylecontext, the correct one will be set later
michael@0 2674 rootFrame = NS_NewRootBoxFrame(mPresShell, viewportPseudoStyle);
michael@0 2675 } else
michael@0 2676 #endif
michael@0 2677 {
michael@0 2678 // pass a temporary stylecontext, the correct one will be set later
michael@0 2679 rootFrame = NS_NewCanvasFrame(mPresShell, viewportPseudoStyle);
michael@0 2680 mHasRootAbsPosContainingBlock = true;
michael@0 2681 }
michael@0 2682
michael@0 2683 rootPseudo = nsCSSAnonBoxes::canvas;
michael@0 2684 mDocElementContainingBlock = rootFrame;
michael@0 2685 } else {
michael@0 2686 // Create a page sequence frame
michael@0 2687 rootFrame = NS_NewSimplePageSequenceFrame(mPresShell, viewportPseudoStyle);
michael@0 2688 mPageSequenceFrame = rootFrame;
michael@0 2689 rootPseudo = nsCSSAnonBoxes::pageSequence;
michael@0 2690 }
michael@0 2691
michael@0 2692
michael@0 2693 // --------- IF SCROLLABLE WRAP IN SCROLLFRAME --------
michael@0 2694
michael@0 2695 // If the device supports scrolling (e.g., in galley mode on the screen and
michael@0 2696 // for print-preview, but not when printing), then create a scroll frame that
michael@0 2697 // will act as the scrolling mechanism for the viewport.
michael@0 2698 // XXX Do we even need a viewport when printing to a printer?
michael@0 2699
michael@0 2700 bool isHTML = aDocElement->IsHTML();
michael@0 2701 bool isXUL = false;
michael@0 2702
michael@0 2703 if (!isHTML) {
michael@0 2704 isXUL = aDocElement->IsXUL();
michael@0 2705 }
michael@0 2706
michael@0 2707 // Never create scrollbars for XUL documents
michael@0 2708 bool isScrollable = isPaginated ? presContext->HasPaginatedScrolling() : !isXUL;
michael@0 2709
michael@0 2710 // We no longer need to do overflow propagation here. It's taken care of
michael@0 2711 // when we construct frames for the element whose overflow might be
michael@0 2712 // propagated
michael@0 2713 NS_ASSERTION(!isScrollable || !isXUL,
michael@0 2714 "XUL documents should never be scrollable - see above");
michael@0 2715
michael@0 2716 nsIFrame* newFrame = rootFrame;
michael@0 2717 nsRefPtr<nsStyleContext> rootPseudoStyle;
michael@0 2718 // we must create a state because if the scrollbars are GFX it needs the
michael@0 2719 // state to build the scrollbar frames.
michael@0 2720 nsFrameConstructorState state(mPresShell, nullptr, nullptr, nullptr);
michael@0 2721
michael@0 2722 // Start off with the viewport as parent; we'll adjust it as needed.
michael@0 2723 nsIFrame* parentFrame = viewportFrame;
michael@0 2724
michael@0 2725 nsStyleSet* styleSet = mPresShell->StyleSet();
michael@0 2726 // If paginated, make sure we don't put scrollbars in
michael@0 2727 if (!isScrollable) {
michael@0 2728 rootPseudoStyle = styleSet->ResolveAnonymousBoxStyle(rootPseudo,
michael@0 2729 viewportPseudoStyle);
michael@0 2730 } else {
michael@0 2731 if (rootPseudo == nsCSSAnonBoxes::canvas) {
michael@0 2732 rootPseudo = nsCSSAnonBoxes::scrolledCanvas;
michael@0 2733 } else {
michael@0 2734 NS_ASSERTION(rootPseudo == nsCSSAnonBoxes::pageSequence,
michael@0 2735 "Unknown root pseudo");
michael@0 2736 rootPseudo = nsCSSAnonBoxes::scrolledPageSequence;
michael@0 2737 }
michael@0 2738
michael@0 2739 // Build the frame. We give it the content we are wrapping which is the
michael@0 2740 // document element, the root frame, the parent view port frame, and we
michael@0 2741 // should get back the new frame and the scrollable view if one was
michael@0 2742 // created.
michael@0 2743
michael@0 2744 // resolve a context for the scrollframe
michael@0 2745 nsRefPtr<nsStyleContext> styleContext;
michael@0 2746 styleContext = styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::viewportScroll,
michael@0 2747 viewportPseudoStyle);
michael@0 2748
michael@0 2749 // Note that the viewport scrollframe is always built with
michael@0 2750 // overflow:auto style. This forces the scroll frame to create
michael@0 2751 // anonymous content for both scrollbars. This is necessary even
michael@0 2752 // if the HTML or BODY elements are overriding the viewport
michael@0 2753 // scroll style to 'hidden' --- dynamic style changes might put
michael@0 2754 // scrollbars back on the viewport and we don't want to have to
michael@0 2755 // reframe the viewport to create the scrollbar content.
michael@0 2756 newFrame = nullptr;
michael@0 2757 rootPseudoStyle = BeginBuildingScrollFrame( state,
michael@0 2758 aDocElement,
michael@0 2759 styleContext,
michael@0 2760 viewportFrame,
michael@0 2761 rootPseudo,
michael@0 2762 true,
michael@0 2763 newFrame);
michael@0 2764 parentFrame = newFrame;
michael@0 2765 mGfxScrollFrame = newFrame;
michael@0 2766 }
michael@0 2767
michael@0 2768 rootFrame->SetStyleContextWithoutNotification(rootPseudoStyle);
michael@0 2769 rootFrame->Init(aDocElement, parentFrame, nullptr);
michael@0 2770
michael@0 2771 if (isScrollable) {
michael@0 2772 FinishBuildingScrollFrame(parentFrame, rootFrame);
michael@0 2773 }
michael@0 2774
michael@0 2775 if (isPaginated) { // paginated
michael@0 2776 // Create the first page
michael@0 2777 // Set the initial child lists
michael@0 2778 nsIFrame* canvasFrame;
michael@0 2779 nsIFrame* pageFrame =
michael@0 2780 ConstructPageFrame(mPresShell, presContext, rootFrame, nullptr,
michael@0 2781 canvasFrame);
michael@0 2782 SetInitialSingleChild(rootFrame, pageFrame);
michael@0 2783
michael@0 2784 // The eventual parent of the document element frame.
michael@0 2785 // XXX should this be set for every new page (in ConstructPageFrame)?
michael@0 2786 mDocElementContainingBlock = canvasFrame;
michael@0 2787 mHasRootAbsPosContainingBlock = true;
michael@0 2788 }
michael@0 2789
michael@0 2790 if (viewportFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
michael@0 2791 SetInitialSingleChild(viewportFrame, newFrame);
michael@0 2792 } else {
michael@0 2793 nsFrameList newFrameList(newFrame, newFrame);
michael@0 2794 viewportFrame->AppendFrames(kPrincipalList, newFrameList);
michael@0 2795 }
michael@0 2796 }
michael@0 2797
michael@0 2798 nsIFrame*
michael@0 2799 nsCSSFrameConstructor::ConstructPageFrame(nsIPresShell* aPresShell,
michael@0 2800 nsPresContext* aPresContext,
michael@0 2801 nsIFrame* aParentFrame,
michael@0 2802 nsIFrame* aPrevPageFrame,
michael@0 2803 nsIFrame*& aCanvasFrame)
michael@0 2804 {
michael@0 2805 nsStyleContext* parentStyleContext = aParentFrame->StyleContext();
michael@0 2806 nsStyleSet *styleSet = aPresShell->StyleSet();
michael@0 2807
michael@0 2808 nsRefPtr<nsStyleContext> pagePseudoStyle;
michael@0 2809 pagePseudoStyle = styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::page,
michael@0 2810 parentStyleContext);
michael@0 2811
michael@0 2812 nsIFrame* pageFrame = NS_NewPageFrame(aPresShell, pagePseudoStyle);
michael@0 2813
michael@0 2814 // Initialize the page frame and force it to have a view. This makes printing of
michael@0 2815 // the pages easier and faster.
michael@0 2816 pageFrame->Init(nullptr, aParentFrame, aPrevPageFrame);
michael@0 2817
michael@0 2818 nsRefPtr<nsStyleContext> pageContentPseudoStyle;
michael@0 2819 pageContentPseudoStyle =
michael@0 2820 styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::pageContent,
michael@0 2821 pagePseudoStyle);
michael@0 2822
michael@0 2823 nsIFrame* pageContentFrame = NS_NewPageContentFrame(aPresShell, pageContentPseudoStyle);
michael@0 2824
michael@0 2825 // Initialize the page content frame and force it to have a view. Also make it the
michael@0 2826 // containing block for fixed elements which are repeated on every page.
michael@0 2827 nsIFrame* prevPageContentFrame = nullptr;
michael@0 2828 if (aPrevPageFrame) {
michael@0 2829 prevPageContentFrame = aPrevPageFrame->GetFirstPrincipalChild();
michael@0 2830 NS_ASSERTION(prevPageContentFrame, "missing page content frame");
michael@0 2831 }
michael@0 2832 pageContentFrame->Init(nullptr, pageFrame, prevPageContentFrame);
michael@0 2833 SetInitialSingleChild(pageFrame, pageContentFrame);
michael@0 2834 mFixedContainingBlock = pageContentFrame;
michael@0 2835 // Make it an absolute container for fixed-pos elements
michael@0 2836 mFixedContainingBlock->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
michael@0 2837 mFixedContainingBlock->MarkAsAbsoluteContainingBlock();
michael@0 2838
michael@0 2839 nsRefPtr<nsStyleContext> canvasPseudoStyle;
michael@0 2840 canvasPseudoStyle = styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::canvas,
michael@0 2841 pageContentPseudoStyle);
michael@0 2842
michael@0 2843 aCanvasFrame = NS_NewCanvasFrame(aPresShell, canvasPseudoStyle);
michael@0 2844
michael@0 2845 nsIFrame* prevCanvasFrame = nullptr;
michael@0 2846 if (prevPageContentFrame) {
michael@0 2847 prevCanvasFrame = prevPageContentFrame->GetFirstPrincipalChild();
michael@0 2848 NS_ASSERTION(prevCanvasFrame, "missing canvas frame");
michael@0 2849 }
michael@0 2850 aCanvasFrame->Init(nullptr, pageContentFrame, prevCanvasFrame);
michael@0 2851 SetInitialSingleChild(pageContentFrame, aCanvasFrame);
michael@0 2852 return pageFrame;
michael@0 2853 }
michael@0 2854
michael@0 2855 /* static */
michael@0 2856 nsIFrame*
michael@0 2857 nsCSSFrameConstructor::CreatePlaceholderFrameFor(nsIPresShell* aPresShell,
michael@0 2858 nsIContent* aContent,
michael@0 2859 nsIFrame* aFrame,
michael@0 2860 nsStyleContext* aStyleContext,
michael@0 2861 nsIFrame* aParentFrame,
michael@0 2862 nsIFrame* aPrevInFlow,
michael@0 2863 nsFrameState aTypeBit)
michael@0 2864 {
michael@0 2865 nsRefPtr<nsStyleContext> placeholderStyle = aPresShell->StyleSet()->
michael@0 2866 ResolveStyleForNonElement(aStyleContext->GetParent());
michael@0 2867
michael@0 2868 // The placeholder frame gets a pseudo style context
michael@0 2869 nsPlaceholderFrame* placeholderFrame =
michael@0 2870 (nsPlaceholderFrame*)NS_NewPlaceholderFrame(aPresShell, placeholderStyle,
michael@0 2871 aTypeBit);
michael@0 2872
michael@0 2873 placeholderFrame->Init(aContent, aParentFrame, aPrevInFlow);
michael@0 2874
michael@0 2875 // The placeholder frame has a pointer back to the out-of-flow frame
michael@0 2876 placeholderFrame->SetOutOfFlowFrame(aFrame);
michael@0 2877
michael@0 2878 aFrame->AddStateBits(NS_FRAME_OUT_OF_FLOW);
michael@0 2879
michael@0 2880 // Add mapping from absolutely positioned frame to its placeholder frame
michael@0 2881 aPresShell->FrameManager()->RegisterPlaceholderFrame(placeholderFrame);
michael@0 2882
michael@0 2883 return placeholderFrame;
michael@0 2884 }
michael@0 2885
michael@0 2886 // Clears any lazy bits set in the range [aStartContent, aEndContent). If
michael@0 2887 // aEndContent is null, that means to clear bits in all siblings starting with
michael@0 2888 // aStartContent. aStartContent must not be null unless aEndContent is also
michael@0 2889 // null. We do this so that when new children are inserted under elements whose
michael@0 2890 // frame is a leaf the new children don't cause us to try to construct frames
michael@0 2891 // for the existing children again.
michael@0 2892 static inline void
michael@0 2893 ClearLazyBits(nsIContent* aStartContent, nsIContent* aEndContent)
michael@0 2894 {
michael@0 2895 NS_PRECONDITION(aStartContent || !aEndContent,
michael@0 2896 "Must have start child if we have an end child");
michael@0 2897 for (nsIContent* cur = aStartContent; cur != aEndContent;
michael@0 2898 cur = cur->GetNextSibling()) {
michael@0 2899 cur->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
michael@0 2900 }
michael@0 2901 }
michael@0 2902
michael@0 2903 nsIFrame*
michael@0 2904 nsCSSFrameConstructor::ConstructSelectFrame(nsFrameConstructorState& aState,
michael@0 2905 FrameConstructionItem& aItem,
michael@0 2906 nsIFrame* aParentFrame,
michael@0 2907 const nsStyleDisplay* aStyleDisplay,
michael@0 2908 nsFrameItems& aFrameItems)
michael@0 2909 {
michael@0 2910 nsIContent* const content = aItem.mContent;
michael@0 2911 nsStyleContext* const styleContext = aItem.mStyleContext;
michael@0 2912
michael@0 2913 // Construct a frame-based listbox or combobox
michael@0 2914 dom::HTMLSelectElement* sel = dom::HTMLSelectElement::FromContent(content);
michael@0 2915 MOZ_ASSERT(sel);
michael@0 2916 if (sel->IsCombobox()) {
michael@0 2917 // Construct a frame-based combo box.
michael@0 2918 // The frame-based combo box is built out of three parts. A display area, a button and
michael@0 2919 // a dropdown list. The display area and button are created through anonymous content.
michael@0 2920 // The drop-down list's frame is created explicitly. The combobox frame shares its content
michael@0 2921 // with the drop-down list.
michael@0 2922 nsFrameState flags = NS_BLOCK_FLOAT_MGR;
michael@0 2923 nsIFrame* comboboxFrame = NS_NewComboboxControlFrame(mPresShell, styleContext, flags);
michael@0 2924
michael@0 2925 // Save the history state so we don't restore during construction
michael@0 2926 // since the complete tree is required before we restore.
michael@0 2927 nsILayoutHistoryState *historyState = aState.mFrameState;
michael@0 2928 aState.mFrameState = nullptr;
michael@0 2929 // Initialize the combobox frame
michael@0 2930 InitAndRestoreFrame(aState, content,
michael@0 2931 aState.GetGeometricParent(aStyleDisplay, aParentFrame),
michael@0 2932 comboboxFrame);
michael@0 2933
michael@0 2934 aState.AddChild(comboboxFrame, aFrameItems, content, styleContext,
michael@0 2935 aParentFrame);
michael@0 2936
michael@0 2937 nsIComboboxControlFrame* comboBox = do_QueryFrame(comboboxFrame);
michael@0 2938 NS_ASSERTION(comboBox, "NS_NewComboboxControlFrame returned frame that "
michael@0 2939 "doesn't implement nsIComboboxControlFrame");
michael@0 2940
michael@0 2941 // Resolve pseudo element style for the dropdown list
michael@0 2942 nsRefPtr<nsStyleContext> listStyle;
michael@0 2943 listStyle = mPresShell->StyleSet()->
michael@0 2944 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::dropDownList, styleContext);
michael@0 2945
michael@0 2946 // Create a listbox
michael@0 2947 nsIFrame* listFrame = NS_NewListControlFrame(mPresShell, listStyle);
michael@0 2948
michael@0 2949 // Notify the listbox that it is being used as a dropdown list.
michael@0 2950 nsIListControlFrame * listControlFrame = do_QueryFrame(listFrame);
michael@0 2951 if (listControlFrame) {
michael@0 2952 listControlFrame->SetComboboxFrame(comboboxFrame);
michael@0 2953 }
michael@0 2954 // Notify combobox that it should use the listbox as it's popup
michael@0 2955 comboBox->SetDropDown(listFrame);
michael@0 2956
michael@0 2957 NS_ASSERTION(!listFrame->IsPositioned(),
michael@0 2958 "Ended up with positioned dropdown list somehow.");
michael@0 2959 NS_ASSERTION(!listFrame->IsFloating(),
michael@0 2960 "Ended up with floating dropdown list somehow.");
michael@0 2961
michael@0 2962 // Initialize the scroll frame positioned. Note that it is NOT
michael@0 2963 // initialized as absolutely positioned.
michael@0 2964 nsIFrame* scrolledFrame = NS_NewSelectsAreaFrame(mPresShell, styleContext, flags);
michael@0 2965
michael@0 2966 InitializeSelectFrame(aState, listFrame, scrolledFrame, content,
michael@0 2967 comboboxFrame, listStyle, true,
michael@0 2968 aItem.mPendingBinding, aFrameItems);
michael@0 2969
michael@0 2970 NS_ASSERTION(listFrame->GetView(), "ListFrame's view is nullptr");
michael@0 2971
michael@0 2972 // Create display and button frames from the combobox's anonymous content.
michael@0 2973 // The anonymous content is appended to existing anonymous content for this
michael@0 2974 // element (the scrollbars).
michael@0 2975
michael@0 2976 nsFrameItems childItems;
michael@0 2977 CreateAnonymousFrames(aState, content, comboboxFrame,
michael@0 2978 aItem.mPendingBinding, childItems);
michael@0 2979
michael@0 2980 comboboxFrame->SetInitialChildList(kPrincipalList, childItems);
michael@0 2981
michael@0 2982 // Initialize the additional popup child list which contains the
michael@0 2983 // dropdown list frame.
michael@0 2984 nsFrameItems popupItems;
michael@0 2985 popupItems.AddChild(listFrame);
michael@0 2986 comboboxFrame->SetInitialChildList(nsIFrame::kSelectPopupList,
michael@0 2987 popupItems);
michael@0 2988
michael@0 2989 aState.mFrameState = historyState;
michael@0 2990 if (aState.mFrameState) {
michael@0 2991 // Restore frame state for the entire subtree of |comboboxFrame|.
michael@0 2992 RestoreFrameState(comboboxFrame, aState.mFrameState);
michael@0 2993 }
michael@0 2994 return comboboxFrame;
michael@0 2995 }
michael@0 2996
michael@0 2997 // Listbox, not combobox
michael@0 2998 nsIFrame* listFrame = NS_NewListControlFrame(mPresShell, styleContext);
michael@0 2999
michael@0 3000 nsIFrame* scrolledFrame = NS_NewSelectsAreaFrame(
michael@0 3001 mPresShell, styleContext, NS_BLOCK_FLOAT_MGR);
michael@0 3002
michael@0 3003 // ******* this code stolen from Initialze ScrollFrame ********
michael@0 3004 // please adjust this code to use BuildScrollFrame.
michael@0 3005
michael@0 3006 InitializeSelectFrame(aState, listFrame, scrolledFrame, content,
michael@0 3007 aParentFrame, styleContext, false,
michael@0 3008 aItem.mPendingBinding, aFrameItems);
michael@0 3009
michael@0 3010 return listFrame;
michael@0 3011 }
michael@0 3012
michael@0 3013 /**
michael@0 3014 * Used to be InitializeScrollFrame but now it's only used for the select tag
michael@0 3015 * But the select tag should really be fixed to use GFX scrollbars that can
michael@0 3016 * be create with BuildScrollFrame.
michael@0 3017 */
michael@0 3018 nsresult
michael@0 3019 nsCSSFrameConstructor::InitializeSelectFrame(nsFrameConstructorState& aState,
michael@0 3020 nsIFrame* scrollFrame,
michael@0 3021 nsIFrame* scrolledFrame,
michael@0 3022 nsIContent* aContent,
michael@0 3023 nsIFrame* aParentFrame,
michael@0 3024 nsStyleContext* aStyleContext,
michael@0 3025 bool aBuildCombobox,
michael@0 3026 PendingBinding* aPendingBinding,
michael@0 3027 nsFrameItems& aFrameItems)
michael@0 3028 {
michael@0 3029 const nsStyleDisplay* display = aStyleContext->StyleDisplay();
michael@0 3030
michael@0 3031 // Initialize it
michael@0 3032 nsIFrame* geometricParent = aState.GetGeometricParent(display, aParentFrame);
michael@0 3033
michael@0 3034 // We don't call InitAndRestoreFrame for scrollFrame because we can only
michael@0 3035 // restore the frame state after its parts have been created (in particular,
michael@0 3036 // the scrollable view). So we have to split Init and Restore.
michael@0 3037
michael@0 3038 // Initialize the frame
michael@0 3039 scrollFrame->Init(aContent, geometricParent, nullptr);
michael@0 3040
michael@0 3041 if (!aBuildCombobox) {
michael@0 3042 aState.AddChild(scrollFrame, aFrameItems, aContent,
michael@0 3043 aStyleContext, aParentFrame);
michael@0 3044 }
michael@0 3045
michael@0 3046 if (aBuildCombobox) {
michael@0 3047 nsContainerFrame::CreateViewForFrame(scrollFrame, true);
michael@0 3048 }
michael@0 3049
michael@0 3050 BuildScrollFrame(aState, aContent, aStyleContext, scrolledFrame,
michael@0 3051 geometricParent, scrollFrame);
michael@0 3052
michael@0 3053 if (aState.mFrameState) {
michael@0 3054 // Restore frame state for the scroll frame
michael@0 3055 RestoreFrameStateFor(scrollFrame, aState.mFrameState);
michael@0 3056 }
michael@0 3057
michael@0 3058 // Process children
michael@0 3059 nsFrameItems childItems;
michael@0 3060
michael@0 3061 ProcessChildren(aState, aContent, aStyleContext, scrolledFrame, false,
michael@0 3062 childItems, false, aPendingBinding);
michael@0 3063
michael@0 3064 // Set the scrolled frame's initial child lists
michael@0 3065 scrolledFrame->SetInitialChildList(kPrincipalList, childItems);
michael@0 3066 return NS_OK;
michael@0 3067 }
michael@0 3068
michael@0 3069 nsIFrame*
michael@0 3070 nsCSSFrameConstructor::ConstructFieldSetFrame(nsFrameConstructorState& aState,
michael@0 3071 FrameConstructionItem& aItem,
michael@0 3072 nsIFrame* aParentFrame,
michael@0 3073 const nsStyleDisplay* aStyleDisplay,
michael@0 3074 nsFrameItems& aFrameItems)
michael@0 3075 {
michael@0 3076 nsIContent* const content = aItem.mContent;
michael@0 3077 nsStyleContext* const styleContext = aItem.mStyleContext;
michael@0 3078
michael@0 3079 nsIFrame* fieldsetFrame = NS_NewFieldSetFrame(mPresShell, styleContext);
michael@0 3080
michael@0 3081 // Initialize it
michael@0 3082 InitAndRestoreFrame(aState, content,
michael@0 3083 aState.GetGeometricParent(aStyleDisplay, aParentFrame),
michael@0 3084 fieldsetFrame);
michael@0 3085
michael@0 3086 // Resolve style and initialize the frame
michael@0 3087 nsRefPtr<nsStyleContext> fieldsetContentStyle;
michael@0 3088 fieldsetContentStyle = mPresShell->StyleSet()->
michael@0 3089 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::fieldsetContent, styleContext);
michael@0 3090
michael@0 3091 const nsStyleDisplay* fieldsetContentDisplay = fieldsetContentStyle->StyleDisplay();
michael@0 3092 bool isScrollable = fieldsetContentDisplay->IsScrollableOverflow();
michael@0 3093 nsIFrame* scrollFrame = nullptr;
michael@0 3094 if (isScrollable) {
michael@0 3095 fieldsetContentStyle =
michael@0 3096 BeginBuildingScrollFrame(aState, content, fieldsetContentStyle,
michael@0 3097 fieldsetFrame, nsCSSAnonBoxes::scrolledContent,
michael@0 3098 false, scrollFrame);
michael@0 3099 }
michael@0 3100
michael@0 3101 nsIFrame* blockFrame = NS_NewBlockFrame(mPresShell, fieldsetContentStyle,
michael@0 3102 NS_BLOCK_FLOAT_MGR |
michael@0 3103 NS_BLOCK_MARGIN_ROOT);
michael@0 3104 InitAndRestoreFrame(aState, content,
michael@0 3105 scrollFrame ? scrollFrame : fieldsetFrame, blockFrame);
michael@0 3106
michael@0 3107 aState.AddChild(fieldsetFrame, aFrameItems, content, styleContext, aParentFrame);
michael@0 3108
michael@0 3109 // Process children
michael@0 3110 nsFrameConstructorSaveState absoluteSaveState;
michael@0 3111 nsFrameItems childItems;
michael@0 3112
michael@0 3113 blockFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
michael@0 3114 if (fieldsetFrame->IsPositioned()) {
michael@0 3115 aState.PushAbsoluteContainingBlock(blockFrame, fieldsetFrame, absoluteSaveState);
michael@0 3116 }
michael@0 3117
michael@0 3118 ProcessChildren(aState, content, styleContext, blockFrame, true,
michael@0 3119 childItems, true, aItem.mPendingBinding);
michael@0 3120
michael@0 3121 nsFrameItems fieldsetKids;
michael@0 3122 fieldsetKids.AddChild(scrollFrame ? scrollFrame : blockFrame);
michael@0 3123
michael@0 3124 for (nsFrameList::Enumerator e(childItems); !e.AtEnd(); e.Next()) {
michael@0 3125 nsIFrame* child = e.get();
michael@0 3126 if (child->GetContentInsertionFrame()->GetType() == nsGkAtoms::legendFrame) {
michael@0 3127 // We want the legend to be the first frame in the fieldset child list.
michael@0 3128 // That way the EventStateManager will do the right thing when tabbing
michael@0 3129 // from a selection point within the legend (bug 236071), which is
michael@0 3130 // used for implementing legend access keys (bug 81481).
michael@0 3131 // GetAdjustedParentFrame() below depends on this frame order.
michael@0 3132 childItems.RemoveFrame(child);
michael@0 3133 // Make sure to reparent the legend so it has the fieldset as the parent.
michael@0 3134 fieldsetKids.InsertFrame(fieldsetFrame, nullptr, child);
michael@0 3135 if (scrollFrame) {
michael@0 3136 StickyScrollContainer::NotifyReparentedFrameAcrossScrollFrameBoundary(
michael@0 3137 child, blockFrame);
michael@0 3138 }
michael@0 3139 break;
michael@0 3140 }
michael@0 3141 }
michael@0 3142
michael@0 3143 if (isScrollable) {
michael@0 3144 FinishBuildingScrollFrame(scrollFrame, blockFrame);
michael@0 3145 }
michael@0 3146
michael@0 3147 // Set the inner frame's initial child lists
michael@0 3148 blockFrame->SetInitialChildList(kPrincipalList, childItems);
michael@0 3149
michael@0 3150 // Set the outer frame's initial child list
michael@0 3151 fieldsetFrame->SetInitialChildList(kPrincipalList, fieldsetKids);
michael@0 3152
michael@0 3153 fieldsetFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT);
michael@0 3154
michael@0 3155 // Our new frame returned is the outer frame, which is the fieldset frame.
michael@0 3156 return fieldsetFrame;
michael@0 3157 }
michael@0 3158
michael@0 3159 static nsIFrame*
michael@0 3160 FindAncestorWithGeneratedContentPseudo(nsIFrame* aFrame)
michael@0 3161 {
michael@0 3162 for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) {
michael@0 3163 NS_ASSERTION(f->IsGeneratedContentFrame(),
michael@0 3164 "should not have exited generated content");
michael@0 3165 nsIAtom* pseudo = f->StyleContext()->GetPseudo();
michael@0 3166 if (pseudo == nsCSSPseudoElements::before ||
michael@0 3167 pseudo == nsCSSPseudoElements::after)
michael@0 3168 return f;
michael@0 3169 }
michael@0 3170 return nullptr;
michael@0 3171 }
michael@0 3172
michael@0 3173 #define SIMPLE_FCDATA(_func) FCDATA_DECL(0, _func)
michael@0 3174 #define FULL_CTOR_FCDATA(_flags, _func) \
michael@0 3175 { _flags | FCDATA_FUNC_IS_FULL_CTOR, { nullptr }, _func, nullptr }
michael@0 3176
michael@0 3177 /* static */
michael@0 3178 const nsCSSFrameConstructor::FrameConstructionData*
michael@0 3179 nsCSSFrameConstructor::FindTextData(nsIFrame* aParentFrame)
michael@0 3180 {
michael@0 3181 if (aParentFrame && IsFrameForSVG(aParentFrame)) {
michael@0 3182 nsIFrame *ancestorFrame =
michael@0 3183 nsSVGUtils::GetFirstNonAAncestorFrame(aParentFrame);
michael@0 3184 if (ancestorFrame) {
michael@0 3185 static const FrameConstructionData sSVGTextData =
michael@0 3186 FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT | FCDATA_IS_SVG_TEXT,
michael@0 3187 NS_NewTextFrame);
michael@0 3188 if (ancestorFrame->IsSVGText()) {
michael@0 3189 return &sSVGTextData;
michael@0 3190 }
michael@0 3191 }
michael@0 3192 return nullptr;
michael@0 3193 }
michael@0 3194
michael@0 3195 static const FrameConstructionData sTextData =
michael@0 3196 FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT, NS_NewTextFrame);
michael@0 3197 return &sTextData;
michael@0 3198 }
michael@0 3199
michael@0 3200 void
michael@0 3201 nsCSSFrameConstructor::ConstructTextFrame(const FrameConstructionData* aData,
michael@0 3202 nsFrameConstructorState& aState,
michael@0 3203 nsIContent* aContent,
michael@0 3204 nsIFrame* aParentFrame,
michael@0 3205 nsStyleContext* aStyleContext,
michael@0 3206 nsFrameItems& aFrameItems)
michael@0 3207 {
michael@0 3208 NS_PRECONDITION(aData, "Must have frame construction data");
michael@0 3209
michael@0 3210 nsIFrame* newFrame = (*aData->mFunc.mCreationFunc)(mPresShell, aStyleContext);
michael@0 3211
michael@0 3212 InitAndRestoreFrame(aState, aContent, aParentFrame, newFrame);
michael@0 3213
michael@0 3214 // We never need to create a view for a text frame.
michael@0 3215
michael@0 3216 if (newFrame->IsGeneratedContentFrame()) {
michael@0 3217 nsAutoPtr<nsGenConInitializer> initializer;
michael@0 3218 initializer =
michael@0 3219 static_cast<nsGenConInitializer*>(
michael@0 3220 aContent->UnsetProperty(nsGkAtoms::genConInitializerProperty));
michael@0 3221 if (initializer) {
michael@0 3222 if (initializer->mNode->InitTextFrame(initializer->mList,
michael@0 3223 FindAncestorWithGeneratedContentPseudo(newFrame), newFrame)) {
michael@0 3224 (this->*(initializer->mDirtyAll))();
michael@0 3225 }
michael@0 3226 initializer->mNode.forget();
michael@0 3227 }
michael@0 3228 }
michael@0 3229
michael@0 3230 // Add the newly constructed frame to the flow
michael@0 3231 aFrameItems.AddChild(newFrame);
michael@0 3232
michael@0 3233 if (!aState.mCreatingExtraFrames)
michael@0 3234 aContent->SetPrimaryFrame(newFrame);
michael@0 3235 }
michael@0 3236
michael@0 3237 /* static */
michael@0 3238 const nsCSSFrameConstructor::FrameConstructionData*
michael@0 3239 nsCSSFrameConstructor::FindDataByInt(int32_t aInt,
michael@0 3240 Element* aElement,
michael@0 3241 nsStyleContext* aStyleContext,
michael@0 3242 const FrameConstructionDataByInt* aDataPtr,
michael@0 3243 uint32_t aDataLength)
michael@0 3244 {
michael@0 3245 for (const FrameConstructionDataByInt *curData = aDataPtr,
michael@0 3246 *endData = aDataPtr + aDataLength;
michael@0 3247 curData != endData;
michael@0 3248 ++curData) {
michael@0 3249 if (curData->mInt == aInt) {
michael@0 3250 const FrameConstructionData* data = &curData->mData;
michael@0 3251 if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) {
michael@0 3252 return data->mFunc.mDataGetter(aElement, aStyleContext);
michael@0 3253 }
michael@0 3254
michael@0 3255 return data;
michael@0 3256 }
michael@0 3257 }
michael@0 3258
michael@0 3259 return nullptr;
michael@0 3260 }
michael@0 3261
michael@0 3262 /* static */
michael@0 3263 const nsCSSFrameConstructor::FrameConstructionData*
michael@0 3264 nsCSSFrameConstructor::FindDataByTag(nsIAtom* aTag,
michael@0 3265 Element* aElement,
michael@0 3266 nsStyleContext* aStyleContext,
michael@0 3267 const FrameConstructionDataByTag* aDataPtr,
michael@0 3268 uint32_t aDataLength)
michael@0 3269 {
michael@0 3270 for (const FrameConstructionDataByTag *curData = aDataPtr,
michael@0 3271 *endData = aDataPtr + aDataLength;
michael@0 3272 curData != endData;
michael@0 3273 ++curData) {
michael@0 3274 if (*curData->mTag == aTag) {
michael@0 3275 const FrameConstructionData* data = &curData->mData;
michael@0 3276 if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) {
michael@0 3277 return data->mFunc.mDataGetter(aElement, aStyleContext);
michael@0 3278 }
michael@0 3279
michael@0 3280 return data;
michael@0 3281 }
michael@0 3282 }
michael@0 3283
michael@0 3284 return nullptr;
michael@0 3285 }
michael@0 3286
michael@0 3287 #define SUPPRESS_FCDATA() FCDATA_DECL(FCDATA_SUPPRESS_FRAME, nullptr)
michael@0 3288 #define SIMPLE_INT_CREATE(_int, _func) { _int, SIMPLE_FCDATA(_func) }
michael@0 3289 #define SIMPLE_INT_CHAIN(_int, _func) \
michael@0 3290 { _int, FCDATA_DECL(FCDATA_FUNC_IS_DATA_GETTER, _func) }
michael@0 3291 #define COMPLEX_INT_CREATE(_int, _func) \
michael@0 3292 { _int, FULL_CTOR_FCDATA(0, _func) }
michael@0 3293
michael@0 3294 #define SIMPLE_TAG_CREATE(_tag, _func) \
michael@0 3295 { &nsGkAtoms::_tag, SIMPLE_FCDATA(_func) }
michael@0 3296 #define SIMPLE_TAG_CHAIN(_tag, _func) \
michael@0 3297 { &nsGkAtoms::_tag, FCDATA_DECL(FCDATA_FUNC_IS_DATA_GETTER, _func) }
michael@0 3298 #define COMPLEX_TAG_CREATE(_tag, _func) \
michael@0 3299 { &nsGkAtoms::_tag, FULL_CTOR_FCDATA(0, _func) }
michael@0 3300
michael@0 3301 static bool
michael@0 3302 IsFrameForFieldSet(nsIFrame* aFrame, nsIAtom* aFrameType)
michael@0 3303 {
michael@0 3304 nsIAtom* pseudo = aFrame->StyleContext()->GetPseudo();
michael@0 3305 if (pseudo == nsCSSAnonBoxes::fieldsetContent ||
michael@0 3306 pseudo == nsCSSAnonBoxes::scrolledContent) {
michael@0 3307 return IsFrameForFieldSet(aFrame->GetParent(), aFrame->GetParent()->GetType());
michael@0 3308 }
michael@0 3309 return aFrameType == nsGkAtoms::fieldSetFrame;
michael@0 3310 }
michael@0 3311
michael@0 3312 /* static */
michael@0 3313 const nsCSSFrameConstructor::FrameConstructionData*
michael@0 3314 nsCSSFrameConstructor::FindHTMLData(Element* aElement,
michael@0 3315 nsIAtom* aTag,
michael@0 3316 int32_t aNameSpaceID,
michael@0 3317 nsIFrame* aParentFrame,
michael@0 3318 nsStyleContext* aStyleContext)
michael@0 3319 {
michael@0 3320 // Ignore the tag if it's not HTML content and if it doesn't extend (via XBL)
michael@0 3321 // a valid HTML namespace. This check must match the one in
michael@0 3322 // ShouldHaveFirstLineStyle.
michael@0 3323 if (aNameSpaceID != kNameSpaceID_XHTML) {
michael@0 3324 return nullptr;
michael@0 3325 }
michael@0 3326
michael@0 3327 NS_ASSERTION(!aParentFrame ||
michael@0 3328 aParentFrame->StyleContext()->GetPseudo() !=
michael@0 3329 nsCSSAnonBoxes::fieldsetContent ||
michael@0 3330 aParentFrame->GetParent()->GetType() == nsGkAtoms::fieldSetFrame,
michael@0 3331 "Unexpected parent for fieldset content anon box");
michael@0 3332 if (aTag == nsGkAtoms::legend &&
michael@0 3333 (!aParentFrame ||
michael@0 3334 !IsFrameForFieldSet(aParentFrame, aParentFrame->GetType()) ||
michael@0 3335 !aElement->GetParent() ||
michael@0 3336 !aElement->GetParent()->IsHTML(nsGkAtoms::fieldset) ||
michael@0 3337 aStyleContext->StyleDisplay()->IsFloatingStyle() ||
michael@0 3338 aStyleContext->StyleDisplay()->IsAbsolutelyPositionedStyle())) {
michael@0 3339 // <legend> is only special inside fieldset, check both the frame tree
michael@0 3340 // parent and content tree parent due to XBL issues. For floated or
michael@0 3341 // absolutely positioned legends we want to construct by display type and
michael@0 3342 // not do special legend stuff.
michael@0 3343 // XXXbz it would be nice if we could just decide this based on the parent
michael@0 3344 // tag, and hence just use a SIMPLE_TAG_CHAIN for legend below, but the
michael@0 3345 // fact that with XBL we could end up with this legend element in some
michael@0 3346 // totally weird insertion point makes that chancy, I think.
michael@0 3347 return nullptr;
michael@0 3348 }
michael@0 3349
michael@0 3350 static const FrameConstructionDataByTag sHTMLData[] = {
michael@0 3351 SIMPLE_TAG_CHAIN(img, nsCSSFrameConstructor::FindImgData),
michael@0 3352 SIMPLE_TAG_CHAIN(mozgeneratedcontentimage,
michael@0 3353 nsCSSFrameConstructor::FindImgData),
michael@0 3354 { &nsGkAtoms::br,
michael@0 3355 FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT | FCDATA_IS_LINE_BREAK,
michael@0 3356 NS_NewBRFrame) },
michael@0 3357 SIMPLE_TAG_CREATE(wbr, NS_NewWBRFrame),
michael@0 3358 SIMPLE_TAG_CHAIN(input, nsCSSFrameConstructor::FindInputData),
michael@0 3359 SIMPLE_TAG_CREATE(textarea, NS_NewTextControlFrame),
michael@0 3360 COMPLEX_TAG_CREATE(select, &nsCSSFrameConstructor::ConstructSelectFrame),
michael@0 3361 SIMPLE_TAG_CHAIN(object, nsCSSFrameConstructor::FindObjectData),
michael@0 3362 SIMPLE_TAG_CHAIN(applet, nsCSSFrameConstructor::FindObjectData),
michael@0 3363 SIMPLE_TAG_CHAIN(embed, nsCSSFrameConstructor::FindObjectData),
michael@0 3364 COMPLEX_TAG_CREATE(fieldset,
michael@0 3365 &nsCSSFrameConstructor::ConstructFieldSetFrame),
michael@0 3366 { &nsGkAtoms::legend,
michael@0 3367 FCDATA_DECL(FCDATA_ALLOW_BLOCK_STYLES | FCDATA_MAY_NEED_SCROLLFRAME,
michael@0 3368 NS_NewLegendFrame) },
michael@0 3369 SIMPLE_TAG_CREATE(frameset, NS_NewHTMLFramesetFrame),
michael@0 3370 SIMPLE_TAG_CREATE(iframe, NS_NewSubDocumentFrame),
michael@0 3371 { &nsGkAtoms::button,
michael@0 3372 FCDATA_WITH_WRAPPING_BLOCK(FCDATA_ALLOW_BLOCK_STYLES,
michael@0 3373 NS_NewHTMLButtonControlFrame,
michael@0 3374 nsCSSAnonBoxes::buttonContent) },
michael@0 3375 SIMPLE_TAG_CHAIN(canvas, nsCSSFrameConstructor::FindCanvasData),
michael@0 3376 SIMPLE_TAG_CREATE(video, NS_NewHTMLVideoFrame),
michael@0 3377 SIMPLE_TAG_CREATE(audio, NS_NewHTMLVideoFrame),
michael@0 3378 SIMPLE_TAG_CREATE(progress, NS_NewProgressFrame),
michael@0 3379 SIMPLE_TAG_CREATE(meter, NS_NewMeterFrame)
michael@0 3380 };
michael@0 3381
michael@0 3382 return FindDataByTag(aTag, aElement, aStyleContext, sHTMLData,
michael@0 3383 ArrayLength(sHTMLData));
michael@0 3384 }
michael@0 3385
michael@0 3386 /* static */
michael@0 3387 const nsCSSFrameConstructor::FrameConstructionData*
michael@0 3388 nsCSSFrameConstructor::FindImgData(Element* aElement,
michael@0 3389 nsStyleContext* aStyleContext)
michael@0 3390 {
michael@0 3391 if (!nsImageFrame::ShouldCreateImageFrameFor(aElement, aStyleContext)) {
michael@0 3392 return nullptr;
michael@0 3393 }
michael@0 3394
michael@0 3395 static const FrameConstructionData sImgData = SIMPLE_FCDATA(NS_NewImageFrame);
michael@0 3396 return &sImgData;
michael@0 3397 }
michael@0 3398
michael@0 3399 /* static */
michael@0 3400 const nsCSSFrameConstructor::FrameConstructionData*
michael@0 3401 nsCSSFrameConstructor::FindImgControlData(Element* aElement,
michael@0 3402 nsStyleContext* aStyleContext)
michael@0 3403 {
michael@0 3404 if (!nsImageFrame::ShouldCreateImageFrameFor(aElement, aStyleContext)) {
michael@0 3405 return nullptr;
michael@0 3406 }
michael@0 3407
michael@0 3408 static const FrameConstructionData sImgControlData =
michael@0 3409 SIMPLE_FCDATA(NS_NewImageControlFrame);
michael@0 3410 return &sImgControlData;
michael@0 3411 }
michael@0 3412
michael@0 3413 /* static */
michael@0 3414 const nsCSSFrameConstructor::FrameConstructionData*
michael@0 3415 nsCSSFrameConstructor::FindInputData(Element* aElement,
michael@0 3416 nsStyleContext* aStyleContext)
michael@0 3417 {
michael@0 3418 static const FrameConstructionDataByInt sInputData[] = {
michael@0 3419 SIMPLE_INT_CREATE(NS_FORM_INPUT_CHECKBOX, NS_NewGfxCheckboxControlFrame),
michael@0 3420 SIMPLE_INT_CREATE(NS_FORM_INPUT_RADIO, NS_NewGfxRadioControlFrame),
michael@0 3421 SIMPLE_INT_CREATE(NS_FORM_INPUT_FILE, NS_NewFileControlFrame),
michael@0 3422 SIMPLE_INT_CHAIN(NS_FORM_INPUT_IMAGE,
michael@0 3423 nsCSSFrameConstructor::FindImgControlData),
michael@0 3424 SIMPLE_INT_CREATE(NS_FORM_INPUT_EMAIL, NS_NewTextControlFrame),
michael@0 3425 SIMPLE_INT_CREATE(NS_FORM_INPUT_SEARCH, NS_NewTextControlFrame),
michael@0 3426 SIMPLE_INT_CREATE(NS_FORM_INPUT_TEXT, NS_NewTextControlFrame),
michael@0 3427 SIMPLE_INT_CREATE(NS_FORM_INPUT_TEL, NS_NewTextControlFrame),
michael@0 3428 SIMPLE_INT_CREATE(NS_FORM_INPUT_URL, NS_NewTextControlFrame),
michael@0 3429 SIMPLE_INT_CREATE(NS_FORM_INPUT_RANGE, NS_NewRangeFrame),
michael@0 3430 SIMPLE_INT_CREATE(NS_FORM_INPUT_PASSWORD, NS_NewTextControlFrame),
michael@0 3431 { NS_FORM_INPUT_COLOR,
michael@0 3432 FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewColorControlFrame,
michael@0 3433 nsCSSAnonBoxes::buttonContent) },
michael@0 3434 // TODO: this is temporary until a frame is written: bug 635240.
michael@0 3435 SIMPLE_INT_CREATE(NS_FORM_INPUT_NUMBER, NS_NewNumberControlFrame),
michael@0 3436 // TODO: this is temporary until a frame is written: bug 773205.
michael@0 3437 SIMPLE_INT_CREATE(NS_FORM_INPUT_DATE, NS_NewTextControlFrame),
michael@0 3438 // TODO: this is temporary until a frame is written: bug 773205
michael@0 3439 SIMPLE_INT_CREATE(NS_FORM_INPUT_TIME, NS_NewTextControlFrame),
michael@0 3440 { NS_FORM_INPUT_SUBMIT,
michael@0 3441 FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame,
michael@0 3442 nsCSSAnonBoxes::buttonContent) },
michael@0 3443 { NS_FORM_INPUT_RESET,
michael@0 3444 FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame,
michael@0 3445 nsCSSAnonBoxes::buttonContent) },
michael@0 3446 { NS_FORM_INPUT_BUTTON,
michael@0 3447 FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame,
michael@0 3448 nsCSSAnonBoxes::buttonContent) }
michael@0 3449 // Keeping hidden inputs out of here on purpose for so they get frames by
michael@0 3450 // display (in practice, none).
michael@0 3451 };
michael@0 3452
michael@0 3453 nsCOMPtr<nsIFormControl> control = do_QueryInterface(aElement);
michael@0 3454 NS_ASSERTION(control, "input doesn't implement nsIFormControl?");
michael@0 3455
michael@0 3456 return FindDataByInt(control->GetType(), aElement, aStyleContext,
michael@0 3457 sInputData, ArrayLength(sInputData));
michael@0 3458 }
michael@0 3459
michael@0 3460 /* static */
michael@0 3461 const nsCSSFrameConstructor::FrameConstructionData*
michael@0 3462 nsCSSFrameConstructor::FindObjectData(Element* aElement,
michael@0 3463 nsStyleContext* aStyleContext)
michael@0 3464 {
michael@0 3465 // GetDisplayedType isn't necessarily nsIObjectLoadingContent::TYPE_NULL for
michael@0 3466 // cases when the object is broken/suppressed/etc (e.g. a broken image), but
michael@0 3467 // we want to treat those cases as TYPE_NULL
michael@0 3468 uint32_t type;
michael@0 3469 if (aElement->State().HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
michael@0 3470 NS_EVENT_STATE_USERDISABLED |
michael@0 3471 NS_EVENT_STATE_SUPPRESSED)) {
michael@0 3472 type = nsIObjectLoadingContent::TYPE_NULL;
michael@0 3473 } else {
michael@0 3474 nsCOMPtr<nsIObjectLoadingContent> objContent(do_QueryInterface(aElement));
michael@0 3475 NS_ASSERTION(objContent,
michael@0 3476 "applet, embed and object must implement "
michael@0 3477 "nsIObjectLoadingContent!");
michael@0 3478
michael@0 3479 objContent->GetDisplayedType(&type);
michael@0 3480 }
michael@0 3481
michael@0 3482 static const FrameConstructionDataByInt sObjectData[] = {
michael@0 3483 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_LOADING,
michael@0 3484 NS_NewEmptyFrame),
michael@0 3485 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_PLUGIN,
michael@0 3486 NS_NewObjectFrame),
michael@0 3487 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_IMAGE,
michael@0 3488 NS_NewImageFrame),
michael@0 3489 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_DOCUMENT,
michael@0 3490 NS_NewSubDocumentFrame)
michael@0 3491 // Nothing for TYPE_NULL so we'll construct frames by display there
michael@0 3492 };
michael@0 3493
michael@0 3494 return FindDataByInt((int32_t)type, aElement, aStyleContext,
michael@0 3495 sObjectData, ArrayLength(sObjectData));
michael@0 3496 }
michael@0 3497
michael@0 3498 /* static */
michael@0 3499 const nsCSSFrameConstructor::FrameConstructionData*
michael@0 3500 nsCSSFrameConstructor::FindCanvasData(Element* aElement,
michael@0 3501 nsStyleContext* aStyleContext)
michael@0 3502 {
michael@0 3503 // We want to check whether script is enabled on the document that
michael@0 3504 // could be painting to the canvas. That's the owner document of
michael@0 3505 // the canvas, except when the owner document is a static document,
michael@0 3506 // in which case it's the original document it was cloned from.
michael@0 3507 nsIDocument* doc = aElement->OwnerDoc();
michael@0 3508 if (doc->IsStaticDocument()) {
michael@0 3509 doc = doc->GetOriginalDocument();
michael@0 3510 }
michael@0 3511 if (!doc->IsScriptEnabled()) {
michael@0 3512 return nullptr;
michael@0 3513 }
michael@0 3514
michael@0 3515 static const FrameConstructionData sCanvasData =
michael@0 3516 FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewHTMLCanvasFrame,
michael@0 3517 nsCSSAnonBoxes::htmlCanvasContent);
michael@0 3518 return &sCanvasData;
michael@0 3519 }
michael@0 3520
michael@0 3521 void
michael@0 3522 nsCSSFrameConstructor::ConstructFrameFromItemInternal(FrameConstructionItem& aItem,
michael@0 3523 nsFrameConstructorState& aState,
michael@0 3524 nsIFrame* aParentFrame,
michael@0 3525 nsFrameItems& aFrameItems)
michael@0 3526 {
michael@0 3527 const FrameConstructionData* data = aItem.mFCData;
michael@0 3528 NS_ASSERTION(data, "Must have frame construction data");
michael@0 3529
michael@0 3530 uint32_t bits = data->mBits;
michael@0 3531
michael@0 3532 NS_ASSERTION(!(bits & FCDATA_FUNC_IS_DATA_GETTER),
michael@0 3533 "Should have dealt with this inside the data finder");
michael@0 3534
michael@0 3535 // Some sets of bits are not compatible with each other
michael@0 3536 #define CHECK_ONLY_ONE_BIT(_bit1, _bit2) \
michael@0 3537 NS_ASSERTION(!(bits & _bit1) || !(bits & _bit2), \
michael@0 3538 "Only one of these bits should be set")
michael@0 3539 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_FORCE_NULL_ABSPOS_CONTAINER);
michael@0 3540 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_WRAP_KIDS_IN_BLOCKS);
michael@0 3541 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_MAY_NEED_SCROLLFRAME);
michael@0 3542 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_IS_POPUP);
michael@0 3543 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_SKIP_ABSPOS_PUSH);
michael@0 3544 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
michael@0 3545 FCDATA_DISALLOW_GENERATED_CONTENT);
michael@0 3546 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_ALLOW_BLOCK_STYLES);
michael@0 3547 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
michael@0 3548 FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS);
michael@0 3549 CHECK_ONLY_ONE_BIT(FCDATA_WRAP_KIDS_IN_BLOCKS,
michael@0 3550 FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS);
michael@0 3551 #undef CHECK_ONLY_ONE_BIT
michael@0 3552 NS_ASSERTION(!(bits & FCDATA_FORCED_NON_SCROLLABLE_BLOCK) ||
michael@0 3553 ((bits & FCDATA_FUNC_IS_FULL_CTOR) &&
michael@0 3554 data->mFullConstructor ==
michael@0 3555 &nsCSSFrameConstructor::ConstructNonScrollableBlock),
michael@0 3556 "Unexpected FCDATA_FORCED_NON_SCROLLABLE_BLOCK flag");
michael@0 3557
michael@0 3558 // Don't create a subdocument frame for iframes if we're creating extra frames
michael@0 3559 if (aState.mCreatingExtraFrames && aItem.mContent->IsHTML() &&
michael@0 3560 aItem.mContent->Tag() == nsGkAtoms::iframe)
michael@0 3561 {
michael@0 3562 return;
michael@0 3563 }
michael@0 3564
michael@0 3565 nsStyleContext* const styleContext = aItem.mStyleContext;
michael@0 3566 const nsStyleDisplay* display = styleContext->StyleDisplay();
michael@0 3567 nsIContent* const content = aItem.mContent;
michael@0 3568
michael@0 3569 // Get the parent of the content and check if it is a XBL children element.
michael@0 3570 // Push the children element as an ancestor here because it does
michael@0 3571 // not have a frame and would not otherwise be pushed as an ancestor. It is
michael@0 3572 // necessary to do so in order to correctly handle style resolution on
michael@0 3573 // descendants.
michael@0 3574 nsIContent* parent = content->GetParent();
michael@0 3575 TreeMatchContext::AutoAncestorPusher
michael@0 3576 insertionPointPusher(aState.mTreeMatchContext);
michael@0 3577 if (parent && nsContentUtils::IsContentInsertionPoint(parent)) {
michael@0 3578 if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
michael@0 3579 insertionPointPusher.PushAncestorAndStyleScope(parent);
michael@0 3580 } else {
michael@0 3581 insertionPointPusher.PushStyleScope(parent);
michael@0 3582 }
michael@0 3583 }
michael@0 3584
michael@0 3585 // Push the content as a style ancestor now, so we don't have to do
michael@0 3586 // it in our various full-constructor functions. In particular,
michael@0 3587 // since a number of full-constructor functions don't actually call
michael@0 3588 // ProcessChildren in some cases (e.g. for CSS anonymous table boxes
michael@0 3589 // or for situations where only anonymouse children are having
michael@0 3590 // frames constructed), this is the best place to bottleneck the
michael@0 3591 // pushing of the content instead of having to do it in multiple
michael@0 3592 // places.
michael@0 3593 TreeMatchContext::AutoAncestorPusher
michael@0 3594 ancestorPusher(aState.mTreeMatchContext);
michael@0 3595 if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
michael@0 3596 ancestorPusher.PushAncestorAndStyleScope(content);
michael@0 3597 } else {
michael@0 3598 ancestorPusher.PushStyleScope(content);
michael@0 3599 }
michael@0 3600
michael@0 3601 nsIFrame* newFrame;
michael@0 3602 nsIFrame* primaryFrame;
michael@0 3603 if (bits & FCDATA_FUNC_IS_FULL_CTOR) {
michael@0 3604 newFrame =
michael@0 3605 (this->*(data->mFullConstructor))(aState, aItem, aParentFrame,
michael@0 3606 display, aFrameItems);
michael@0 3607 MOZ_ASSERT(newFrame, "Full constructor failed");
michael@0 3608 primaryFrame = newFrame;
michael@0 3609 } else {
michael@0 3610 newFrame =
michael@0 3611 (*data->mFunc.mCreationFunc)(mPresShell, styleContext);
michael@0 3612
michael@0 3613 bool allowOutOfFlow = !(bits & FCDATA_DISALLOW_OUT_OF_FLOW);
michael@0 3614 bool isPopup = aItem.mIsPopup;
michael@0 3615 NS_ASSERTION(!isPopup ||
michael@0 3616 (aState.mPopupItems.containingBlock &&
michael@0 3617 aState.mPopupItems.containingBlock->GetType() ==
michael@0 3618 nsGkAtoms::popupSetFrame),
michael@0 3619 "Should have a containing block here!");
michael@0 3620
michael@0 3621 nsIFrame* geometricParent =
michael@0 3622 isPopup ? aState.mPopupItems.containingBlock :
michael@0 3623 (allowOutOfFlow ? aState.GetGeometricParent(display, aParentFrame)
michael@0 3624 : aParentFrame);
michael@0 3625
michael@0 3626 // Must init frameToAddToList to null, since it's inout
michael@0 3627 nsIFrame* frameToAddToList = nullptr;
michael@0 3628 if ((bits & FCDATA_MAY_NEED_SCROLLFRAME) &&
michael@0 3629 display->IsScrollableOverflow()) {
michael@0 3630 BuildScrollFrame(aState, content, styleContext, newFrame,
michael@0 3631 geometricParent, frameToAddToList);
michael@0 3632 } else {
michael@0 3633 InitAndRestoreFrame(aState, content, geometricParent, newFrame);
michael@0 3634 // See whether we need to create a view
michael@0 3635 nsContainerFrame::CreateViewForFrame(newFrame, false);
michael@0 3636 frameToAddToList = newFrame;
michael@0 3637 }
michael@0 3638
michael@0 3639 // Use frameToAddToList as the primary frame. In the non-scrollframe case
michael@0 3640 // they're equal, but in the scrollframe case newFrame is the scrolled
michael@0 3641 // frame, while frameToAddToList is the scrollframe (and should be the
michael@0 3642 // primary frame).
michael@0 3643 primaryFrame = frameToAddToList;
michael@0 3644
michael@0 3645 // If we need to create a block formatting context to wrap our
michael@0 3646 // kids, do it now.
michael@0 3647 const nsStyleDisplay* maybeAbsoluteContainingBlockDisplay = display;
michael@0 3648 nsIFrame* maybeAbsoluteContainingBlock = newFrame;
michael@0 3649 nsIFrame* possiblyLeafFrame = newFrame;
michael@0 3650 if (bits & FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS) {
michael@0 3651 nsRefPtr<nsStyleContext> blockContext;
michael@0 3652 blockContext =
michael@0 3653 mPresShell->StyleSet()->ResolveAnonymousBoxStyle(*data->mAnonBoxPseudo,
michael@0 3654 styleContext);
michael@0 3655 nsIFrame* blockFrame =
michael@0 3656 NS_NewBlockFormattingContext(mPresShell, blockContext);
michael@0 3657
michael@0 3658 InitAndRestoreFrame(aState, content, newFrame, blockFrame);
michael@0 3659
michael@0 3660 SetInitialSingleChild(newFrame, blockFrame);
michael@0 3661
michael@0 3662 // Now figure out whether newFrame or blockFrame should be the
michael@0 3663 // absolute container. It should be the latter if it's
michael@0 3664 // positioned, otherwise the former.
michael@0 3665 const nsStyleDisplay* blockDisplay = blockContext->StyleDisplay();
michael@0 3666 if (blockDisplay->IsPositioned(blockFrame)) {
michael@0 3667 maybeAbsoluteContainingBlockDisplay = blockDisplay;
michael@0 3668 maybeAbsoluteContainingBlock = blockFrame;
michael@0 3669 }
michael@0 3670
michael@0 3671 // Our kids should go into the blockFrame
michael@0 3672 newFrame = blockFrame;
michael@0 3673 }
michael@0 3674
michael@0 3675 aState.AddChild(frameToAddToList, aFrameItems, content, styleContext,
michael@0 3676 aParentFrame, allowOutOfFlow, allowOutOfFlow, isPopup);
michael@0 3677
michael@0 3678 #ifdef MOZ_XUL
michael@0 3679 // Icky XUL stuff, sadly
michael@0 3680
michael@0 3681 if (aItem.mIsRootPopupgroup) {
michael@0 3682 NS_ASSERTION(nsIRootBox::GetRootBox(mPresShell) &&
michael@0 3683 nsIRootBox::GetRootBox(mPresShell)->GetPopupSetFrame() ==
michael@0 3684 newFrame,
michael@0 3685 "Unexpected PopupSetFrame");
michael@0 3686 aState.mPopupItems.containingBlock = newFrame;
michael@0 3687 aState.mHavePendingPopupgroup = false;
michael@0 3688 }
michael@0 3689 #endif /* MOZ_XUL */
michael@0 3690
michael@0 3691 // Process the child content if requested
michael@0 3692 nsFrameItems childItems;
michael@0 3693 nsFrameConstructorSaveState absoluteSaveState;
michael@0 3694
michael@0 3695 if (bits & FCDATA_FORCE_NULL_ABSPOS_CONTAINER) {
michael@0 3696 aState.PushAbsoluteContainingBlock(nullptr, nullptr, absoluteSaveState);
michael@0 3697 } else if (!(bits & FCDATA_SKIP_ABSPOS_PUSH)) {
michael@0 3698 nsIFrame* cb = maybeAbsoluteContainingBlock;
michael@0 3699 cb->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
michael@0 3700 // This check is identical to nsStyleDisplay::IsPositioned except without
michael@0 3701 // the assertion that the style display and frame match. When constructing
michael@0 3702 // scroll frames we intentionally use the style display for the outer, but
michael@0 3703 // make the inner the containing block.
michael@0 3704 if ((maybeAbsoluteContainingBlockDisplay->IsAbsolutelyPositionedStyle() ||
michael@0 3705 maybeAbsoluteContainingBlockDisplay->IsRelativelyPositionedStyle() ||
michael@0 3706 (maybeAbsoluteContainingBlockDisplay->HasTransformStyle() &&
michael@0 3707 cb->IsFrameOfType(nsIFrame::eSupportsCSSTransforms)) ||
michael@0 3708 maybeAbsoluteContainingBlockDisplay->HasPerspectiveStyle()) &&
michael@0 3709 !cb->IsSVGText()) {
michael@0 3710 aState.PushAbsoluteContainingBlock(cb, cb, absoluteSaveState);
michael@0 3711 }
michael@0 3712 }
michael@0 3713
michael@0 3714 if (!aItem.mAnonChildren.IsEmpty()) {
michael@0 3715 NS_ASSERTION(!(bits & FCDATA_USE_CHILD_ITEMS),
michael@0 3716 "We should not have both anonymous and non-anonymous "
michael@0 3717 "children in a given FrameConstructorItem");
michael@0 3718 AddFCItemsForAnonymousContent(aState, newFrame, aItem.mAnonChildren,
michael@0 3719 aItem.mChildItems);
michael@0 3720 bits |= FCDATA_USE_CHILD_ITEMS;
michael@0 3721 }
michael@0 3722
michael@0 3723 if (bits & FCDATA_USE_CHILD_ITEMS) {
michael@0 3724 nsFrameConstructorSaveState floatSaveState;
michael@0 3725
michael@0 3726 if (ShouldSuppressFloatingOfDescendants(newFrame)) {
michael@0 3727 aState.PushFloatContainingBlock(nullptr, floatSaveState);
michael@0 3728 } else if (newFrame->IsFloatContainingBlock()) {
michael@0 3729 aState.PushFloatContainingBlock(newFrame, floatSaveState);
michael@0 3730 }
michael@0 3731 ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame,
michael@0 3732 childItems);
michael@0 3733 } else {
michael@0 3734 // Process the child frames.
michael@0 3735 ProcessChildren(aState, content, styleContext, newFrame,
michael@0 3736 !(bits & FCDATA_DISALLOW_GENERATED_CONTENT),
michael@0 3737 childItems,
michael@0 3738 (bits & FCDATA_ALLOW_BLOCK_STYLES) != 0,
michael@0 3739 aItem.mPendingBinding, possiblyLeafFrame);
michael@0 3740 }
michael@0 3741
michael@0 3742 #ifdef MOZ_XUL
michael@0 3743 // More icky XUL stuff
michael@0 3744 if (aItem.mNameSpaceID == kNameSpaceID_XUL &&
michael@0 3745 (aItem.mTag == nsGkAtoms::treechildren || // trees always need titletips
michael@0 3746 content->HasAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext) ||
michael@0 3747 content->HasAttr(kNameSpaceID_None, nsGkAtoms::tooltip))) {
michael@0 3748 nsIRootBox* rootBox = nsIRootBox::GetRootBox(mPresShell);
michael@0 3749 if (rootBox) {
michael@0 3750 rootBox->AddTooltipSupport(content);
michael@0 3751 }
michael@0 3752 }
michael@0 3753 #endif
michael@0 3754
michael@0 3755 if (bits & FCDATA_WRAP_KIDS_IN_BLOCKS) {
michael@0 3756 nsFrameItems newItems;
michael@0 3757 nsFrameItems currentBlockItems;
michael@0 3758 nsIFrame* f;
michael@0 3759 while ((f = childItems.FirstChild()) != nullptr) {
michael@0 3760 bool wrapFrame = IsInlineFrame(f) || IsFramePartOfIBSplit(f);
michael@0 3761 if (!wrapFrame) {
michael@0 3762 FlushAccumulatedBlock(aState, content, newFrame,
michael@0 3763 currentBlockItems, newItems);
michael@0 3764 }
michael@0 3765
michael@0 3766 childItems.RemoveFrame(f);
michael@0 3767 if (wrapFrame) {
michael@0 3768 currentBlockItems.AddChild(f);
michael@0 3769 } else {
michael@0 3770 newItems.AddChild(f);
michael@0 3771 }
michael@0 3772 }
michael@0 3773 FlushAccumulatedBlock(aState, content, newFrame,
michael@0 3774 currentBlockItems, newItems);
michael@0 3775
michael@0 3776 if (childItems.NotEmpty()) {
michael@0 3777 // an error must have occurred, delete unprocessed frames
michael@0 3778 childItems.DestroyFrames();
michael@0 3779 }
michael@0 3780
michael@0 3781 childItems = newItems;
michael@0 3782 }
michael@0 3783
michael@0 3784 // Set the frame's initial child list
michael@0 3785 // Note that MathML depends on this being called even if
michael@0 3786 // childItems is empty!
michael@0 3787 newFrame->SetInitialChildList(kPrincipalList, childItems);
michael@0 3788 }
michael@0 3789
michael@0 3790 NS_ASSERTION(newFrame->IsFrameOfType(nsIFrame::eLineParticipant) ==
michael@0 3791 ((bits & FCDATA_IS_LINE_PARTICIPANT) != 0),
michael@0 3792 "Incorrectly set FCDATA_IS_LINE_PARTICIPANT bits");
michael@0 3793
michael@0 3794 if (aItem.mIsAnonymousContentCreatorContent) {
michael@0 3795 primaryFrame->AddStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT);
michael@0 3796 }
michael@0 3797
michael@0 3798 // Even if mCreatingExtraFrames is set, we may need to SetPrimaryFrame for
michael@0 3799 // generated content that doesn't have one yet. Note that we have to examine
michael@0 3800 // the frame bit, because by this point mIsGeneratedContent has been cleared
michael@0 3801 // on aItem.
michael@0 3802 if ((!aState.mCreatingExtraFrames ||
michael@0 3803 ((primaryFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT) &&
michael@0 3804 !aItem.mContent->GetPrimaryFrame())) &&
michael@0 3805 !(bits & FCDATA_SKIP_FRAMESET)) {
michael@0 3806 aItem.mContent->SetPrimaryFrame(primaryFrame);
michael@0 3807 }
michael@0 3808 }
michael@0 3809
michael@0 3810 // after the node has been constructed and initialized create any
michael@0 3811 // anonymous content a node needs.
michael@0 3812 nsresult
michael@0 3813 nsCSSFrameConstructor::CreateAnonymousFrames(nsFrameConstructorState& aState,
michael@0 3814 nsIContent* aParent,
michael@0 3815 nsIFrame* aParentFrame,
michael@0 3816 PendingBinding* aPendingBinding,
michael@0 3817 nsFrameItems& aChildItems)
michael@0 3818 {
michael@0 3819 nsAutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> newAnonymousItems;
michael@0 3820 nsresult rv = GetAnonymousContent(aParent, aParentFrame, newAnonymousItems);
michael@0 3821 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3822
michael@0 3823 uint32_t count = newAnonymousItems.Length();
michael@0 3824 if (count == 0) {
michael@0 3825 return NS_OK;
michael@0 3826 }
michael@0 3827
michael@0 3828 nsFrameConstructorState::PendingBindingAutoPusher pusher(aState,
michael@0 3829 aPendingBinding);
michael@0 3830 TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext);
michael@0 3831 if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
michael@0 3832 ancestorPusher.PushAncestorAndStyleScope(aParent->AsElement());
michael@0 3833 } else {
michael@0 3834 ancestorPusher.PushStyleScope(aParent->AsElement());
michael@0 3835 }
michael@0 3836
michael@0 3837 nsIAnonymousContentCreator* creator = do_QueryFrame(aParentFrame);
michael@0 3838 NS_ASSERTION(creator,
michael@0 3839 "How can that happen if we have nodes to construct frames for?");
michael@0 3840
michael@0 3841 for (uint32_t i=0; i < count; i++) {
michael@0 3842 nsIContent* content = newAnonymousItems[i].mContent;
michael@0 3843 NS_ASSERTION(content, "null anonymous content?");
michael@0 3844 NS_ASSERTION(!newAnonymousItems[i].mStyleContext, "Unexpected style context");
michael@0 3845 NS_ASSERTION(newAnonymousItems[i].mChildren.IsEmpty(),
michael@0 3846 "This method is not currently used with frames that implement "
michael@0 3847 "nsIAnonymousContentCreator::CreateAnonymousContent to "
michael@0 3848 "output a list where the items have their own children");
michael@0 3849
michael@0 3850 nsIFrame* newFrame = creator->CreateFrameFor(content);
michael@0 3851 if (newFrame) {
michael@0 3852 NS_ASSERTION(content->GetPrimaryFrame(),
michael@0 3853 "Content must have a primary frame now");
michael@0 3854 newFrame->AddStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT);
michael@0 3855 aChildItems.AddChild(newFrame);
michael@0 3856 } else {
michael@0 3857 FrameConstructionItemList items;
michael@0 3858 {
michael@0 3859 // Skip flex item style-fixup during our AddFrameConstructionItems() call:
michael@0 3860 TreeMatchContext::AutoFlexItemStyleFixupSkipper
michael@0 3861 flexItemStyleFixupSkipper(aState.mTreeMatchContext);
michael@0 3862
michael@0 3863 AddFrameConstructionItems(aState, content, true, aParentFrame, items);
michael@0 3864 }
michael@0 3865 ConstructFramesFromItemList(aState, items, aParentFrame, aChildItems);
michael@0 3866 }
michael@0 3867 }
michael@0 3868
michael@0 3869 return NS_OK;
michael@0 3870 }
michael@0 3871
michael@0 3872 static void
michael@0 3873 SetFlagsOnSubtree(nsIContent *aNode, uintptr_t aFlagsToSet)
michael@0 3874 {
michael@0 3875 #ifdef DEBUG
michael@0 3876 // Make sure that the node passed to us doesn't have any XBL children
michael@0 3877 {
michael@0 3878 FlattenedChildIterator iter(aNode);
michael@0 3879 NS_ASSERTION(!iter.XBLInvolved() || !iter.GetNextChild(),
michael@0 3880 "The node should not have any XBL children");
michael@0 3881 }
michael@0 3882 #endif
michael@0 3883
michael@0 3884 // Set the flag on the node itself
michael@0 3885 aNode->SetFlags(aFlagsToSet);
michael@0 3886
michael@0 3887 // Set the flag on all of its children recursively
michael@0 3888 uint32_t count;
michael@0 3889 nsIContent * const *children = aNode->GetChildArray(&count);
michael@0 3890
michael@0 3891 for (uint32_t index = 0; index < count; ++index) {
michael@0 3892 SetFlagsOnSubtree(children[index], aFlagsToSet);
michael@0 3893 }
michael@0 3894 }
michael@0 3895
michael@0 3896 /**
michael@0 3897 * This function takes a tree of nsIAnonymousContentCreator::ContentInfo
michael@0 3898 * objects where the nsIContent nodes have just been created, and appends the
michael@0 3899 * nsIContent children in the tree to their parent. The leaf nsIContent objects
michael@0 3900 * are appended first to minimize the number of notifications that are sent
michael@0 3901 * out (i.e. by appending as many descendants as posible while their parent is
michael@0 3902 * not yet in the document tree).
michael@0 3903 *
michael@0 3904 * This function is used simply as a convenience so that implementations of
michael@0 3905 * nsIAnonymousContentCreator::CreateAnonymousContent don't all have to have
michael@0 3906 * their own code to connect the elements that they create.
michael@0 3907 */
michael@0 3908 static void
michael@0 3909 ConnectAnonymousTreeDescendants(nsIContent* aParent,
michael@0 3910 nsTArray<nsIAnonymousContentCreator::ContentInfo>& aContent)
michael@0 3911 {
michael@0 3912 uint32_t count = aContent.Length();
michael@0 3913 for (uint32_t i=0; i < count; i++) {
michael@0 3914 nsIContent* content = aContent[i].mContent;
michael@0 3915 NS_ASSERTION(content, "null anonymous content?");
michael@0 3916
michael@0 3917 ConnectAnonymousTreeDescendants(content, aContent[i].mChildren);
michael@0 3918
michael@0 3919 aParent->AppendChildTo(content, false);
michael@0 3920 }
michael@0 3921 }
michael@0 3922
michael@0 3923 nsresult
michael@0 3924 nsCSSFrameConstructor::GetAnonymousContent(nsIContent* aParent,
michael@0 3925 nsIFrame* aParentFrame,
michael@0 3926 nsTArray<nsIAnonymousContentCreator::ContentInfo>& aContent)
michael@0 3927 {
michael@0 3928 nsIAnonymousContentCreator* creator = do_QueryFrame(aParentFrame);
michael@0 3929 if (!creator)
michael@0 3930 return NS_OK;
michael@0 3931
michael@0 3932 nsresult rv = creator->CreateAnonymousContent(aContent);
michael@0 3933 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3934
michael@0 3935 uint32_t count = aContent.Length();
michael@0 3936 for (uint32_t i=0; i < count; i++) {
michael@0 3937 // get our child's content and set its parent to our content
michael@0 3938 nsIContent* content = aContent[i].mContent;
michael@0 3939 NS_ASSERTION(content, "null anonymous content?");
michael@0 3940
michael@0 3941 // least-surprise CSS binding until we do the SVG specified
michael@0 3942 // cascading rules for <svg:use> - bug 265894
michael@0 3943 if (aParentFrame->GetType() == nsGkAtoms::svgUseFrame) {
michael@0 3944 content->SetFlags(NODE_IS_ANONYMOUS_ROOT);
michael@0 3945 } else {
michael@0 3946 content->SetIsNativeAnonymousRoot();
michael@0 3947 }
michael@0 3948
michael@0 3949 ConnectAnonymousTreeDescendants(content, aContent[i].mChildren);
michael@0 3950
michael@0 3951 bool anonContentIsEditable = content->HasFlag(NODE_IS_EDITABLE);
michael@0 3952 rv = content->BindToTree(mDocument, aParent, aParent, true);
michael@0 3953 // If the anonymous content creator requested that the content should be
michael@0 3954 // editable, honor its request.
michael@0 3955 // We need to set the flag on the whole subtree, because existing
michael@0 3956 // children's flags have already been set as part of the BindToTree operation.
michael@0 3957 if (anonContentIsEditable) {
michael@0 3958 NS_ASSERTION(aParentFrame->GetType() == nsGkAtoms::textInputFrame,
michael@0 3959 "We only expect this for anonymous content under a text control frame");
michael@0 3960 SetFlagsOnSubtree(content, NODE_IS_EDITABLE);
michael@0 3961 }
michael@0 3962 if (NS_FAILED(rv)) {
michael@0 3963 content->UnbindFromTree();
michael@0 3964 return rv;
michael@0 3965 }
michael@0 3966 }
michael@0 3967
michael@0 3968 return NS_OK;
michael@0 3969 }
michael@0 3970
michael@0 3971 static
michael@0 3972 bool IsXULDisplayType(const nsStyleDisplay* aDisplay)
michael@0 3973 {
michael@0 3974 return (aDisplay->mDisplay == NS_STYLE_DISPLAY_INLINE_BOX ||
michael@0 3975 #ifdef MOZ_XUL
michael@0 3976 aDisplay->mDisplay == NS_STYLE_DISPLAY_INLINE_XUL_GRID ||
michael@0 3977 aDisplay->mDisplay == NS_STYLE_DISPLAY_INLINE_STACK ||
michael@0 3978 #endif
michael@0 3979 aDisplay->mDisplay == NS_STYLE_DISPLAY_BOX
michael@0 3980 #ifdef MOZ_XUL
michael@0 3981 || aDisplay->mDisplay == NS_STYLE_DISPLAY_XUL_GRID ||
michael@0 3982 aDisplay->mDisplay == NS_STYLE_DISPLAY_STACK ||
michael@0 3983 aDisplay->mDisplay == NS_STYLE_DISPLAY_XUL_GRID_GROUP ||
michael@0 3984 aDisplay->mDisplay == NS_STYLE_DISPLAY_XUL_GRID_LINE ||
michael@0 3985 aDisplay->mDisplay == NS_STYLE_DISPLAY_DECK ||
michael@0 3986 aDisplay->mDisplay == NS_STYLE_DISPLAY_POPUP ||
michael@0 3987 aDisplay->mDisplay == NS_STYLE_DISPLAY_GROUPBOX
michael@0 3988 #endif
michael@0 3989 );
michael@0 3990 }
michael@0 3991
michael@0 3992
michael@0 3993 // XUL frames are not allowed to be out of flow.
michael@0 3994 #define SIMPLE_XUL_FCDATA(_func) \
michael@0 3995 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH, \
michael@0 3996 _func)
michael@0 3997 #define SCROLLABLE_XUL_FCDATA(_func) \
michael@0 3998 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH | \
michael@0 3999 FCDATA_MAY_NEED_SCROLLFRAME, _func)
michael@0 4000 // .. but we allow some XUL frames to be _containers_ for out-of-flow content
michael@0 4001 // (This is the same as SCROLLABLE_XUL_FCDATA, but w/o FCDATA_SKIP_ABSPOS_PUSH)
michael@0 4002 #define SCROLLABLE_ABSPOS_CONTAINER_XUL_FCDATA(_func) \
michael@0 4003 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | \
michael@0 4004 FCDATA_MAY_NEED_SCROLLFRAME, _func)
michael@0 4005
michael@0 4006 #define SIMPLE_XUL_CREATE(_tag, _func) \
michael@0 4007 { &nsGkAtoms::_tag, SIMPLE_XUL_FCDATA(_func) }
michael@0 4008 #define SCROLLABLE_XUL_CREATE(_tag, _func) \
michael@0 4009 { &nsGkAtoms::_tag, SCROLLABLE_XUL_FCDATA(_func) }
michael@0 4010 #define SIMPLE_XUL_INT_CREATE(_int, _func) \
michael@0 4011 { _int, SIMPLE_XUL_FCDATA(_func) }
michael@0 4012 #define SCROLLABLE_XUL_INT_CREATE(_int, _func) \
michael@0 4013 { _int, SCROLLABLE_XUL_FCDATA(_func) }
michael@0 4014 #define SCROLLABLE_ABSPOS_CONTAINER_XUL_INT_CREATE(_int, _func) \
michael@0 4015 { _int, SCROLLABLE_ABSPOS_CONTAINER_XUL_FCDATA(_func) }
michael@0 4016
michael@0 4017 static
michael@0 4018 nsIFrame* NS_NewGridBoxFrame(nsIPresShell* aPresShell,
michael@0 4019 nsStyleContext* aStyleContext)
michael@0 4020 {
michael@0 4021 nsCOMPtr<nsBoxLayout> layout;
michael@0 4022 NS_NewGridLayout2(aPresShell, getter_AddRefs(layout));
michael@0 4023 return NS_NewBoxFrame(aPresShell, aStyleContext, false, layout);
michael@0 4024 }
michael@0 4025
michael@0 4026 /* static */
michael@0 4027 const nsCSSFrameConstructor::FrameConstructionData*
michael@0 4028 nsCSSFrameConstructor::FindXULTagData(Element* aElement,
michael@0 4029 nsIAtom* aTag,
michael@0 4030 int32_t aNameSpaceID,
michael@0 4031 nsStyleContext* aStyleContext)
michael@0 4032 {
michael@0 4033 if (aNameSpaceID != kNameSpaceID_XUL) {
michael@0 4034 return nullptr;
michael@0 4035 }
michael@0 4036
michael@0 4037 static const FrameConstructionDataByTag sXULTagData[] = {
michael@0 4038 #ifdef MOZ_XUL
michael@0 4039 SCROLLABLE_XUL_CREATE(button, NS_NewButtonBoxFrame),
michael@0 4040 SCROLLABLE_XUL_CREATE(checkbox, NS_NewButtonBoxFrame),
michael@0 4041 SCROLLABLE_XUL_CREATE(radio, NS_NewButtonBoxFrame),
michael@0 4042 SCROLLABLE_XUL_CREATE(autorepeatbutton, NS_NewAutoRepeatBoxFrame),
michael@0 4043 SCROLLABLE_XUL_CREATE(titlebar, NS_NewTitleBarFrame),
michael@0 4044 SCROLLABLE_XUL_CREATE(resizer, NS_NewResizerFrame),
michael@0 4045 SIMPLE_XUL_CREATE(image, NS_NewImageBoxFrame),
michael@0 4046 SIMPLE_XUL_CREATE(spring, NS_NewLeafBoxFrame),
michael@0 4047 SIMPLE_XUL_CREATE(spacer, NS_NewLeafBoxFrame),
michael@0 4048 SIMPLE_XUL_CREATE(treechildren, NS_NewTreeBodyFrame),
michael@0 4049 SIMPLE_XUL_CREATE(treecol, NS_NewTreeColFrame),
michael@0 4050 SIMPLE_XUL_CREATE(text, NS_NewTextBoxFrame),
michael@0 4051 SIMPLE_TAG_CHAIN(label, nsCSSFrameConstructor::FindXULLabelData),
michael@0 4052 SIMPLE_TAG_CHAIN(description, nsCSSFrameConstructor::FindXULDescriptionData),
michael@0 4053 SIMPLE_XUL_CREATE(menu, NS_NewMenuFrame),
michael@0 4054 SIMPLE_XUL_CREATE(menubutton, NS_NewMenuFrame),
michael@0 4055 SIMPLE_XUL_CREATE(menuitem, NS_NewMenuItemFrame),
michael@0 4056 #ifdef XP_MACOSX
michael@0 4057 SIMPLE_TAG_CHAIN(menubar, nsCSSFrameConstructor::FindXULMenubarData),
michael@0 4058 #else
michael@0 4059 SIMPLE_XUL_CREATE(menubar, NS_NewMenuBarFrame),
michael@0 4060 #endif /* XP_MACOSX */
michael@0 4061 SIMPLE_TAG_CHAIN(popupgroup, nsCSSFrameConstructor::FindPopupGroupData),
michael@0 4062 SIMPLE_XUL_CREATE(iframe, NS_NewSubDocumentFrame),
michael@0 4063 SIMPLE_XUL_CREATE(editor, NS_NewSubDocumentFrame),
michael@0 4064 SIMPLE_XUL_CREATE(browser, NS_NewSubDocumentFrame),
michael@0 4065 SIMPLE_XUL_CREATE(progressmeter, NS_NewProgressMeterFrame),
michael@0 4066 SIMPLE_XUL_CREATE(splitter, NS_NewSplitterFrame),
michael@0 4067 SIMPLE_TAG_CHAIN(listboxbody,
michael@0 4068 nsCSSFrameConstructor::FindXULListBoxBodyData),
michael@0 4069 SIMPLE_TAG_CHAIN(listitem, nsCSSFrameConstructor::FindXULListItemData),
michael@0 4070 #endif /* MOZ_XUL */
michael@0 4071 SIMPLE_XUL_CREATE(slider, NS_NewSliderFrame),
michael@0 4072 SIMPLE_XUL_CREATE(scrollbar, NS_NewScrollbarFrame),
michael@0 4073 SIMPLE_XUL_CREATE(scrollbarbutton, NS_NewScrollbarButtonFrame)
michael@0 4074 };
michael@0 4075
michael@0 4076 return FindDataByTag(aTag, aElement, aStyleContext, sXULTagData,
michael@0 4077 ArrayLength(sXULTagData));
michael@0 4078 }
michael@0 4079
michael@0 4080 #ifdef MOZ_XUL
michael@0 4081 /* static */
michael@0 4082 const nsCSSFrameConstructor::FrameConstructionData*
michael@0 4083 nsCSSFrameConstructor::FindPopupGroupData(Element* aElement,
michael@0 4084 nsStyleContext* /* unused */)
michael@0 4085 {
michael@0 4086 if (!aElement->IsRootOfNativeAnonymousSubtree()) {
michael@0 4087 return nullptr;
michael@0 4088 }
michael@0 4089
michael@0 4090 static const FrameConstructionData sPopupSetData =
michael@0 4091 SIMPLE_XUL_FCDATA(NS_NewPopupSetFrame);
michael@0 4092 return &sPopupSetData;
michael@0 4093 }
michael@0 4094
michael@0 4095 /* static */
michael@0 4096 const nsCSSFrameConstructor::FrameConstructionData
michael@0 4097 nsCSSFrameConstructor::sXULTextBoxData = SIMPLE_XUL_FCDATA(NS_NewTextBoxFrame);
michael@0 4098
michael@0 4099 /* static */
michael@0 4100 const nsCSSFrameConstructor::FrameConstructionData*
michael@0 4101 nsCSSFrameConstructor::FindXULLabelData(Element* aElement,
michael@0 4102 nsStyleContext* /* unused */)
michael@0 4103 {
michael@0 4104 if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
michael@0 4105 return &sXULTextBoxData;
michael@0 4106 }
michael@0 4107
michael@0 4108 static const FrameConstructionData sLabelData =
michael@0 4109 SIMPLE_XUL_FCDATA(NS_NewXULLabelFrame);
michael@0 4110 return &sLabelData;
michael@0 4111 }
michael@0 4112
michael@0 4113 static nsIFrame*
michael@0 4114 NS_NewXULDescriptionFrame(nsIPresShell* aPresShell, nsStyleContext *aContext)
michael@0 4115 {
michael@0 4116 // XXXbz do we really need to set those flags? If the parent is not
michael@0 4117 // a block we'll get them anyway, and if it is, do we want them?
michael@0 4118 return NS_NewBlockFrame(aPresShell, aContext,
michael@0 4119 NS_BLOCK_FLOAT_MGR | NS_BLOCK_MARGIN_ROOT);
michael@0 4120 }
michael@0 4121
michael@0 4122 /* static */
michael@0 4123 const nsCSSFrameConstructor::FrameConstructionData*
michael@0 4124 nsCSSFrameConstructor::FindXULDescriptionData(Element* aElement,
michael@0 4125 nsStyleContext* /* unused */)
michael@0 4126 {
michael@0 4127 if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
michael@0 4128 return &sXULTextBoxData;
michael@0 4129 }
michael@0 4130
michael@0 4131 static const FrameConstructionData sDescriptionData =
michael@0 4132 SIMPLE_XUL_FCDATA(NS_NewXULDescriptionFrame);
michael@0 4133 return &sDescriptionData;
michael@0 4134 }
michael@0 4135
michael@0 4136 #ifdef XP_MACOSX
michael@0 4137 /* static */
michael@0 4138 const nsCSSFrameConstructor::FrameConstructionData*
michael@0 4139 nsCSSFrameConstructor::FindXULMenubarData(Element* aElement,
michael@0 4140 nsStyleContext* aStyleContext)
michael@0 4141 {
michael@0 4142 nsCOMPtr<nsIDocShell> treeItem =
michael@0 4143 aStyleContext->PresContext()->GetDocShell();
michael@0 4144 if (treeItem && nsIDocShellTreeItem::typeChrome == treeItem->ItemType()) {
michael@0 4145 nsCOMPtr<nsIDocShellTreeItem> parent;
michael@0 4146 treeItem->GetParent(getter_AddRefs(parent));
michael@0 4147 if (!parent) {
michael@0 4148 // This is the root. Suppress the menubar, since on Mac
michael@0 4149 // window menus are not attached to the window.
michael@0 4150 static const FrameConstructionData sSuppressData = SUPPRESS_FCDATA();
michael@0 4151 return &sSuppressData;
michael@0 4152 }
michael@0 4153 }
michael@0 4154
michael@0 4155 static const FrameConstructionData sMenubarData =
michael@0 4156 SIMPLE_XUL_FCDATA(NS_NewMenuBarFrame);
michael@0 4157 return &sMenubarData;
michael@0 4158 }
michael@0 4159 #endif /* XP_MACOSX */
michael@0 4160
michael@0 4161 /* static */
michael@0 4162 const nsCSSFrameConstructor::FrameConstructionData*
michael@0 4163 nsCSSFrameConstructor::FindXULListBoxBodyData(Element* aElement,
michael@0 4164 nsStyleContext* aStyleContext)
michael@0 4165 {
michael@0 4166 if (aStyleContext->StyleDisplay()->mDisplay !=
michael@0 4167 NS_STYLE_DISPLAY_XUL_GRID_GROUP) {
michael@0 4168 return nullptr;
michael@0 4169 }
michael@0 4170
michael@0 4171 static const FrameConstructionData sListBoxBodyData =
michael@0 4172 SCROLLABLE_XUL_FCDATA(NS_NewListBoxBodyFrame);
michael@0 4173 return &sListBoxBodyData;
michael@0 4174 }
michael@0 4175
michael@0 4176 /* static */
michael@0 4177 const nsCSSFrameConstructor::FrameConstructionData*
michael@0 4178 nsCSSFrameConstructor::FindXULListItemData(Element* aElement,
michael@0 4179 nsStyleContext* aStyleContext)
michael@0 4180 {
michael@0 4181 if (aStyleContext->StyleDisplay()->mDisplay !=
michael@0 4182 NS_STYLE_DISPLAY_XUL_GRID_LINE) {
michael@0 4183 return nullptr;
michael@0 4184 }
michael@0 4185
michael@0 4186 static const FrameConstructionData sListItemData =
michael@0 4187 SCROLLABLE_XUL_FCDATA(NS_NewListItemFrame);
michael@0 4188 return &sListItemData;
michael@0 4189 }
michael@0 4190
michael@0 4191 #endif /* MOZ_XUL */
michael@0 4192
michael@0 4193 /* static */
michael@0 4194 const nsCSSFrameConstructor::FrameConstructionData*
michael@0 4195 nsCSSFrameConstructor::FindXULDisplayData(const nsStyleDisplay* aDisplay,
michael@0 4196 Element* aElement,
michael@0 4197 nsStyleContext* aStyleContext)
michael@0 4198 {
michael@0 4199 static const FrameConstructionDataByInt sXULDisplayData[] = {
michael@0 4200 SCROLLABLE_ABSPOS_CONTAINER_XUL_INT_CREATE(NS_STYLE_DISPLAY_INLINE_BOX,
michael@0 4201 NS_NewBoxFrame),
michael@0 4202 SCROLLABLE_ABSPOS_CONTAINER_XUL_INT_CREATE(NS_STYLE_DISPLAY_BOX,
michael@0 4203 NS_NewBoxFrame),
michael@0 4204 #ifdef MOZ_XUL
michael@0 4205 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_INLINE_XUL_GRID, NS_NewGridBoxFrame),
michael@0 4206 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_XUL_GRID, NS_NewGridBoxFrame),
michael@0 4207 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_XUL_GRID_GROUP,
michael@0 4208 NS_NewGridRowGroupFrame),
michael@0 4209 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_XUL_GRID_LINE,
michael@0 4210 NS_NewGridRowLeafFrame),
michael@0 4211 SIMPLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_DECK, NS_NewDeckFrame),
michael@0 4212 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_GROUPBOX, NS_NewGroupBoxFrame),
michael@0 4213 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_INLINE_STACK, NS_NewStackFrame),
michael@0 4214 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_STACK, NS_NewStackFrame),
michael@0 4215 { NS_STYLE_DISPLAY_POPUP,
michael@0 4216 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_IS_POPUP |
michael@0 4217 FCDATA_SKIP_ABSPOS_PUSH, NS_NewMenuPopupFrame) }
michael@0 4218 #endif /* MOZ_XUL */
michael@0 4219 };
michael@0 4220
michael@0 4221 // Processing by display here:
michael@0 4222 return FindDataByInt(aDisplay->mDisplay, aElement, aStyleContext,
michael@0 4223 sXULDisplayData, ArrayLength(sXULDisplayData));
michael@0 4224 }
michael@0 4225
michael@0 4226 already_AddRefed<nsStyleContext>
michael@0 4227 nsCSSFrameConstructor::BeginBuildingScrollFrame(nsFrameConstructorState& aState,
michael@0 4228 nsIContent* aContent,
michael@0 4229 nsStyleContext* aContentStyle,
michael@0 4230 nsIFrame* aParentFrame,
michael@0 4231 nsIAtom* aScrolledPseudo,
michael@0 4232 bool aIsRoot,
michael@0 4233 nsIFrame*& aNewFrame)
michael@0 4234 {
michael@0 4235 nsIFrame* gfxScrollFrame = aNewFrame;
michael@0 4236
michael@0 4237 nsFrameItems anonymousItems;
michael@0 4238
michael@0 4239 nsRefPtr<nsStyleContext> contentStyle = aContentStyle;
michael@0 4240
michael@0 4241 if (!gfxScrollFrame) {
michael@0 4242 // Build a XULScrollFrame when the child is a box, otherwise an
michael@0 4243 // HTMLScrollFrame
michael@0 4244 // XXXbz this is the lone remaining consumer of IsXULDisplayType.
michael@0 4245 // I wonder whether we can eliminate that somehow.
michael@0 4246 const nsStyleDisplay* displayStyle = aContentStyle->StyleDisplay();
michael@0 4247 if (IsXULDisplayType(displayStyle)) {
michael@0 4248 gfxScrollFrame = NS_NewXULScrollFrame(mPresShell, contentStyle, aIsRoot,
michael@0 4249 displayStyle->mDisplay == NS_STYLE_DISPLAY_STACK ||
michael@0 4250 displayStyle->mDisplay == NS_STYLE_DISPLAY_INLINE_STACK);
michael@0 4251 } else {
michael@0 4252 gfxScrollFrame = NS_NewHTMLScrollFrame(mPresShell, contentStyle, aIsRoot);
michael@0 4253 }
michael@0 4254
michael@0 4255 InitAndRestoreFrame(aState, aContent, aParentFrame, gfxScrollFrame);
michael@0 4256 }
michael@0 4257
michael@0 4258 // if there are any anonymous children for the scroll frame, create
michael@0 4259 // frames for them.
michael@0 4260 // Pass a null pending binding: we don't care how constructors for any of
michael@0 4261 // this anonymous content order with anything else. It's never been
michael@0 4262 // consistent anyway.
michael@0 4263 CreateAnonymousFrames(aState, aContent, gfxScrollFrame, nullptr,
michael@0 4264 anonymousItems);
michael@0 4265
michael@0 4266 aNewFrame = gfxScrollFrame;
michael@0 4267
michael@0 4268 // we used the style that was passed in. So resolve another one.
michael@0 4269 nsStyleSet *styleSet = mPresShell->StyleSet();
michael@0 4270 nsRefPtr<nsStyleContext> scrolledChildStyle =
michael@0 4271 styleSet->ResolveAnonymousBoxStyle(aScrolledPseudo, contentStyle);
michael@0 4272
michael@0 4273 if (gfxScrollFrame) {
michael@0 4274 gfxScrollFrame->SetInitialChildList(kPrincipalList, anonymousItems);
michael@0 4275 }
michael@0 4276
michael@0 4277 return scrolledChildStyle.forget();
michael@0 4278 }
michael@0 4279
michael@0 4280 void
michael@0 4281 nsCSSFrameConstructor::FinishBuildingScrollFrame(nsIFrame* aScrollFrame,
michael@0 4282 nsIFrame* aScrolledFrame)
michael@0 4283 {
michael@0 4284 nsFrameList scrolled(aScrolledFrame, aScrolledFrame);
michael@0 4285 aScrollFrame->AppendFrames(kPrincipalList, scrolled);
michael@0 4286 }
michael@0 4287
michael@0 4288
michael@0 4289 /**
michael@0 4290 * Called to wrap a gfx scrollframe around a frame. The hierarchy will look like this
michael@0 4291 *
michael@0 4292 * ------- for gfx scrollbars ------
michael@0 4293 *
michael@0 4294 *
michael@0 4295 * ScrollFrame
michael@0 4296 * ^
michael@0 4297 * |
michael@0 4298 * Frame (scrolled frame you passed in)
michael@0 4299 *
michael@0 4300 *
michael@0 4301 *-----------------------------------
michael@0 4302 * LEGEND:
michael@0 4303 *
michael@0 4304 * ScrollFrame: This is a frame that manages gfx cross platform frame based scrollbars.
michael@0 4305 *
michael@0 4306 * @param aContent the content node of the child to wrap.
michael@0 4307 * @param aScrolledFrame The frame of the content to wrap. This should not be
michael@0 4308 * Initialized. This method will initialize it with a scrolled pseudo
michael@0 4309 * and no nsIContent. The content will be attached to the scrollframe
michael@0 4310 * returned.
michael@0 4311 * @param aContentStyle the style context that has already been resolved for the content being passed in.
michael@0 4312 *
michael@0 4313 * @param aParentFrame The parent to attach the scroll frame to
michael@0 4314 *
michael@0 4315 * @param aNewFrame The new scrollframe or gfx scrollframe that we create. It will contain the
michael@0 4316 * scrolled frame you passed in. (returned)
michael@0 4317 * If this is not null, we'll just use it
michael@0 4318 * @param aScrolledContentStyle the style that was resolved for the scrolled frame. (returned)
michael@0 4319 */
michael@0 4320 nsresult
michael@0 4321 nsCSSFrameConstructor::BuildScrollFrame(nsFrameConstructorState& aState,
michael@0 4322 nsIContent* aContent,
michael@0 4323 nsStyleContext* aContentStyle,
michael@0 4324 nsIFrame* aScrolledFrame,
michael@0 4325 nsIFrame* aParentFrame,
michael@0 4326 nsIFrame*& aNewFrame)
michael@0 4327 {
michael@0 4328 nsRefPtr<nsStyleContext> scrolledContentStyle =
michael@0 4329 BeginBuildingScrollFrame(aState, aContent, aContentStyle, aParentFrame,
michael@0 4330 nsCSSAnonBoxes::scrolledContent,
michael@0 4331 false, aNewFrame);
michael@0 4332
michael@0 4333 aScrolledFrame->SetStyleContextWithoutNotification(scrolledContentStyle);
michael@0 4334 InitAndRestoreFrame(aState, aContent, aNewFrame, aScrolledFrame);
michael@0 4335
michael@0 4336 FinishBuildingScrollFrame(aNewFrame, aScrolledFrame);
michael@0 4337 return NS_OK;
michael@0 4338 }
michael@0 4339
michael@0 4340 const nsCSSFrameConstructor::FrameConstructionData*
michael@0 4341 nsCSSFrameConstructor::FindDisplayData(const nsStyleDisplay* aDisplay,
michael@0 4342 Element* aElement,
michael@0 4343 nsIFrame* aParentFrame,
michael@0 4344 nsStyleContext* aStyleContext)
michael@0 4345 {
michael@0 4346 PR_STATIC_ASSERT(eParentTypeCount < (1 << (32 - FCDATA_PARENT_TYPE_OFFSET)));
michael@0 4347
michael@0 4348 // The style system ensures that floated and positioned frames are
michael@0 4349 // block-level.
michael@0 4350 NS_ASSERTION(!(aDisplay->IsFloatingStyle() ||
michael@0 4351 aDisplay->IsAbsolutelyPositionedStyle()) ||
michael@0 4352 aDisplay->IsBlockOutsideStyle(),
michael@0 4353 "Style system did not apply CSS2.1 section 9.7 fixups");
michael@0 4354
michael@0 4355 // If this is "body", try propagating its scroll style to the viewport
michael@0 4356 // Note that we need to do this even if the body is NOT scrollable;
michael@0 4357 // it might have dynamically changed from scrollable to not scrollable,
michael@0 4358 // and that might need to be propagated.
michael@0 4359 // XXXbz is this the right place to do this? If this code moves,
michael@0 4360 // make this function static.
michael@0 4361 bool propagatedScrollToViewport = false;
michael@0 4362 if (aElement->IsHTML(nsGkAtoms::body)) {
michael@0 4363 propagatedScrollToViewport =
michael@0 4364 PropagateScrollToViewport() == aElement;
michael@0 4365 }
michael@0 4366
michael@0 4367 NS_ASSERTION(!propagatedScrollToViewport ||
michael@0 4368 !mPresShell->GetPresContext()->IsPaginated(),
michael@0 4369 "Shouldn't propagate scroll in paginated contexts");
michael@0 4370
michael@0 4371 // If the frame is a block-level frame and is scrollable, then wrap it in a
michael@0 4372 // scroll frame.
michael@0 4373 // XXX Ignore tables for the time being
michael@0 4374 // XXXbz it would be nice to combine this with the other block
michael@0 4375 // case... Think about how do do this?
michael@0 4376 if (aDisplay->IsBlockInsideStyle() &&
michael@0 4377 aDisplay->IsScrollableOverflow() &&
michael@0 4378 !propagatedScrollToViewport) {
michael@0 4379 // Except we don't want to do that for paginated contexts for
michael@0 4380 // frames that are block-outside and aren't frames for native
michael@0 4381 // anonymous stuff.
michael@0 4382 if (mPresShell->GetPresContext()->IsPaginated() &&
michael@0 4383 aDisplay->IsBlockOutsideStyle() &&
michael@0 4384 !aElement->IsInNativeAnonymousSubtree()) {
michael@0 4385 static const FrameConstructionData sForcedNonScrollableBlockData =
michael@0 4386 FULL_CTOR_FCDATA(FCDATA_FORCED_NON_SCROLLABLE_BLOCK,
michael@0 4387 &nsCSSFrameConstructor::ConstructNonScrollableBlock);
michael@0 4388 return &sForcedNonScrollableBlockData;
michael@0 4389 }
michael@0 4390
michael@0 4391 static const FrameConstructionData sScrollableBlockData =
michael@0 4392 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructScrollableBlock);
michael@0 4393 return &sScrollableBlockData;
michael@0 4394 }
michael@0 4395
michael@0 4396 // Handle various non-scrollable blocks
michael@0 4397 if (aDisplay->IsBlockInsideStyle()) {
michael@0 4398 static const FrameConstructionData sNonScrollableBlockData =
michael@0 4399 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructNonScrollableBlock);
michael@0 4400 return &sNonScrollableBlockData;
michael@0 4401 }
michael@0 4402
michael@0 4403 static const FrameConstructionDataByInt sDisplayData[] = {
michael@0 4404 // To keep the hash table small don't add inline frames (they're
michael@0 4405 // typically things like FONT and B), because we can quickly
michael@0 4406 // find them if we need to.
michael@0 4407 // XXXbz the "quickly" part is a bald-faced lie!
michael@0 4408 { NS_STYLE_DISPLAY_INLINE,
michael@0 4409 FULL_CTOR_FCDATA(FCDATA_IS_INLINE | FCDATA_IS_LINE_PARTICIPANT,
michael@0 4410 &nsCSSFrameConstructor::ConstructInline) },
michael@0 4411 { NS_STYLE_DISPLAY_FLEX,
michael@0 4412 FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame) },
michael@0 4413 { NS_STYLE_DISPLAY_INLINE_FLEX,
michael@0 4414 FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame) },
michael@0 4415 { NS_STYLE_DISPLAY_GRID,
michael@0 4416 FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewGridContainerFrame) },
michael@0 4417 { NS_STYLE_DISPLAY_INLINE_GRID,
michael@0 4418 FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewGridContainerFrame) },
michael@0 4419 { NS_STYLE_DISPLAY_TABLE,
michael@0 4420 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructTable) },
michael@0 4421 { NS_STYLE_DISPLAY_INLINE_TABLE,
michael@0 4422 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructTable) },
michael@0 4423 // NOTE: In the unlikely event that we add another table-part here that has
michael@0 4424 // a desired-parent-type (& hence triggers table fixup), we'll need to also
michael@0 4425 // update the flexbox chunk in nsStyleContext::ApplyStyleFixups().
michael@0 4426 { NS_STYLE_DISPLAY_TABLE_CAPTION,
michael@0 4427 FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_ALLOW_BLOCK_STYLES |
michael@0 4428 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH |
michael@0 4429 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
michael@0 4430 NS_NewTableCaptionFrame) },
michael@0 4431 { NS_STYLE_DISPLAY_TABLE_ROW_GROUP,
michael@0 4432 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
michael@0 4433 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
michael@0 4434 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup) },
michael@0 4435 { NS_STYLE_DISPLAY_TABLE_HEADER_GROUP,
michael@0 4436 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
michael@0 4437 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
michael@0 4438 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup) },
michael@0 4439 { NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP,
michael@0 4440 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
michael@0 4441 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
michael@0 4442 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup) },
michael@0 4443 { NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP,
michael@0 4444 FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW |
michael@0 4445 FCDATA_SKIP_ABSPOS_PUSH |
michael@0 4446 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
michael@0 4447 NS_NewTableColGroupFrame) },
michael@0 4448 { NS_STYLE_DISPLAY_TABLE_COLUMN,
michael@0 4449 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
michael@0 4450 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeColGroup),
michael@0 4451 &nsCSSFrameConstructor::ConstructTableCol) },
michael@0 4452 { NS_STYLE_DISPLAY_TABLE_ROW,
michael@0 4453 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
michael@0 4454 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup),
michael@0 4455 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup) },
michael@0 4456 { NS_STYLE_DISPLAY_TABLE_CELL,
michael@0 4457 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
michael@0 4458 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow),
michael@0 4459 &nsCSSFrameConstructor::ConstructTableCell) }
michael@0 4460 };
michael@0 4461
michael@0 4462 return FindDataByInt(aDisplay->mDisplay,
michael@0 4463 aElement, aStyleContext, sDisplayData,
michael@0 4464 ArrayLength(sDisplayData));
michael@0 4465 }
michael@0 4466
michael@0 4467 nsIFrame*
michael@0 4468 nsCSSFrameConstructor::ConstructScrollableBlock(nsFrameConstructorState& aState,
michael@0 4469 FrameConstructionItem& aItem,
michael@0 4470 nsIFrame* aParentFrame,
michael@0 4471 const nsStyleDisplay* aDisplay,
michael@0 4472 nsFrameItems& aFrameItems)
michael@0 4473 {
michael@0 4474 nsIContent* const content = aItem.mContent;
michael@0 4475 nsStyleContext* const styleContext = aItem.mStyleContext;
michael@0 4476
michael@0 4477 nsIFrame* newFrame = nullptr;
michael@0 4478 nsRefPtr<nsStyleContext> scrolledContentStyle
michael@0 4479 = BeginBuildingScrollFrame(aState, content, styleContext,
michael@0 4480 aState.GetGeometricParent(aDisplay, aParentFrame),
michael@0 4481 nsCSSAnonBoxes::scrolledContent,
michael@0 4482 false, newFrame);
michael@0 4483
michael@0 4484 // Create our block frame
michael@0 4485 // pass a temporary stylecontext, the correct one will be set later
michael@0 4486 nsIFrame* scrolledFrame =
michael@0 4487 NS_NewBlockFormattingContext(mPresShell, styleContext);
michael@0 4488
michael@0 4489 // Make sure to AddChild before we call ConstructBlock so that we
michael@0 4490 // end up before our descendants in fixed-pos lists as needed.
michael@0 4491 aState.AddChild(newFrame, aFrameItems, content, styleContext, aParentFrame);
michael@0 4492
michael@0 4493 nsFrameItems blockItem;
michael@0 4494 ConstructBlock(aState, scrolledContentStyle->StyleDisplay(), content,
michael@0 4495 newFrame, newFrame, scrolledContentStyle,
michael@0 4496 &scrolledFrame, blockItem,
michael@0 4497 aDisplay->IsPositioned(newFrame) ? newFrame : nullptr,
michael@0 4498 aItem.mPendingBinding);
michael@0 4499
michael@0 4500 NS_ASSERTION(blockItem.FirstChild() == scrolledFrame,
michael@0 4501 "Scrollframe's frameItems should be exactly the scrolled frame");
michael@0 4502 FinishBuildingScrollFrame(newFrame, scrolledFrame);
michael@0 4503
michael@0 4504 return newFrame;
michael@0 4505 }
michael@0 4506
michael@0 4507 nsIFrame*
michael@0 4508 nsCSSFrameConstructor::ConstructNonScrollableBlock(nsFrameConstructorState& aState,
michael@0 4509 FrameConstructionItem& aItem,
michael@0 4510 nsIFrame* aParentFrame,
michael@0 4511 const nsStyleDisplay* aDisplay,
michael@0 4512 nsFrameItems& aFrameItems)
michael@0 4513 {
michael@0 4514 nsStyleContext* const styleContext = aItem.mStyleContext;
michael@0 4515
michael@0 4516 // We want a block formatting context root in paginated contexts for
michael@0 4517 // every block that would be scrollable in a non-paginated context.
michael@0 4518 // We mark our blocks with a bit here if this condition is true, so
michael@0 4519 // we can check it later in nsFrame::ApplyPaginatedOverflowClipping.
michael@0 4520 bool clipPaginatedOverflow =
michael@0 4521 (aItem.mFCData->mBits & FCDATA_FORCED_NON_SCROLLABLE_BLOCK) != 0;
michael@0 4522 nsIFrame* newFrame;
michael@0 4523 if ((aDisplay->IsAbsolutelyPositionedStyle() ||
michael@0 4524 aDisplay->IsFloatingStyle() ||
michael@0 4525 NS_STYLE_DISPLAY_INLINE_BLOCK == aDisplay->mDisplay ||
michael@0 4526 clipPaginatedOverflow) &&
michael@0 4527 !aParentFrame->IsSVGText()) {
michael@0 4528 newFrame = NS_NewBlockFormattingContext(mPresShell, styleContext);
michael@0 4529 if (clipPaginatedOverflow) {
michael@0 4530 newFrame->AddStateBits(NS_BLOCK_CLIP_PAGINATED_OVERFLOW);
michael@0 4531 }
michael@0 4532 } else {
michael@0 4533 newFrame = NS_NewBlockFrame(mPresShell, styleContext);
michael@0 4534 }
michael@0 4535
michael@0 4536 ConstructBlock(aState, aDisplay, aItem.mContent,
michael@0 4537 aState.GetGeometricParent(aDisplay, aParentFrame),
michael@0 4538 aParentFrame, styleContext, &newFrame,
michael@0 4539 aFrameItems,
michael@0 4540 aDisplay->IsPositioned(newFrame) ? newFrame : nullptr,
michael@0 4541 aItem.mPendingBinding);
michael@0 4542 return newFrame;
michael@0 4543 }
michael@0 4544
michael@0 4545
michael@0 4546 void
michael@0 4547 nsCSSFrameConstructor::InitAndRestoreFrame(const nsFrameConstructorState& aState,
michael@0 4548 nsIContent* aContent,
michael@0 4549 nsIFrame* aParentFrame,
michael@0 4550 nsIFrame* aNewFrame,
michael@0 4551 bool aAllowCounters)
michael@0 4552 {
michael@0 4553 NS_PRECONDITION(mUpdateCount != 0,
michael@0 4554 "Should be in an update while creating frames");
michael@0 4555
michael@0 4556 MOZ_ASSERT(aNewFrame, "Null frame cannot be initialized");
michael@0 4557
michael@0 4558 // Initialize the frame
michael@0 4559 aNewFrame->Init(aContent, aParentFrame, nullptr);
michael@0 4560 aNewFrame->AddStateBits(aState.mAdditionalStateBits);
michael@0 4561
michael@0 4562 if (aState.mFrameState) {
michael@0 4563 // Restore frame state for just the newly created frame.
michael@0 4564 RestoreFrameStateFor(aNewFrame, aState.mFrameState);
michael@0 4565 }
michael@0 4566
michael@0 4567 if (aAllowCounters &&
michael@0 4568 mCounterManager.AddCounterResetsAndIncrements(aNewFrame)) {
michael@0 4569 CountersDirty();
michael@0 4570 }
michael@0 4571 }
michael@0 4572
michael@0 4573 already_AddRefed<nsStyleContext>
michael@0 4574 nsCSSFrameConstructor::ResolveStyleContext(nsIFrame* aParentFrame,
michael@0 4575 nsIContent* aContent,
michael@0 4576 nsFrameConstructorState* aState)
michael@0 4577 {
michael@0 4578 nsStyleContext* parentStyleContext = nullptr;
michael@0 4579 NS_ASSERTION(aContent->GetParent(), "Must have parent here");
michael@0 4580
michael@0 4581 aParentFrame = nsFrame::CorrectStyleParentFrame(aParentFrame, nullptr);
michael@0 4582
michael@0 4583 if (aParentFrame) {
michael@0 4584 // Resolve the style context based on the content object and the parent
michael@0 4585 // style context
michael@0 4586 parentStyleContext = aParentFrame->StyleContext();
michael@0 4587 } else {
michael@0 4588 // Perhaps aParentFrame is a canvasFrame and we're replicating
michael@0 4589 // fixed-pos frames.
michael@0 4590 // XXX should we create a way to tell ConstructFrame which style
michael@0 4591 // context to use, and pass it the style context for the
michael@0 4592 // previous page's fixed-pos frame?
michael@0 4593 }
michael@0 4594
michael@0 4595 return ResolveStyleContext(parentStyleContext, aContent, aState);
michael@0 4596 }
michael@0 4597
michael@0 4598 already_AddRefed<nsStyleContext>
michael@0 4599 nsCSSFrameConstructor::ResolveStyleContext(nsStyleContext* aParentStyleContext,
michael@0 4600 nsIContent* aContent,
michael@0 4601 nsFrameConstructorState* aState)
michael@0 4602 {
michael@0 4603 nsStyleSet *styleSet = mPresShell->StyleSet();
michael@0 4604 aContent->OwnerDoc()->FlushPendingLinkUpdates();
michael@0 4605
michael@0 4606 if (aContent->IsElement()) {
michael@0 4607 if (aState) {
michael@0 4608 return styleSet->ResolveStyleFor(aContent->AsElement(),
michael@0 4609 aParentStyleContext,
michael@0 4610 aState->mTreeMatchContext);
michael@0 4611 }
michael@0 4612 return styleSet->ResolveStyleFor(aContent->AsElement(), aParentStyleContext);
michael@0 4613
michael@0 4614 }
michael@0 4615
michael@0 4616 NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
michael@0 4617 "shouldn't waste time creating style contexts for "
michael@0 4618 "comments and processing instructions");
michael@0 4619
michael@0 4620 return styleSet->ResolveStyleForNonElement(aParentStyleContext);
michael@0 4621 }
michael@0 4622
michael@0 4623 // MathML Mod - RBS
michael@0 4624 void
michael@0 4625 nsCSSFrameConstructor::FlushAccumulatedBlock(nsFrameConstructorState& aState,
michael@0 4626 nsIContent* aContent,
michael@0 4627 nsIFrame* aParentFrame,
michael@0 4628 nsFrameItems& aBlockItems,
michael@0 4629 nsFrameItems& aNewItems)
michael@0 4630 {
michael@0 4631 if (aBlockItems.IsEmpty()) {
michael@0 4632 // Nothing to do
michael@0 4633 return;
michael@0 4634 }
michael@0 4635
michael@0 4636 nsIAtom* anonPseudo = nsCSSAnonBoxes::mozMathMLAnonymousBlock;
michael@0 4637
michael@0 4638 nsStyleContext* parentContext =
michael@0 4639 nsFrame::CorrectStyleParentFrame(aParentFrame,
michael@0 4640 anonPseudo)->StyleContext();
michael@0 4641 nsStyleSet* styleSet = mPresShell->StyleSet();
michael@0 4642 nsRefPtr<nsStyleContext> blockContext;
michael@0 4643 blockContext = styleSet->
michael@0 4644 ResolveAnonymousBoxStyle(anonPseudo, parentContext);
michael@0 4645
michael@0 4646
michael@0 4647 // then, create a block frame that will wrap the child frames. Make it a
michael@0 4648 // MathML frame so that Get(Absolute/Float)ContainingBlockFor know that this
michael@0 4649 // is not a suitable block.
michael@0 4650 nsIFrame* blockFrame =
michael@0 4651 NS_NewMathMLmathBlockFrame(mPresShell, blockContext,
michael@0 4652 NS_BLOCK_FLOAT_MGR | NS_BLOCK_MARGIN_ROOT);
michael@0 4653
michael@0 4654 InitAndRestoreFrame(aState, aContent, aParentFrame, blockFrame);
michael@0 4655 ReparentFrames(this, blockFrame, aBlockItems);
michael@0 4656 // abs-pos and floats are disabled in MathML children so we don't have to
michael@0 4657 // worry about messing up those.
michael@0 4658 blockFrame->SetInitialChildList(kPrincipalList, aBlockItems);
michael@0 4659 NS_ASSERTION(aBlockItems.IsEmpty(), "What happened?");
michael@0 4660 aBlockItems.Clear();
michael@0 4661 aNewItems.AddChild(blockFrame);
michael@0 4662 }
michael@0 4663
michael@0 4664 // Only <math> elements can be floated or positioned. All other MathML
michael@0 4665 // should be in-flow.
michael@0 4666 #define SIMPLE_MATHML_CREATE(_tag, _func) \
michael@0 4667 { &nsGkAtoms::_tag, \
michael@0 4668 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | \
michael@0 4669 FCDATA_FORCE_NULL_ABSPOS_CONTAINER | \
michael@0 4670 FCDATA_WRAP_KIDS_IN_BLOCKS, _func) }
michael@0 4671
michael@0 4672 /* static */
michael@0 4673 const nsCSSFrameConstructor::FrameConstructionData*
michael@0 4674 nsCSSFrameConstructor::FindMathMLData(Element* aElement,
michael@0 4675 nsIAtom* aTag,
michael@0 4676 int32_t aNameSpaceID,
michael@0 4677 nsStyleContext* aStyleContext)
michael@0 4678 {
michael@0 4679 // Make sure that we remain confined in the MathML world
michael@0 4680 if (aNameSpaceID != kNameSpaceID_MathML)
michael@0 4681 return nullptr;
michael@0 4682
michael@0 4683 // Handle <math> specially, because it sometimes produces inlines
michael@0 4684 if (aTag == nsGkAtoms::math) {
michael@0 4685 // This needs to match the test in EnsureBlockDisplay in
michael@0 4686 // nsRuleNode.cpp. Though the behavior here for the display:table
michael@0 4687 // case is pretty weird...
michael@0 4688 if (aStyleContext->StyleDisplay()->IsBlockOutsideStyle()) {
michael@0 4689 static const FrameConstructionData sBlockMathData =
michael@0 4690 FCDATA_DECL(FCDATA_FORCE_NULL_ABSPOS_CONTAINER |
michael@0 4691 FCDATA_WRAP_KIDS_IN_BLOCKS,
michael@0 4692 NS_CreateNewMathMLmathBlockFrame);
michael@0 4693 return &sBlockMathData;
michael@0 4694 }
michael@0 4695
michael@0 4696 static const FrameConstructionData sInlineMathData =
michael@0 4697 FCDATA_DECL(FCDATA_FORCE_NULL_ABSPOS_CONTAINER |
michael@0 4698 FCDATA_IS_LINE_PARTICIPANT |
michael@0 4699 FCDATA_WRAP_KIDS_IN_BLOCKS,
michael@0 4700 NS_NewMathMLmathInlineFrame);
michael@0 4701 return &sInlineMathData;
michael@0 4702 }
michael@0 4703
michael@0 4704
michael@0 4705 static const FrameConstructionDataByTag sMathMLData[] = {
michael@0 4706 SIMPLE_MATHML_CREATE(annotation_, NS_NewMathMLTokenFrame),
michael@0 4707 SIMPLE_MATHML_CREATE(annotation_xml_, NS_NewMathMLmrowFrame),
michael@0 4708 SIMPLE_MATHML_CREATE(mi_, NS_NewMathMLTokenFrame),
michael@0 4709 SIMPLE_MATHML_CREATE(mn_, NS_NewMathMLTokenFrame),
michael@0 4710 SIMPLE_MATHML_CREATE(ms_, NS_NewMathMLTokenFrame),
michael@0 4711 SIMPLE_MATHML_CREATE(mtext_, NS_NewMathMLTokenFrame),
michael@0 4712 SIMPLE_MATHML_CREATE(mo_, NS_NewMathMLmoFrame),
michael@0 4713 SIMPLE_MATHML_CREATE(mfrac_, NS_NewMathMLmfracFrame),
michael@0 4714 SIMPLE_MATHML_CREATE(msup_, NS_NewMathMLmmultiscriptsFrame),
michael@0 4715 SIMPLE_MATHML_CREATE(msub_, NS_NewMathMLmmultiscriptsFrame),
michael@0 4716 SIMPLE_MATHML_CREATE(msubsup_, NS_NewMathMLmmultiscriptsFrame),
michael@0 4717 SIMPLE_MATHML_CREATE(munder_, NS_NewMathMLmunderoverFrame),
michael@0 4718 SIMPLE_MATHML_CREATE(mover_, NS_NewMathMLmunderoverFrame),
michael@0 4719 SIMPLE_MATHML_CREATE(munderover_, NS_NewMathMLmunderoverFrame),
michael@0 4720 SIMPLE_MATHML_CREATE(mphantom_, NS_NewMathMLmphantomFrame),
michael@0 4721 SIMPLE_MATHML_CREATE(mpadded_, NS_NewMathMLmpaddedFrame),
michael@0 4722 SIMPLE_MATHML_CREATE(mspace_, NS_NewMathMLmspaceFrame),
michael@0 4723 SIMPLE_MATHML_CREATE(none, NS_NewMathMLmspaceFrame),
michael@0 4724 SIMPLE_MATHML_CREATE(mprescripts_, NS_NewMathMLmspaceFrame),
michael@0 4725 SIMPLE_MATHML_CREATE(mfenced_, NS_NewMathMLmfencedFrame),
michael@0 4726 SIMPLE_MATHML_CREATE(mmultiscripts_, NS_NewMathMLmmultiscriptsFrame),
michael@0 4727 SIMPLE_MATHML_CREATE(mstyle_, NS_NewMathMLmrowFrame),
michael@0 4728 SIMPLE_MATHML_CREATE(msqrt_, NS_NewMathMLmsqrtFrame),
michael@0 4729 SIMPLE_MATHML_CREATE(mroot_, NS_NewMathMLmrootFrame),
michael@0 4730 SIMPLE_MATHML_CREATE(maction_, NS_NewMathMLmactionFrame),
michael@0 4731 SIMPLE_MATHML_CREATE(mrow_, NS_NewMathMLmrowFrame),
michael@0 4732 SIMPLE_MATHML_CREATE(merror_, NS_NewMathMLmrowFrame),
michael@0 4733 SIMPLE_MATHML_CREATE(menclose_, NS_NewMathMLmencloseFrame),
michael@0 4734 SIMPLE_MATHML_CREATE(semantics_, NS_NewMathMLsemanticsFrame)
michael@0 4735 };
michael@0 4736
michael@0 4737 return FindDataByTag(aTag, aElement, aStyleContext, sMathMLData,
michael@0 4738 ArrayLength(sMathMLData));
michael@0 4739 }
michael@0 4740
michael@0 4741
michael@0 4742 nsIFrame*
michael@0 4743 nsCSSFrameConstructor::ConstructFrameWithAnonymousChild(
michael@0 4744 nsFrameConstructorState& aState,
michael@0 4745 FrameConstructionItem& aItem,
michael@0 4746 nsIFrame* aParentFrame,
michael@0 4747 const nsStyleDisplay* aDisplay,
michael@0 4748 nsFrameItems& aFrameItems,
michael@0 4749 FrameCreationFunc aConstructor,
michael@0 4750 FrameCreationFunc aInnerConstructor,
michael@0 4751 nsICSSAnonBoxPseudo* aInnerPseudo,
michael@0 4752 bool aCandidateRootFrame)
michael@0 4753 {
michael@0 4754 nsIContent* const content = aItem.mContent;
michael@0 4755 nsStyleContext* const styleContext = aItem.mStyleContext;
michael@0 4756
michael@0 4757 // Create the outer frame:
michael@0 4758 nsIFrame* newFrame = aConstructor(mPresShell, styleContext);
michael@0 4759
michael@0 4760 InitAndRestoreFrame(aState, content,
michael@0 4761 aCandidateRootFrame ?
michael@0 4762 aState.GetGeometricParent(styleContext->StyleDisplay(),
michael@0 4763 aParentFrame) :
michael@0 4764 aParentFrame,
michael@0 4765 newFrame);
michael@0 4766
michael@0 4767 // Create the pseudo SC for the anonymous wrapper child as a child of the SC:
michael@0 4768 nsRefPtr<nsStyleContext> scForAnon;
michael@0 4769 scForAnon = mPresShell->StyleSet()->
michael@0 4770 ResolveAnonymousBoxStyle(aInnerPseudo, styleContext);
michael@0 4771
michael@0 4772 // Create the anonymous inner wrapper frame
michael@0 4773 nsIFrame* innerFrame = aInnerConstructor(mPresShell, scForAnon);
michael@0 4774
michael@0 4775 InitAndRestoreFrame(aState, content, newFrame, innerFrame);
michael@0 4776
michael@0 4777 // Put the newly created frames into the right child list
michael@0 4778 SetInitialSingleChild(newFrame, innerFrame);
michael@0 4779
michael@0 4780 aState.AddChild(newFrame, aFrameItems, content, styleContext, aParentFrame,
michael@0 4781 aCandidateRootFrame, aCandidateRootFrame);
michael@0 4782
michael@0 4783 if (!mRootElementFrame && aCandidateRootFrame) {
michael@0 4784 // The frame we're constructing will be the root element frame.
michael@0 4785 // Set mRootElementFrame before processing children.
michael@0 4786 mRootElementFrame = newFrame;
michael@0 4787 }
michael@0 4788
michael@0 4789 nsFrameItems childItems;
michael@0 4790
michael@0 4791 // Process children
michael@0 4792 NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
michael@0 4793 "nsIAnonymousContentCreator::CreateAnonymousContent should not "
michael@0 4794 "be implemented for frames for which we explicitly create an "
michael@0 4795 "anonymous child to wrap its child frames");
michael@0 4796 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
michael@0 4797 ConstructFramesFromItemList(aState, aItem.mChildItems,
michael@0 4798 innerFrame, childItems);
michael@0 4799 } else {
michael@0 4800 ProcessChildren(aState, content, styleContext, innerFrame,
michael@0 4801 true, childItems, false, aItem.mPendingBinding);
michael@0 4802 }
michael@0 4803
michael@0 4804 // Set the inner wrapper frame's initial primary list
michael@0 4805 innerFrame->SetInitialChildList(kPrincipalList, childItems);
michael@0 4806
michael@0 4807 return newFrame;
michael@0 4808 }
michael@0 4809
michael@0 4810 nsIFrame*
michael@0 4811 nsCSSFrameConstructor::ConstructOuterSVG(nsFrameConstructorState& aState,
michael@0 4812 FrameConstructionItem& aItem,
michael@0 4813 nsIFrame* aParentFrame,
michael@0 4814 const nsStyleDisplay* aDisplay,
michael@0 4815 nsFrameItems& aFrameItems)
michael@0 4816 {
michael@0 4817 return ConstructFrameWithAnonymousChild(
michael@0 4818 aState, aItem, aParentFrame, aDisplay, aFrameItems,
michael@0 4819 NS_NewSVGOuterSVGFrame, NS_NewSVGOuterSVGAnonChildFrame,
michael@0 4820 nsCSSAnonBoxes::mozSVGOuterSVGAnonChild, true);
michael@0 4821 }
michael@0 4822
michael@0 4823 nsIFrame*
michael@0 4824 nsCSSFrameConstructor::ConstructMarker(nsFrameConstructorState& aState,
michael@0 4825 FrameConstructionItem& aItem,
michael@0 4826 nsIFrame* aParentFrame,
michael@0 4827 const nsStyleDisplay* aDisplay,
michael@0 4828 nsFrameItems& aFrameItems)
michael@0 4829 {
michael@0 4830 return ConstructFrameWithAnonymousChild(
michael@0 4831 aState, aItem, aParentFrame, aDisplay, aFrameItems,
michael@0 4832 NS_NewSVGMarkerFrame, NS_NewSVGMarkerAnonChildFrame,
michael@0 4833 nsCSSAnonBoxes::mozSVGMarkerAnonChild, false);
michael@0 4834 }
michael@0 4835
michael@0 4836 // Only outer <svg> elements can be floated or positioned. All other SVG
michael@0 4837 // should be in-flow.
michael@0 4838 #define SIMPLE_SVG_FCDATA(_func) \
michael@0 4839 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | \
michael@0 4840 FCDATA_SKIP_ABSPOS_PUSH | \
michael@0 4841 FCDATA_DISALLOW_GENERATED_CONTENT, _func)
michael@0 4842 #define SIMPLE_SVG_CREATE(_tag, _func) \
michael@0 4843 { &nsGkAtoms::_tag, SIMPLE_SVG_FCDATA(_func) }
michael@0 4844
michael@0 4845 static bool
michael@0 4846 IsFilterPrimitiveChildTag(const nsIAtom* aTag)
michael@0 4847 {
michael@0 4848 return aTag == nsGkAtoms::feDistantLight ||
michael@0 4849 aTag == nsGkAtoms::fePointLight ||
michael@0 4850 aTag == nsGkAtoms::feSpotLight ||
michael@0 4851 aTag == nsGkAtoms::feFuncR ||
michael@0 4852 aTag == nsGkAtoms::feFuncG ||
michael@0 4853 aTag == nsGkAtoms::feFuncB ||
michael@0 4854 aTag == nsGkAtoms::feFuncA ||
michael@0 4855 aTag == nsGkAtoms::feMergeNode;
michael@0 4856 }
michael@0 4857
michael@0 4858 /* static */
michael@0 4859 const nsCSSFrameConstructor::FrameConstructionData*
michael@0 4860 nsCSSFrameConstructor::FindSVGData(Element* aElement,
michael@0 4861 nsIAtom* aTag,
michael@0 4862 int32_t aNameSpaceID,
michael@0 4863 nsIFrame* aParentFrame,
michael@0 4864 bool aIsWithinSVGText,
michael@0 4865 bool aAllowsTextPathChild,
michael@0 4866 nsStyleContext* aStyleContext)
michael@0 4867 {
michael@0 4868 if (aNameSpaceID != kNameSpaceID_SVG) {
michael@0 4869 return nullptr;
michael@0 4870 }
michael@0 4871
michael@0 4872 static const FrameConstructionData sSuppressData = SUPPRESS_FCDATA();
michael@0 4873 static const FrameConstructionData sContainerData =
michael@0 4874 SIMPLE_SVG_FCDATA(NS_NewSVGContainerFrame);
michael@0 4875
michael@0 4876 bool parentIsSVG = aIsWithinSVGText;
michael@0 4877 nsIContent* parentContent =
michael@0 4878 aParentFrame ? aParentFrame->GetContent() : nullptr;
michael@0 4879 // XXXbz should this really be based on the XBL-resolved tag of the parent
michael@0 4880 // frame's content? Should it not be based on the type of the parent frame
michael@0 4881 // (e.g. whether it's an SVG frame)?
michael@0 4882 if (parentContent) {
michael@0 4883 int32_t parentNSID;
michael@0 4884 nsIAtom* parentTag =
michael@0 4885 parentContent->OwnerDoc()->BindingManager()->
michael@0 4886 ResolveTag(parentContent, &parentNSID);
michael@0 4887
michael@0 4888 // It's not clear whether the SVG spec intends to allow any SVG
michael@0 4889 // content within svg:foreignObject at all (SVG 1.1, section
michael@0 4890 // 23.2), but if it does, it better be svg:svg. So given that
michael@0 4891 // we're allowing it, treat it as a non-SVG parent.
michael@0 4892 parentIsSVG = parentNSID == kNameSpaceID_SVG &&
michael@0 4893 parentTag != nsGkAtoms::foreignObject;
michael@0 4894 }
michael@0 4895
michael@0 4896 if ((aTag != nsGkAtoms::svg && !parentIsSVG) ||
michael@0 4897 (aTag == nsGkAtoms::desc || aTag == nsGkAtoms::title)) {
michael@0 4898 // Sections 5.1 and G.4 of SVG 1.1 say that SVG elements other than
michael@0 4899 // svg:svg not contained within svg:svg are incorrect, although they
michael@0 4900 // don't seem to specify error handling. Ignore them, since many of
michael@0 4901 // our frame classes can't deal. It *may* be that the document
michael@0 4902 // should at that point be considered in error according to F.2, but
michael@0 4903 // it's hard to tell.
michael@0 4904 //
michael@0 4905 // Style mutation can't change this situation, so don't bother
michael@0 4906 // adding to the undisplayed content map.
michael@0 4907 //
michael@0 4908 // We don't currently handle any UI for desc/title
michael@0 4909 return &sSuppressData;
michael@0 4910 }
michael@0 4911
michael@0 4912 // We don't need frames for animation elements
michael@0 4913 if (aElement->IsNodeOfType(nsINode::eANIMATION)) {
michael@0 4914 return &sSuppressData;
michael@0 4915 }
michael@0 4916
michael@0 4917 if (aTag == nsGkAtoms::svg && !parentIsSVG) {
michael@0 4918 // We need outer <svg> elements to have an nsSVGOuterSVGFrame regardless
michael@0 4919 // of whether they fail conditional processing attributes, since various
michael@0 4920 // SVG frames assume that one exists. We handle the non-rendering
michael@0 4921 // of failing outer <svg> element contents like <switch> statements,
michael@0 4922 // and do the PassesConditionalProcessingTests call in
michael@0 4923 // nsSVGOuterSVGFrame::Init.
michael@0 4924 static const FrameConstructionData sOuterSVGData =
michael@0 4925 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructOuterSVG);
michael@0 4926 return &sOuterSVGData;
michael@0 4927 }
michael@0 4928
michael@0 4929 if (aTag == nsGkAtoms::marker) {
michael@0 4930 static const FrameConstructionData sMarkerSVGData =
michael@0 4931 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructMarker);
michael@0 4932 return &sMarkerSVGData;
michael@0 4933 }
michael@0 4934
michael@0 4935 nsCOMPtr<SVGTests> tests(do_QueryInterface(aElement));
michael@0 4936 if (tests && !tests->PassesConditionalProcessingTests()) {
michael@0 4937 // Elements with failing conditional processing attributes never get
michael@0 4938 // rendered. Note that this is not where we select which frame in a
michael@0 4939 // <switch> to render! That happens in nsSVGSwitchFrame::PaintSVG.
michael@0 4940 return &sContainerData;
michael@0 4941 }
michael@0 4942
michael@0 4943 // Prevent bad frame types being children of filters or parents of filter
michael@0 4944 // primitives. If aParentFrame is null, we know that the frame that will
michael@0 4945 // be created will be an nsInlineFrame, so it can never be a filter.
michael@0 4946 bool parentIsFilter = aParentFrame &&
michael@0 4947 aParentFrame->GetType() == nsGkAtoms::svgFilterFrame;
michael@0 4948 bool filterPrimitive = aElement->IsNodeOfType(nsINode::eFILTER);
michael@0 4949 if ((parentIsFilter && !filterPrimitive) ||
michael@0 4950 (!parentIsFilter && filterPrimitive)) {
michael@0 4951 return &sSuppressData;
michael@0 4952 }
michael@0 4953
michael@0 4954 // Prevent bad frame types being children of filter primitives or parents of
michael@0 4955 // filter primitive children. If aParentFrame is null, we know that the frame
michael@0 4956 // that will be created will be an nsInlineFrame, so it can never be a filter
michael@0 4957 // primitive.
michael@0 4958 bool parentIsFEContainerFrame = aParentFrame &&
michael@0 4959 aParentFrame->GetType() == nsGkAtoms::svgFEContainerFrame;
michael@0 4960 if ((parentIsFEContainerFrame && !IsFilterPrimitiveChildTag(aTag)) ||
michael@0 4961 (!parentIsFEContainerFrame && IsFilterPrimitiveChildTag(aTag))) {
michael@0 4962 return &sSuppressData;
michael@0 4963 }
michael@0 4964
michael@0 4965 // Special cases for text/tspan/textPath, because the kind of frame
michael@0 4966 // they get depends on the parent frame. We ignore 'a' elements when
michael@0 4967 // determining the parent, however.
michael@0 4968 if (aIsWithinSVGText) {
michael@0 4969 // If aIsWithinSVGText is true, then we know that the "SVG text uses
michael@0 4970 // CSS frames" pref was true when this SVG fragment was first constructed.
michael@0 4971
michael@0 4972 // We don't use ConstructInline because we want different behavior
michael@0 4973 // for generated content.
michael@0 4974 static const FrameConstructionData sTSpanData =
michael@0 4975 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW |
michael@0 4976 FCDATA_SKIP_ABSPOS_PUSH |
michael@0 4977 FCDATA_DISALLOW_GENERATED_CONTENT |
michael@0 4978 FCDATA_IS_LINE_PARTICIPANT |
michael@0 4979 FCDATA_IS_INLINE |
michael@0 4980 FCDATA_USE_CHILD_ITEMS,
michael@0 4981 NS_NewInlineFrame);
michael@0 4982 if (aTag == nsGkAtoms::textPath) {
michael@0 4983 if (aAllowsTextPathChild) {
michael@0 4984 return &sTSpanData;
michael@0 4985 }
michael@0 4986 } else if (aTag == nsGkAtoms::tspan ||
michael@0 4987 aTag == nsGkAtoms::altGlyph ||
michael@0 4988 aTag == nsGkAtoms::a) {
michael@0 4989 return &sTSpanData;
michael@0 4990 }
michael@0 4991 return &sSuppressData;
michael@0 4992 } else if (aTag == nsGkAtoms::text) {
michael@0 4993 static const FrameConstructionData sTextData =
michael@0 4994 FCDATA_WITH_WRAPPING_BLOCK(FCDATA_DISALLOW_OUT_OF_FLOW |
michael@0 4995 FCDATA_ALLOW_BLOCK_STYLES,
michael@0 4996 NS_NewSVGTextFrame,
michael@0 4997 nsCSSAnonBoxes::mozSVGText);
michael@0 4998 return &sTextData;
michael@0 4999 } else if (aTag == nsGkAtoms::tspan ||
michael@0 5000 aTag == nsGkAtoms::altGlyph ||
michael@0 5001 aTag == nsGkAtoms::textPath) {
michael@0 5002 return &sSuppressData;
michael@0 5003 }
michael@0 5004
michael@0 5005 static const FrameConstructionDataByTag sSVGData[] = {
michael@0 5006 SIMPLE_SVG_CREATE(svg, NS_NewSVGInnerSVGFrame),
michael@0 5007 SIMPLE_SVG_CREATE(g, NS_NewSVGGFrame),
michael@0 5008 SIMPLE_SVG_CREATE(svgSwitch, NS_NewSVGSwitchFrame),
michael@0 5009 SIMPLE_SVG_CREATE(polygon, NS_NewSVGPathGeometryFrame),
michael@0 5010 SIMPLE_SVG_CREATE(polyline, NS_NewSVGPathGeometryFrame),
michael@0 5011 SIMPLE_SVG_CREATE(circle, NS_NewSVGPathGeometryFrame),
michael@0 5012 SIMPLE_SVG_CREATE(ellipse, NS_NewSVGPathGeometryFrame),
michael@0 5013 SIMPLE_SVG_CREATE(line, NS_NewSVGPathGeometryFrame),
michael@0 5014 SIMPLE_SVG_CREATE(rect, NS_NewSVGPathGeometryFrame),
michael@0 5015 SIMPLE_SVG_CREATE(path, NS_NewSVGPathGeometryFrame),
michael@0 5016 SIMPLE_SVG_CREATE(defs, NS_NewSVGContainerFrame),
michael@0 5017 SIMPLE_SVG_CREATE(generic_, NS_NewSVGGenericContainerFrame),
michael@0 5018 { &nsGkAtoms::foreignObject,
michael@0 5019 FCDATA_WITH_WRAPPING_BLOCK(FCDATA_DISALLOW_OUT_OF_FLOW,
michael@0 5020 NS_NewSVGForeignObjectFrame,
michael@0 5021 nsCSSAnonBoxes::mozSVGForeignContent) },
michael@0 5022 SIMPLE_SVG_CREATE(a, NS_NewSVGAFrame),
michael@0 5023 SIMPLE_SVG_CREATE(linearGradient, NS_NewSVGLinearGradientFrame),
michael@0 5024 SIMPLE_SVG_CREATE(radialGradient, NS_NewSVGRadialGradientFrame),
michael@0 5025 SIMPLE_SVG_CREATE(stop, NS_NewSVGStopFrame),
michael@0 5026 SIMPLE_SVG_CREATE(use, NS_NewSVGUseFrame),
michael@0 5027 SIMPLE_SVG_CREATE(view, NS_NewSVGViewFrame),
michael@0 5028 SIMPLE_SVG_CREATE(image, NS_NewSVGImageFrame),
michael@0 5029 SIMPLE_SVG_CREATE(clipPath, NS_NewSVGClipPathFrame),
michael@0 5030 SIMPLE_SVG_CREATE(filter, NS_NewSVGFilterFrame),
michael@0 5031 SIMPLE_SVG_CREATE(pattern, NS_NewSVGPatternFrame),
michael@0 5032 SIMPLE_SVG_CREATE(mask, NS_NewSVGMaskFrame),
michael@0 5033 SIMPLE_SVG_CREATE(feDistantLight, NS_NewSVGFEUnstyledLeafFrame),
michael@0 5034 SIMPLE_SVG_CREATE(fePointLight, NS_NewSVGFEUnstyledLeafFrame),
michael@0 5035 SIMPLE_SVG_CREATE(feSpotLight, NS_NewSVGFEUnstyledLeafFrame),
michael@0 5036 SIMPLE_SVG_CREATE(feBlend, NS_NewSVGFELeafFrame),
michael@0 5037 SIMPLE_SVG_CREATE(feColorMatrix, NS_NewSVGFELeafFrame),
michael@0 5038 SIMPLE_SVG_CREATE(feFuncR, NS_NewSVGFEUnstyledLeafFrame),
michael@0 5039 SIMPLE_SVG_CREATE(feFuncG, NS_NewSVGFEUnstyledLeafFrame),
michael@0 5040 SIMPLE_SVG_CREATE(feFuncB, NS_NewSVGFEUnstyledLeafFrame),
michael@0 5041 SIMPLE_SVG_CREATE(feFuncA, NS_NewSVGFEUnstyledLeafFrame),
michael@0 5042 SIMPLE_SVG_CREATE(feComposite, NS_NewSVGFELeafFrame),
michael@0 5043 SIMPLE_SVG_CREATE(feComponentTransfer, NS_NewSVGFEContainerFrame),
michael@0 5044 SIMPLE_SVG_CREATE(feConvolveMatrix, NS_NewSVGFELeafFrame),
michael@0 5045 SIMPLE_SVG_CREATE(feDiffuseLighting, NS_NewSVGFEContainerFrame),
michael@0 5046 SIMPLE_SVG_CREATE(feDisplacementMap, NS_NewSVGFELeafFrame),
michael@0 5047 SIMPLE_SVG_CREATE(feDropShadow, NS_NewSVGFELeafFrame),
michael@0 5048 SIMPLE_SVG_CREATE(feFlood, NS_NewSVGFELeafFrame),
michael@0 5049 SIMPLE_SVG_CREATE(feGaussianBlur, NS_NewSVGFELeafFrame),
michael@0 5050 SIMPLE_SVG_CREATE(feImage, NS_NewSVGFEImageFrame),
michael@0 5051 SIMPLE_SVG_CREATE(feMerge, NS_NewSVGFEContainerFrame),
michael@0 5052 SIMPLE_SVG_CREATE(feMergeNode, NS_NewSVGFEUnstyledLeafFrame),
michael@0 5053 SIMPLE_SVG_CREATE(feMorphology, NS_NewSVGFELeafFrame),
michael@0 5054 SIMPLE_SVG_CREATE(feOffset, NS_NewSVGFELeafFrame),
michael@0 5055 SIMPLE_SVG_CREATE(feSpecularLighting, NS_NewSVGFEContainerFrame),
michael@0 5056 SIMPLE_SVG_CREATE(feTile, NS_NewSVGFELeafFrame),
michael@0 5057 SIMPLE_SVG_CREATE(feTurbulence, NS_NewSVGFELeafFrame)
michael@0 5058 };
michael@0 5059
michael@0 5060 const FrameConstructionData* data =
michael@0 5061 FindDataByTag(aTag, aElement, aStyleContext, sSVGData,
michael@0 5062 ArrayLength(sSVGData));
michael@0 5063
michael@0 5064 if (!data) {
michael@0 5065 data = &sContainerData;
michael@0 5066 }
michael@0 5067
michael@0 5068 return data;
michael@0 5069 }
michael@0 5070
michael@0 5071 void
michael@0 5072 nsCSSFrameConstructor::AddPageBreakItem(nsIContent* aContent,
michael@0 5073 nsStyleContext* aMainStyleContext,
michael@0 5074 FrameConstructionItemList& aItems)
michael@0 5075 {
michael@0 5076 // Use the same parent style context that |aMainStyleContext| has, since
michael@0 5077 // that's easier to re-resolve and it doesn't matter in practice.
michael@0 5078 // (Getting different parents can result in framechange hints, e.g.,
michael@0 5079 // for user-modify.)
michael@0 5080 nsRefPtr<nsStyleContext> pseudoStyle =
michael@0 5081 mPresShell->StyleSet()->
michael@0 5082 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::pageBreak,
michael@0 5083 aMainStyleContext->GetParent());
michael@0 5084
michael@0 5085 NS_ASSERTION(pseudoStyle->StyleDisplay()->mDisplay ==
michael@0 5086 NS_STYLE_DISPLAY_BLOCK, "Unexpected display");
michael@0 5087
michael@0 5088 static const FrameConstructionData sPageBreakData =
michael@0 5089 FCDATA_DECL(FCDATA_SKIP_FRAMESET, NS_NewPageBreakFrame);
michael@0 5090
michael@0 5091 // Lie about the tag and namespace so we don't trigger anything
michael@0 5092 // interesting during frame construction.
michael@0 5093 aItems.AppendItem(&sPageBreakData, aContent, nsCSSAnonBoxes::pageBreak,
michael@0 5094 kNameSpaceID_None, nullptr, pseudoStyle.forget(),
michael@0 5095 true, nullptr);
michael@0 5096 }
michael@0 5097
michael@0 5098 void
michael@0 5099 nsCSSFrameConstructor::AddFrameConstructionItems(nsFrameConstructorState& aState,
michael@0 5100 nsIContent* aContent,
michael@0 5101 bool aSuppressWhiteSpaceOptimizations,
michael@0 5102 nsIFrame* aParentFrame,
michael@0 5103 FrameConstructionItemList& aItems)
michael@0 5104 {
michael@0 5105 aContent->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
michael@0 5106 if (aContent->IsElement()) {
michael@0 5107 // We can't just remove our pending restyle flags, since we may
michael@0 5108 // have restyle-later-siblings set on us. But we _can_ remove the
michael@0 5109 // "is possible restyle root" flags, and need to. Otherwise we can
michael@0 5110 // end up with stale such flags (e.g. if we used to have a
michael@0 5111 // display:none parent when our last restyle was posted and
michael@0 5112 // processed and now no longer do).
michael@0 5113 aContent->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS &
michael@0 5114 ~ELEMENT_PENDING_RESTYLE_FLAGS);
michael@0 5115 }
michael@0 5116
michael@0 5117 // XXX the GetContent() != aContent check is needed due to bug 135040.
michael@0 5118 // Remove it once that's fixed.
michael@0 5119 if (aContent->GetPrimaryFrame() &&
michael@0 5120 aContent->GetPrimaryFrame()->GetContent() == aContent &&
michael@0 5121 !aState.mCreatingExtraFrames) {
michael@0 5122 NS_ERROR("asked to create frame construction item for a node that already "
michael@0 5123 "has a frame");
michael@0 5124 return;
michael@0 5125 }
michael@0 5126
michael@0 5127 // don't create a whitespace frame if aParent doesn't want it
michael@0 5128 if (!NeedFrameFor(aState, aParentFrame, aContent)) {
michael@0 5129 return;
michael@0 5130 }
michael@0 5131
michael@0 5132 // never create frames for comments or PIs
michael@0 5133 if (aContent->IsNodeOfType(nsINode::eCOMMENT) ||
michael@0 5134 aContent->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION))
michael@0 5135 return;
michael@0 5136
michael@0 5137 nsRefPtr<nsStyleContext> styleContext;
michael@0 5138 styleContext = ResolveStyleContext(aParentFrame, aContent, &aState);
michael@0 5139
michael@0 5140 uint32_t flags = ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK;
michael@0 5141 if (aParentFrame->IsSVGText()) {
michael@0 5142 flags |= ITEM_IS_WITHIN_SVG_TEXT;
michael@0 5143 }
michael@0 5144 if (aParentFrame->GetType() == nsGkAtoms::blockFrame &&
michael@0 5145 aParentFrame->GetParent() &&
michael@0 5146 aParentFrame->GetParent()->GetType() == nsGkAtoms::svgTextFrame) {
michael@0 5147 flags |= ITEM_ALLOWS_TEXT_PATH_CHILD;
michael@0 5148 }
michael@0 5149 AddFrameConstructionItemsInternal(aState, aContent, aParentFrame,
michael@0 5150 aContent->Tag(), aContent->GetNameSpaceID(),
michael@0 5151 aSuppressWhiteSpaceOptimizations,
michael@0 5152 styleContext,
michael@0 5153 flags, nullptr,
michael@0 5154 aItems);
michael@0 5155 }
michael@0 5156
michael@0 5157 /* static */ void
michael@0 5158 nsCSSFrameConstructor::SetAsUndisplayedContent(FrameConstructionItemList& aList,
michael@0 5159 nsIContent* aContent,
michael@0 5160 nsStyleContext* aStyleContext,
michael@0 5161 bool aIsGeneratedContent)
michael@0 5162 {
michael@0 5163 if (aStyleContext->GetPseudo()) {
michael@0 5164 if (aIsGeneratedContent) {
michael@0 5165 aContent->UnbindFromTree();
michael@0 5166 }
michael@0 5167 return;
michael@0 5168 }
michael@0 5169
michael@0 5170 NS_ASSERTION(!aIsGeneratedContent, "Should have had pseudo type");
michael@0 5171 aList.AppendUndisplayedItem(aContent, aStyleContext);
michael@0 5172 }
michael@0 5173
michael@0 5174 void
michael@0 5175 nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState& aState,
michael@0 5176 nsIContent* aContent,
michael@0 5177 nsIFrame* aParentFrame,
michael@0 5178 nsIAtom* aTag,
michael@0 5179 int32_t aNameSpaceID,
michael@0 5180 bool aSuppressWhiteSpaceOptimizations,
michael@0 5181 nsStyleContext* aStyleContext,
michael@0 5182 uint32_t aFlags,
michael@0 5183 nsTArray<nsIAnonymousContentCreator::ContentInfo>* aAnonChildren,
michael@0 5184 FrameConstructionItemList& aItems)
michael@0 5185 {
michael@0 5186 NS_PRECONDITION(aContent->IsNodeOfType(nsINode::eTEXT) ||
michael@0 5187 aContent->IsElement(),
michael@0 5188 "Shouldn't get anything else here!");
michael@0 5189
michael@0 5190 // The following code allows the user to specify the base tag
michael@0 5191 // of an element using XBL. XUL and HTML objects (like boxes, menus, etc.)
michael@0 5192 // can then be extended arbitrarily.
michael@0 5193 const nsStyleDisplay* display = aStyleContext->StyleDisplay();
michael@0 5194 nsRefPtr<nsStyleContext> styleContext(aStyleContext);
michael@0 5195 PendingBinding* pendingBinding = nullptr;
michael@0 5196 if ((aFlags & ITEM_ALLOW_XBL_BASE) && display->mBinding)
michael@0 5197 {
michael@0 5198 // Ensure that our XBL bindings are installed.
michael@0 5199
michael@0 5200 nsXBLService* xblService = nsXBLService::GetInstance();
michael@0 5201 if (!xblService)
michael@0 5202 return;
michael@0 5203
michael@0 5204 bool resolveStyle;
michael@0 5205
michael@0 5206 nsAutoPtr<PendingBinding> newPendingBinding(new PendingBinding());
michael@0 5207
michael@0 5208 nsresult rv = xblService->LoadBindings(aContent, display->mBinding->GetURI(),
michael@0 5209 display->mBinding->mOriginPrincipal,
michael@0 5210 getter_AddRefs(newPendingBinding->mBinding),
michael@0 5211 &resolveStyle);
michael@0 5212 if (NS_FAILED(rv) && rv != NS_ERROR_XBL_BLOCKED)
michael@0 5213 return;
michael@0 5214
michael@0 5215 if (newPendingBinding->mBinding) {
michael@0 5216 pendingBinding = newPendingBinding;
michael@0 5217 // aState takes over owning newPendingBinding
michael@0 5218 aState.AddPendingBinding(newPendingBinding.forget());
michael@0 5219 }
michael@0 5220
michael@0 5221 if (resolveStyle) {
michael@0 5222 styleContext =
michael@0 5223 ResolveStyleContext(styleContext->GetParent(), aContent, &aState);
michael@0 5224 display = styleContext->StyleDisplay();
michael@0 5225 aStyleContext = styleContext;
michael@0 5226 }
michael@0 5227
michael@0 5228 aTag = mDocument->BindingManager()->ResolveTag(aContent, &aNameSpaceID);
michael@0 5229 }
michael@0 5230
michael@0 5231 bool isGeneratedContent = ((aFlags & ITEM_IS_GENERATED_CONTENT) != 0);
michael@0 5232
michael@0 5233 // Pre-check for display "none" - if we find that, don't create
michael@0 5234 // any frame at all
michael@0 5235 if (NS_STYLE_DISPLAY_NONE == display->mDisplay) {
michael@0 5236 SetAsUndisplayedContent(aItems, aContent, styleContext, isGeneratedContent);
michael@0 5237 return;
michael@0 5238 }
michael@0 5239
michael@0 5240 bool isText = !aContent->IsElement();
michael@0 5241
michael@0 5242 // never create frames for non-option/optgroup kids of <select> and
michael@0 5243 // non-option kids of <optgroup> inside a <select>.
michael@0 5244 // XXXbz it's not clear how this should best work with XBL.
michael@0 5245 nsIContent *parent = aContent->GetParent();
michael@0 5246 if (parent) {
michael@0 5247 // Check tag first, since that check will usually fail
michael@0 5248 nsIAtom* parentTag = parent->Tag();
michael@0 5249 if ((parentTag == nsGkAtoms::select || parentTag == nsGkAtoms::optgroup) &&
michael@0 5250 parent->IsHTML() &&
michael@0 5251 // <option> is ok no matter what
michael@0 5252 !aContent->IsHTML(nsGkAtoms::option) &&
michael@0 5253 // <optgroup> is OK in <select> but not in <optgroup>
michael@0 5254 (!aContent->IsHTML(nsGkAtoms::optgroup) ||
michael@0 5255 parentTag != nsGkAtoms::select) &&
michael@0 5256 // Allow native anonymous content no matter what
michael@0 5257 !aContent->IsRootOfNativeAnonymousSubtree()) {
michael@0 5258 // No frame for aContent
michael@0 5259 if (!isText) {
michael@0 5260 SetAsUndisplayedContent(aItems, aContent, styleContext,
michael@0 5261 isGeneratedContent);
michael@0 5262 }
michael@0 5263 return;
michael@0 5264 }
michael@0 5265 }
michael@0 5266
michael@0 5267 bool isPopup = false;
michael@0 5268 // Try to find frame construction data for this content
michael@0 5269 const FrameConstructionData* data;
michael@0 5270 if (isText) {
michael@0 5271 data = FindTextData(aParentFrame);
michael@0 5272 if (!data) {
michael@0 5273 // Nothing to do here; suppressed text inside SVG
michael@0 5274 return;
michael@0 5275 }
michael@0 5276 } else {
michael@0 5277 Element* element = aContent->AsElement();
michael@0 5278
michael@0 5279 // Don't create frames for non-SVG element children of SVG elements.
michael@0 5280 if (aNameSpaceID != kNameSpaceID_SVG &&
michael@0 5281 ((aParentFrame &&
michael@0 5282 IsFrameForSVG(aParentFrame) &&
michael@0 5283 !aParentFrame->IsFrameOfType(nsIFrame::eSVGForeignObject)) ||
michael@0 5284 (aFlags & ITEM_IS_WITHIN_SVG_TEXT))) {
michael@0 5285 SetAsUndisplayedContent(aItems, element, styleContext,
michael@0 5286 isGeneratedContent);
michael@0 5287 return;
michael@0 5288 }
michael@0 5289
michael@0 5290 data = FindHTMLData(element, aTag, aNameSpaceID, aParentFrame,
michael@0 5291 styleContext);
michael@0 5292 if (!data) {
michael@0 5293 data = FindXULTagData(element, aTag, aNameSpaceID, styleContext);
michael@0 5294 }
michael@0 5295 if (!data) {
michael@0 5296 data = FindMathMLData(element, aTag, aNameSpaceID, styleContext);
michael@0 5297 }
michael@0 5298 if (!data) {
michael@0 5299 data = FindSVGData(element, aTag, aNameSpaceID, aParentFrame,
michael@0 5300 aFlags & ITEM_IS_WITHIN_SVG_TEXT,
michael@0 5301 aFlags & ITEM_ALLOWS_TEXT_PATH_CHILD,
michael@0 5302 styleContext);
michael@0 5303 }
michael@0 5304
michael@0 5305 // Now check for XUL display types
michael@0 5306 if (!data) {
michael@0 5307 data = FindXULDisplayData(display, element, styleContext);
michael@0 5308 }
michael@0 5309
michael@0 5310 // And general display types
michael@0 5311 if (!data) {
michael@0 5312 data = FindDisplayData(display, element, aParentFrame, styleContext);
michael@0 5313 }
michael@0 5314
michael@0 5315 NS_ASSERTION(data, "Should have frame construction data now");
michael@0 5316
michael@0 5317 if (data->mBits & FCDATA_SUPPRESS_FRAME) {
michael@0 5318 SetAsUndisplayedContent(aItems, element, styleContext, isGeneratedContent);
michael@0 5319 return;
michael@0 5320 }
michael@0 5321
michael@0 5322 #ifdef MOZ_XUL
michael@0 5323 if ((data->mBits & FCDATA_IS_POPUP) &&
michael@0 5324 (!aParentFrame || // Parent is inline
michael@0 5325 aParentFrame->GetType() != nsGkAtoms::menuFrame)) {
michael@0 5326 if (!aState.mPopupItems.containingBlock &&
michael@0 5327 !aState.mHavePendingPopupgroup) {
michael@0 5328 SetAsUndisplayedContent(aItems, element, styleContext,
michael@0 5329 isGeneratedContent);
michael@0 5330 return;
michael@0 5331 }
michael@0 5332
michael@0 5333 isPopup = true;
michael@0 5334 }
michael@0 5335 #endif /* MOZ_XUL */
michael@0 5336 }
michael@0 5337
michael@0 5338 uint32_t bits = data->mBits;
michael@0 5339
michael@0 5340 // Inside colgroups, suppress everything except columns.
michael@0 5341 if (aParentFrame &&
michael@0 5342 aParentFrame->GetType() == nsGkAtoms::tableColGroupFrame &&
michael@0 5343 (!(bits & FCDATA_IS_TABLE_PART) ||
michael@0 5344 display->mDisplay != NS_STYLE_DISPLAY_TABLE_COLUMN)) {
michael@0 5345 SetAsUndisplayedContent(aItems, aContent, styleContext, isGeneratedContent);
michael@0 5346 return;
michael@0 5347 }
michael@0 5348
michael@0 5349 bool canHavePageBreak =
michael@0 5350 (aFlags & ITEM_ALLOW_PAGE_BREAK) &&
michael@0 5351 aState.mPresContext->IsPaginated() &&
michael@0 5352 !display->IsAbsolutelyPositionedStyle() &&
michael@0 5353 !(bits & FCDATA_IS_TABLE_PART) &&
michael@0 5354 !(bits & FCDATA_IS_SVG_TEXT);
michael@0 5355
michael@0 5356 if (canHavePageBreak && display->mBreakBefore) {
michael@0 5357 AddPageBreakItem(aContent, aStyleContext, aItems);
michael@0 5358 }
michael@0 5359
michael@0 5360 FrameConstructionItem* item =
michael@0 5361 aItems.AppendItem(data, aContent, aTag, aNameSpaceID,
michael@0 5362 pendingBinding, styleContext.forget(),
michael@0 5363 aSuppressWhiteSpaceOptimizations, aAnonChildren);
michael@0 5364 if (!item) {
michael@0 5365 if (isGeneratedContent) {
michael@0 5366 aContent->UnbindFromTree();
michael@0 5367 }
michael@0 5368 return;
michael@0 5369 }
michael@0 5370
michael@0 5371 item->mIsText = isText;
michael@0 5372 item->mIsGeneratedContent = isGeneratedContent;
michael@0 5373 item->mIsAnonymousContentCreatorContent =
michael@0 5374 aFlags & ITEM_IS_ANONYMOUSCONTENTCREATOR_CONTENT;
michael@0 5375 if (isGeneratedContent) {
michael@0 5376 NS_ADDREF(item->mContent);
michael@0 5377 }
michael@0 5378 item->mIsRootPopupgroup =
michael@0 5379 aNameSpaceID == kNameSpaceID_XUL && aTag == nsGkAtoms::popupgroup &&
michael@0 5380 aContent->IsRootOfNativeAnonymousSubtree();
michael@0 5381 if (item->mIsRootPopupgroup) {
michael@0 5382 aState.mHavePendingPopupgroup = true;
michael@0 5383 }
michael@0 5384 item->mIsPopup = isPopup;
michael@0 5385 item->mIsForSVGAElement = aNameSpaceID == kNameSpaceID_SVG &&
michael@0 5386 aTag == nsGkAtoms::a;
michael@0 5387
michael@0 5388 if (canHavePageBreak && display->mBreakAfter) {
michael@0 5389 AddPageBreakItem(aContent, aStyleContext, aItems);
michael@0 5390 }
michael@0 5391
michael@0 5392 if (bits & FCDATA_IS_INLINE) {
michael@0 5393 // To correctly set item->mIsAllInline we need to build up our child items
michael@0 5394 // right now.
michael@0 5395 BuildInlineChildItems(aState, *item,
michael@0 5396 aFlags & ITEM_IS_WITHIN_SVG_TEXT,
michael@0 5397 aFlags & ITEM_ALLOWS_TEXT_PATH_CHILD);
michael@0 5398 item->mHasInlineEnds = true;
michael@0 5399 item->mIsBlock = false;
michael@0 5400 } else {
michael@0 5401 // Compute a boolean isInline which is guaranteed to be false for blocks
michael@0 5402 // (but may also be false for some inlines).
michael@0 5403 bool isInline =
michael@0 5404 // Table-internal things are inline-outside if and only if they're kids of
michael@0 5405 // inlines, since they'll trigger construction of inline-table
michael@0 5406 // pseudos.
michael@0 5407 ((bits & FCDATA_IS_TABLE_PART) &&
michael@0 5408 (!aParentFrame || // No aParentFrame means inline
michael@0 5409 aParentFrame->StyleDisplay()->mDisplay == NS_STYLE_DISPLAY_INLINE)) ||
michael@0 5410 // Things that are inline-outside but aren't inline frames are inline
michael@0 5411 display->IsInlineOutsideStyle() ||
michael@0 5412 // Popups that are certainly out of flow.
michael@0 5413 isPopup;
michael@0 5414
michael@0 5415 // Set mIsAllInline conservatively. It just might be that even an inline
michael@0 5416 // that has mIsAllInline false doesn't need an {ib} split. So this is just
michael@0 5417 // an optimization to keep from doing too much work in cases when we can
michael@0 5418 // show that mIsAllInline is true..
michael@0 5419 item->mIsAllInline = item->mHasInlineEnds = isInline ||
michael@0 5420 // Figure out whether we're guaranteed this item will be out of flow.
michael@0 5421 // This is not a precise test, since one of our ancestor inlines might add
michael@0 5422 // an absolute containing block (if it's relatively positioned) when there
michael@0 5423 // wasn't such a containing block before. But it's conservative in the
michael@0 5424 // sense that anything that will really end up as an in-flow non-inline
michael@0 5425 // will test false here. In other words, if this test is true we're
michael@0 5426 // guaranteed to be inline; if it's false we don't know what we'll end up
michael@0 5427 // as.
michael@0 5428 //
michael@0 5429 // If we make this test precise, we can remove some of the code dealing
michael@0 5430 // with the imprecision in ConstructInline and adjust the comments on
michael@0 5431 // mIsAllInline and mIsBlock in the header. And probably remove mIsBlock
michael@0 5432 // altogether, since then it will always be equal to !mHasInlineEnds.
michael@0 5433 (!(bits & FCDATA_DISALLOW_OUT_OF_FLOW) &&
michael@0 5434 aState.GetGeometricParent(display, nullptr));
michael@0 5435
michael@0 5436 // Set mIsBlock conservatively. It's OK to set it false for some real
michael@0 5437 // blocks, but not OK to set it true for things that aren't blocks. Since
michael@0 5438 // isOutOfFlow might be false even in cases when the frame will end up
michael@0 5439 // out-of-flow, we can't use it here. But we _can_ say that the frame will
michael@0 5440 // for sure end up in-flow if it's not floated or absolutely positioned.
michael@0 5441 item->mIsBlock = !isInline &&
michael@0 5442 !display->IsAbsolutelyPositionedStyle() &&
michael@0 5443 !display->IsFloatingStyle() &&
michael@0 5444 !(bits & FCDATA_IS_SVG_TEXT);
michael@0 5445 }
michael@0 5446
michael@0 5447 if (item->mIsAllInline) {
michael@0 5448 aItems.InlineItemAdded();
michael@0 5449 } else if (item->mIsBlock) {
michael@0 5450 aItems.BlockItemAdded();
michael@0 5451 }
michael@0 5452
michael@0 5453 // Our item should be treated as a line participant if we have the relevant
michael@0 5454 // bit and are going to be in-flow. Note that this really only matters if
michael@0 5455 // our ancestor is a box or some such, so the fact that we might have an
michael@0 5456 // inline ancestor that might become a containing block is not relevant here.
michael@0 5457 if ((bits & FCDATA_IS_LINE_PARTICIPANT) &&
michael@0 5458 ((bits & FCDATA_DISALLOW_OUT_OF_FLOW) ||
michael@0 5459 !aState.GetGeometricParent(display, nullptr))) {
michael@0 5460 item->mIsLineParticipant = true;
michael@0 5461 aItems.LineParticipantItemAdded();
michael@0 5462 }
michael@0 5463 }
michael@0 5464
michael@0 5465 static void
michael@0 5466 DestroyContent(void* aPropertyValue)
michael@0 5467 {
michael@0 5468 nsIContent* content = static_cast<nsIContent*>(aPropertyValue);
michael@0 5469 content->UnbindFromTree();
michael@0 5470 NS_RELEASE(content);
michael@0 5471 }
michael@0 5472
michael@0 5473 NS_DECLARE_FRAME_PROPERTY(BeforeProperty, DestroyContent)
michael@0 5474 NS_DECLARE_FRAME_PROPERTY(AfterProperty, DestroyContent)
michael@0 5475
michael@0 5476 static const FramePropertyDescriptor*
michael@0 5477 GenConPseudoToProperty(nsIAtom* aPseudo)
michael@0 5478 {
michael@0 5479 NS_ASSERTION(aPseudo == nsCSSPseudoElements::before ||
michael@0 5480 aPseudo == nsCSSPseudoElements::after,
michael@0 5481 "Bad gen-con pseudo");
michael@0 5482 return aPseudo == nsCSSPseudoElements::before ? BeforeProperty()
michael@0 5483 : AfterProperty();
michael@0 5484 }
michael@0 5485
michael@0 5486 /**
michael@0 5487 * Return true if the frame construction item pointed to by aIter will
michael@0 5488 * create a frame adjacent to a line boundary in the frame tree, and that
michael@0 5489 * line boundary is induced by a content node adjacent to the frame's
michael@0 5490 * content node in the content tree. The latter condition is necessary so
michael@0 5491 * that ContentAppended/ContentInserted/ContentRemoved can easily find any
michael@0 5492 * text nodes that were suppressed here.
michael@0 5493 */
michael@0 5494 bool
michael@0 5495 nsCSSFrameConstructor::AtLineBoundary(FCItemIterator& aIter)
michael@0 5496 {
michael@0 5497 if (aIter.item().mSuppressWhiteSpaceOptimizations) {
michael@0 5498 return false;
michael@0 5499 }
michael@0 5500
michael@0 5501 if (aIter.AtStart()) {
michael@0 5502 if (aIter.List()->HasLineBoundaryAtStart() &&
michael@0 5503 !aIter.item().mContent->GetPreviousSibling())
michael@0 5504 return true;
michael@0 5505 } else {
michael@0 5506 FCItemIterator prev = aIter;
michael@0 5507 prev.Prev();
michael@0 5508 if (prev.item().IsLineBoundary() &&
michael@0 5509 !prev.item().mSuppressWhiteSpaceOptimizations &&
michael@0 5510 aIter.item().mContent->GetPreviousSibling() == prev.item().mContent)
michael@0 5511 return true;
michael@0 5512 }
michael@0 5513
michael@0 5514 FCItemIterator next = aIter;
michael@0 5515 next.Next();
michael@0 5516 if (next.IsDone()) {
michael@0 5517 if (aIter.List()->HasLineBoundaryAtEnd() &&
michael@0 5518 !aIter.item().mContent->GetNextSibling())
michael@0 5519 return true;
michael@0 5520 } else {
michael@0 5521 if (next.item().IsLineBoundary() &&
michael@0 5522 !next.item().mSuppressWhiteSpaceOptimizations &&
michael@0 5523 aIter.item().mContent->GetNextSibling() == next.item().mContent)
michael@0 5524 return true;
michael@0 5525 }
michael@0 5526
michael@0 5527 return false;
michael@0 5528 }
michael@0 5529
michael@0 5530 void
michael@0 5531 nsCSSFrameConstructor::ConstructFramesFromItem(nsFrameConstructorState& aState,
michael@0 5532 FCItemIterator& aIter,
michael@0 5533 nsIFrame* aParentFrame,
michael@0 5534 nsFrameItems& aFrameItems)
michael@0 5535 {
michael@0 5536 nsIFrame* adjParentFrame = aParentFrame;
michael@0 5537 FrameConstructionItem& item = aIter.item();
michael@0 5538 nsStyleContext* styleContext = item.mStyleContext;
michael@0 5539 AdjustParentFrame(adjParentFrame, item.mFCData, styleContext);
michael@0 5540
michael@0 5541 if (item.mIsText) {
michael@0 5542 // If this is collapsible whitespace next to a line boundary,
michael@0 5543 // don't create a frame. item.IsWhitespace() also sets the
michael@0 5544 // NS_CREATE_FRAME_IF_NON_WHITESPACE flag in the text node. (If we
michael@0 5545 // end up creating a frame, nsTextFrame::Init will clear the flag.)
michael@0 5546 // We don't do this for generated content, because some generated
michael@0 5547 // text content is empty text nodes that are about to be initialized.
michael@0 5548 // (We check mAdditionalStateBits because only the generated content
michael@0 5549 // container's frame construction item is marked with
michael@0 5550 // mIsGeneratedContent, and we might not have an aParentFrame.)
michael@0 5551 // We don't do it for content that may have XBL anonymous siblings,
michael@0 5552 // because they make it difficult to correctly create the frame
michael@0 5553 // due to dynamic changes.
michael@0 5554 // We don't do it for SVG text, since we might need to position and
michael@0 5555 // measure the white space glyphs due to x/y/dx/dy attributes.
michael@0 5556 if (AtLineBoundary(aIter) &&
michael@0 5557 !styleContext->StyleText()->WhiteSpaceOrNewlineIsSignificant() &&
michael@0 5558 aIter.List()->ParentHasNoXBLChildren() &&
michael@0 5559 !(aState.mAdditionalStateBits & NS_FRAME_GENERATED_CONTENT) &&
michael@0 5560 (item.mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) &&
michael@0 5561 !(item.mFCData->mBits & FCDATA_IS_SVG_TEXT) &&
michael@0 5562 !mAlwaysCreateFramesForIgnorableWhitespace &&
michael@0 5563 item.IsWhitespace(aState))
michael@0 5564 return;
michael@0 5565
michael@0 5566 ConstructTextFrame(item.mFCData, aState, item.mContent,
michael@0 5567 adjParentFrame, styleContext,
michael@0 5568 aFrameItems);
michael@0 5569 return;
michael@0 5570 }
michael@0 5571
michael@0 5572 // Start background loads during frame construction so that we're
michael@0 5573 // guaranteed that they will be started before onload fires.
michael@0 5574 styleContext->StartBackgroundImageLoads();
michael@0 5575
michael@0 5576 nsFrameState savedStateBits = aState.mAdditionalStateBits;
michael@0 5577 if (item.mIsGeneratedContent) {
michael@0 5578 // Ensure that frames created here are all tagged with
michael@0 5579 // NS_FRAME_GENERATED_CONTENT.
michael@0 5580 aState.mAdditionalStateBits |= NS_FRAME_GENERATED_CONTENT;
michael@0 5581
michael@0 5582 // Note that we're not necessarily setting this property on the primary
michael@0 5583 // frame for the content for which this is generated content. We might be
michael@0 5584 // setting it on a table pseudo-frame inserted under that instead. That's
michael@0 5585 // OK, though; we just need to do the property set so that the content will
michael@0 5586 // get cleaned up when the frame is destroyed.
michael@0 5587 aParentFrame->Properties().Set(GenConPseudoToProperty(styleContext->GetPseudo()),
michael@0 5588 item.mContent);
michael@0 5589
michael@0 5590 // Now that we've passed ownership of item.mContent to the frame, unset
michael@0 5591 // our generated content flag so we don't release or unbind it ourselves.
michael@0 5592 item.mIsGeneratedContent = false;
michael@0 5593 }
michael@0 5594
michael@0 5595 // XXXbz maybe just inline ConstructFrameFromItemInternal here or something?
michael@0 5596 ConstructFrameFromItemInternal(item, aState, adjParentFrame, aFrameItems);
michael@0 5597
michael@0 5598 aState.mAdditionalStateBits = savedStateBits;
michael@0 5599 }
michael@0 5600
michael@0 5601
michael@0 5602 inline bool
michael@0 5603 IsRootBoxFrame(nsIFrame *aFrame)
michael@0 5604 {
michael@0 5605 return (aFrame->GetType() == nsGkAtoms::rootFrame);
michael@0 5606 }
michael@0 5607
michael@0 5608 nsresult
michael@0 5609 nsCSSFrameConstructor::ReconstructDocElementHierarchy()
michael@0 5610 {
michael@0 5611 Element* rootElement = mDocument->GetRootElement();
michael@0 5612 if (!rootElement) {
michael@0 5613 /* nothing to do */
michael@0 5614 return NS_OK;
michael@0 5615 }
michael@0 5616 return RecreateFramesForContent(rootElement, false);
michael@0 5617 }
michael@0 5618
michael@0 5619 nsIFrame*
michael@0 5620 nsCSSFrameConstructor::GetFrameFor(nsIContent* aContent)
michael@0 5621 {
michael@0 5622 // Get the primary frame associated with the content
michael@0 5623 nsIFrame* frame = aContent->GetPrimaryFrame();
michael@0 5624
michael@0 5625 if (!frame)
michael@0 5626 return nullptr;
michael@0 5627
michael@0 5628 // If the content of the frame is not the desired content then this is not
michael@0 5629 // really a frame for the desired content.
michael@0 5630 // XXX This check is needed due to bug 135040. Remove it once that's fixed.
michael@0 5631 if (frame->GetContent() != aContent) {
michael@0 5632 return nullptr;
michael@0 5633 }
michael@0 5634
michael@0 5635 nsIFrame* insertionFrame = frame->GetContentInsertionFrame();
michael@0 5636
michael@0 5637 NS_ASSERTION(insertionFrame == frame || !frame->IsLeaf(),
michael@0 5638 "The insertion frame is the primary frame or the primary frame isn't a leaf");
michael@0 5639
michael@0 5640 return insertionFrame;
michael@0 5641 }
michael@0 5642
michael@0 5643 nsIFrame*
michael@0 5644 nsCSSFrameConstructor::GetAbsoluteContainingBlock(nsIFrame* aFrame,
michael@0 5645 ContainingBlockType aType)
michael@0 5646 {
michael@0 5647 NS_PRECONDITION(nullptr != mRootElementFrame, "no root element frame");
michael@0 5648
michael@0 5649 // Starting with aFrame, look for a frame that is absolutely positioned or
michael@0 5650 // relatively positioned (and transformed, if aType is FIXED)
michael@0 5651 for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
michael@0 5652 if (frame->IsFrameOfType(nsIFrame::eMathML)) {
michael@0 5653 // If it's mathml, bail out -- no absolute positioning out from inside
michael@0 5654 // mathml frames. Note that we don't make this part of the loop
michael@0 5655 // condition because of the stuff at the end of this method...
michael@0 5656 return nullptr;
michael@0 5657 }
michael@0 5658
michael@0 5659 // If the frame is positioned, we will probably return it as the containing
michael@0 5660 // block (see the exceptions below). Otherwise, we'll start looking at the
michael@0 5661 // parent frame, unless we're dealing with a scrollframe.
michael@0 5662 // Scrollframes are special since they're not positioned, but their
michael@0 5663 // scrolledframe might be. So, we need to check this special case to return
michael@0 5664 // the correct containing block (the scrolledframe) in that case.
michael@0 5665 // If we're looking for a fixed-pos containing block and the frame is
michael@0 5666 // not transformed, skip it.
michael@0 5667 if (!frame->IsPositioned() ||
michael@0 5668 (aType == FIXED_POS &&
michael@0 5669 !frame->StyleDisplay()->HasTransform(frame) &&
michael@0 5670 !frame->StyleDisplay()->HasPerspectiveStyle())) {
michael@0 5671 continue;
michael@0 5672 }
michael@0 5673 nsIFrame* absPosCBCandidate = frame;
michael@0 5674 nsIAtom* type = absPosCBCandidate->GetType();
michael@0 5675 if (type == nsGkAtoms::fieldSetFrame) {
michael@0 5676 absPosCBCandidate = static_cast<nsFieldSetFrame*>(absPosCBCandidate)->GetInner();
michael@0 5677 if (!absPosCBCandidate) {
michael@0 5678 continue;
michael@0 5679 }
michael@0 5680 type = absPosCBCandidate->GetType();
michael@0 5681 }
michael@0 5682 if (type == nsGkAtoms::scrollFrame) {
michael@0 5683 nsIScrollableFrame* scrollFrame = do_QueryFrame(absPosCBCandidate);
michael@0 5684 absPosCBCandidate = scrollFrame->GetScrolledFrame();
michael@0 5685 if (!absPosCBCandidate) {
michael@0 5686 continue;
michael@0 5687 }
michael@0 5688 type = absPosCBCandidate->GetType();
michael@0 5689 }
michael@0 5690 // Only first continuations can be containing blocks.
michael@0 5691 absPosCBCandidate = absPosCBCandidate->FirstContinuation();
michael@0 5692 // Is the frame really an absolute container?
michael@0 5693 if (!absPosCBCandidate->IsAbsoluteContainer()) {
michael@0 5694 continue;
michael@0 5695 }
michael@0 5696
michael@0 5697 // For tables, skip the inner frame and consider the outer table frame.
michael@0 5698 if (type == nsGkAtoms::tableFrame) {
michael@0 5699 continue;
michael@0 5700 }
michael@0 5701 // For outer table frames, we can just return absPosCBCandidate.
michael@0 5702 return absPosCBCandidate;
michael@0 5703 }
michael@0 5704
michael@0 5705 // It is possible for the search for the containing block to fail, because
michael@0 5706 // no absolute container can be found in the parent chain. In those cases,
michael@0 5707 // we fall back to the document element's containing block.
michael@0 5708 if (aType == FIXED_POS) {
michael@0 5709 return mFixedContainingBlock;
michael@0 5710 }
michael@0 5711 return mHasRootAbsPosContainingBlock ? mDocElementContainingBlock : nullptr;
michael@0 5712 }
michael@0 5713
michael@0 5714 nsIFrame*
michael@0 5715 nsCSSFrameConstructor::GetFloatContainingBlock(nsIFrame* aFrame)
michael@0 5716 {
michael@0 5717 // Starting with aFrame, look for a frame that is a float containing block.
michael@0 5718 // IF we hit a mathml frame, bail out; we don't allow floating out of mathml
michael@0 5719 // frames, because they don't seem to be able to deal.
michael@0 5720 // The logic here needs to match the logic in ProcessChildren()
michael@0 5721 for (nsIFrame* containingBlock = aFrame;
michael@0 5722 containingBlock &&
michael@0 5723 !ShouldSuppressFloatingOfDescendants(containingBlock);
michael@0 5724 containingBlock = containingBlock->GetParent()) {
michael@0 5725 if (containingBlock->IsFloatContainingBlock()) {
michael@0 5726 return containingBlock;
michael@0 5727 }
michael@0 5728 }
michael@0 5729
michael@0 5730 // If we didn't find a containing block, then there just isn't
michael@0 5731 // one.... return null
michael@0 5732 return nullptr;
michael@0 5733 }
michael@0 5734
michael@0 5735 /**
michael@0 5736 * This function will check whether aContainer has :after generated content.
michael@0 5737 * If so, appending to it should actually insert. The return value is the
michael@0 5738 * parent to use for newly-appended content. *aAfterFrame points to the :after
michael@0 5739 * frame before which appended content should go, if there is one.
michael@0 5740 */
michael@0 5741 static nsIFrame*
michael@0 5742 AdjustAppendParentForAfterContent(nsPresContext* aPresContext,
michael@0 5743 nsIContent* aContainer,
michael@0 5744 nsIFrame* aParentFrame,
michael@0 5745 nsIFrame** aAfterFrame)
michael@0 5746 {
michael@0 5747 // See if the parent has an :after pseudo-element. Check for the presence
michael@0 5748 // of style first, since nsLayoutUtils::GetAfterFrame is sorta expensive.
michael@0 5749 nsStyleContext* parentStyle = aParentFrame->StyleContext();
michael@0 5750 if (nsLayoutUtils::HasPseudoStyle(aContainer, parentStyle,
michael@0 5751 nsCSSPseudoElements::ePseudo_after,
michael@0 5752 aPresContext)) {
michael@0 5753 // Ensure that the :after frame is on the principal child list.
michael@0 5754 aParentFrame->DrainSelfOverflowList();
michael@0 5755
michael@0 5756 nsIFrame* afterFrame = nsLayoutUtils::GetAfterFrame(aParentFrame);
michael@0 5757 if (afterFrame) {
michael@0 5758 *aAfterFrame = afterFrame;
michael@0 5759 return afterFrame->GetParent();
michael@0 5760 }
michael@0 5761 }
michael@0 5762
michael@0 5763 *aAfterFrame = nullptr;
michael@0 5764
michael@0 5765 if (IsFramePartOfIBSplit(aParentFrame)) {
michael@0 5766 // We might be in a situation where the last part of the {ib} split was
michael@0 5767 // empty. Since we have no ::after pseudo-element, we do in fact want to be
michael@0 5768 // appending to that last part, so advance to it if needed. Note that here
michael@0 5769 // aParentFrame is the result of a GetLastIBSplitSibling call, so must be
michael@0 5770 // either the last or next to last ib-split sibling.
michael@0 5771 nsIFrame* trailingInline = GetIBSplitSibling(aParentFrame);
michael@0 5772 if (trailingInline) {
michael@0 5773 aParentFrame = trailingInline;
michael@0 5774 }
michael@0 5775
michael@0 5776 // Always make sure to look at the last continuation of the frame
michael@0 5777 // for the {ib} case, even if that continuation is empty. We
michael@0 5778 // don't do this for the non-ib-split-frame case, since in the
michael@0 5779 // other cases appending to the last nonempty continuation is fine
michael@0 5780 // and in fact not doing that can confuse code that doesn't know
michael@0 5781 // to pull kids from continuations other than its next one.
michael@0 5782 aParentFrame = aParentFrame->LastContinuation();
michael@0 5783 }
michael@0 5784
michael@0 5785 return aParentFrame;
michael@0 5786 }
michael@0 5787
michael@0 5788 /**
michael@0 5789 * This function will get the previous sibling to use for an append operation.
michael@0 5790 * it takes a parent frame (must not be null) and its :after frame (may be
michael@0 5791 * null).
michael@0 5792 */
michael@0 5793 static nsIFrame*
michael@0 5794 FindAppendPrevSibling(nsIFrame* aParentFrame, nsIFrame* aAfterFrame)
michael@0 5795 {
michael@0 5796 if (aAfterFrame) {
michael@0 5797 NS_ASSERTION(aAfterFrame->GetParent() == aParentFrame, "Wrong parent");
michael@0 5798 NS_ASSERTION(aAfterFrame->GetPrevSibling() ||
michael@0 5799 aParentFrame->GetFirstPrincipalChild() == aAfterFrame,
michael@0 5800 ":after frame must be on the principal child list here");
michael@0 5801 return aAfterFrame->GetPrevSibling();
michael@0 5802 }
michael@0 5803
michael@0 5804 aParentFrame->DrainSelfOverflowList();
michael@0 5805
michael@0 5806 return aParentFrame->GetLastChild(kPrincipalList);
michael@0 5807 }
michael@0 5808
michael@0 5809 /**
michael@0 5810 * This function will get the next sibling for a frame insert operation given
michael@0 5811 * the parent and previous sibling. aPrevSibling may be null.
michael@0 5812 */
michael@0 5813 static nsIFrame*
michael@0 5814 GetInsertNextSibling(nsIFrame* aParentFrame, nsIFrame* aPrevSibling)
michael@0 5815 {
michael@0 5816 if (aPrevSibling) {
michael@0 5817 return aPrevSibling->GetNextSibling();
michael@0 5818 }
michael@0 5819
michael@0 5820 return aParentFrame->GetFirstPrincipalChild();
michael@0 5821 }
michael@0 5822
michael@0 5823 /**
michael@0 5824 * This function is called by ContentAppended() and ContentInserted() when
michael@0 5825 * appending flowed frames to a parent's principal child list. It handles the
michael@0 5826 * case where the parent is the trailing inline of an {ib} split.
michael@0 5827 */
michael@0 5828 nsresult
michael@0 5829 nsCSSFrameConstructor::AppendFramesToParent(nsFrameConstructorState& aState,
michael@0 5830 nsIFrame* aParentFrame,
michael@0 5831 nsFrameItems& aFrameList,
michael@0 5832 nsIFrame* aPrevSibling,
michael@0 5833 bool aIsRecursiveCall)
michael@0 5834 {
michael@0 5835 NS_PRECONDITION(!IsFramePartOfIBSplit(aParentFrame) ||
michael@0 5836 !GetIBSplitSibling(aParentFrame) ||
michael@0 5837 !GetIBSplitSibling(aParentFrame)->GetFirstPrincipalChild(),
michael@0 5838 "aParentFrame has a ib-split sibling with kids?");
michael@0 5839 NS_PRECONDITION(!aPrevSibling || aPrevSibling->GetParent() == aParentFrame,
michael@0 5840 "Parent and prevsibling don't match");
michael@0 5841
michael@0 5842 nsIFrame* nextSibling = ::GetInsertNextSibling(aParentFrame, aPrevSibling);
michael@0 5843
michael@0 5844 NS_ASSERTION(nextSibling ||
michael@0 5845 !aParentFrame->GetNextContinuation() ||
michael@0 5846 !aParentFrame->GetNextContinuation()->GetFirstPrincipalChild() ||
michael@0 5847 aIsRecursiveCall,
michael@0 5848 "aParentFrame has later continuations with kids?");
michael@0 5849 NS_ASSERTION(nextSibling ||
michael@0 5850 !IsFramePartOfIBSplit(aParentFrame) ||
michael@0 5851 (IsInlineFrame(aParentFrame) &&
michael@0 5852 !GetIBSplitSibling(aParentFrame) &&
michael@0 5853 !aParentFrame->GetNextContinuation()) ||
michael@0 5854 aIsRecursiveCall,
michael@0 5855 "aParentFrame is not last?");
michael@0 5856
michael@0 5857 // If we're inserting a list of frames at the end of the trailing inline
michael@0 5858 // of an {ib} split, we may need to create additional {ib} siblings to parent
michael@0 5859 // them.
michael@0 5860 if (!nextSibling && IsFramePartOfIBSplit(aParentFrame)) {
michael@0 5861 // When we get here, our frame list might start with a block. If it does
michael@0 5862 // so, and aParentFrame is an inline, and it and all its previous
michael@0 5863 // continuations have no siblings, then put the initial blocks from the
michael@0 5864 // frame list into the previous block of the {ib} split. Note that we
michael@0 5865 // didn't want to stop at the block part of the split when figuring out
michael@0 5866 // initial parent, because that could screw up float parenting; it's easier
michael@0 5867 // to do this little fixup here instead.
michael@0 5868 if (aFrameList.NotEmpty() && !aFrameList.FirstChild()->IsInlineOutside()) {
michael@0 5869 // See whether our trailing inline is empty
michael@0 5870 nsIFrame* firstContinuation = aParentFrame->FirstContinuation();
michael@0 5871 if (firstContinuation->PrincipalChildList().IsEmpty()) {
michael@0 5872 // Our trailing inline is empty. Collect our starting blocks from
michael@0 5873 // aFrameList, get the right parent frame for them, and put them in.
michael@0 5874 nsFrameList::FrameLinkEnumerator firstNonBlockEnumerator =
michael@0 5875 FindFirstNonBlock(aFrameList);
michael@0 5876 nsFrameList blockKids = aFrameList.ExtractHead(firstNonBlockEnumerator);
michael@0 5877 NS_ASSERTION(blockKids.NotEmpty(), "No blocks?");
michael@0 5878
michael@0 5879 nsIFrame* prevBlock =
michael@0 5880 GetIBSplitPrevSibling(firstContinuation)->LastContinuation();
michael@0 5881 NS_ASSERTION(prevBlock, "Should have previous block here");
michael@0 5882
michael@0 5883 MoveChildrenTo(aState.mPresContext, aParentFrame, prevBlock, blockKids);
michael@0 5884 }
michael@0 5885 }
michael@0 5886
michael@0 5887 // We want to put some of the frames into this inline frame.
michael@0 5888 nsFrameList::FrameLinkEnumerator firstBlockEnumerator(aFrameList);
michael@0 5889 FindFirstBlock(firstBlockEnumerator);
michael@0 5890
michael@0 5891 nsFrameList inlineKids = aFrameList.ExtractHead(firstBlockEnumerator);
michael@0 5892 if (!inlineKids.IsEmpty()) {
michael@0 5893 AppendFrames(aParentFrame, kPrincipalList, inlineKids);
michael@0 5894 }
michael@0 5895
michael@0 5896 if (!aFrameList.IsEmpty()) {
michael@0 5897 bool positioned = aParentFrame->IsRelativelyPositioned();
michael@0 5898 nsFrameItems ibSiblings;
michael@0 5899 CreateIBSiblings(aState, aParentFrame, positioned, aFrameList,
michael@0 5900 ibSiblings);
michael@0 5901
michael@0 5902 // Make sure to trigger reflow of the inline that used to be our
michael@0 5903 // last one and now isn't anymore, since its GetSkipSides() has
michael@0 5904 // changed.
michael@0 5905 mPresShell->FrameNeedsReflow(aParentFrame,
michael@0 5906 nsIPresShell::eTreeChange,
michael@0 5907 NS_FRAME_HAS_DIRTY_CHILDREN);
michael@0 5908
michael@0 5909 // Recurse so we create new ib siblings as needed for aParentFrame's parent
michael@0 5910 return AppendFramesToParent(aState, aParentFrame->GetParent(), ibSiblings,
michael@0 5911 aParentFrame, true);
michael@0 5912 }
michael@0 5913
michael@0 5914 return NS_OK;
michael@0 5915 }
michael@0 5916
michael@0 5917 // Insert the frames after our aPrevSibling
michael@0 5918 return InsertFrames(aParentFrame, kPrincipalList, aPrevSibling, aFrameList);
michael@0 5919 }
michael@0 5920
michael@0 5921 #define UNSET_DISPLAY 255
michael@0 5922
michael@0 5923 // This gets called to see if the frames corresponding to aSibling and aContent
michael@0 5924 // should be siblings in the frame tree. Although (1) rows and cols, (2) row
michael@0 5925 // groups and col groups, (3) row groups and captions, (4) legends and content
michael@0 5926 // inside fieldsets, (5) popups and other kids of the menu are siblings from a
michael@0 5927 // content perspective, they are not considered siblings in the frame tree.
michael@0 5928 bool
michael@0 5929 nsCSSFrameConstructor::IsValidSibling(nsIFrame* aSibling,
michael@0 5930 nsIContent* aContent,
michael@0 5931 uint8_t& aDisplay)
michael@0 5932 {
michael@0 5933 nsIFrame* parentFrame = aSibling->GetParent();
michael@0 5934 nsIAtom* parentType = nullptr;
michael@0 5935 if (parentFrame) {
michael@0 5936 parentType = parentFrame->GetType();
michael@0 5937 }
michael@0 5938
michael@0 5939 uint8_t siblingDisplay = aSibling->GetDisplay();
michael@0 5940 if ((NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == siblingDisplay) ||
michael@0 5941 (NS_STYLE_DISPLAY_TABLE_COLUMN == siblingDisplay) ||
michael@0 5942 (NS_STYLE_DISPLAY_TABLE_CAPTION == siblingDisplay) ||
michael@0 5943 (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == siblingDisplay) ||
michael@0 5944 (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == siblingDisplay) ||
michael@0 5945 (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == siblingDisplay) ||
michael@0 5946 nsGkAtoms::menuFrame == parentType) {
michael@0 5947 // if we haven't already, construct a style context to find the display type of aContent
michael@0 5948 if (UNSET_DISPLAY == aDisplay) {
michael@0 5949 nsRefPtr<nsStyleContext> styleContext;
michael@0 5950 nsIFrame* styleParent = aSibling->GetParentStyleContextFrame();
michael@0 5951 if (!styleParent) {
michael@0 5952 NS_NOTREACHED("Shouldn't happen");
michael@0 5953 return false;
michael@0 5954 }
michael@0 5955 // XXXbz when this code is killed, the state argument to
michael@0 5956 // ResolveStyleContext can be made non-optional.
michael@0 5957 styleContext = ResolveStyleContext(styleParent, aContent, nullptr);
michael@0 5958 const nsStyleDisplay* display = styleContext->StyleDisplay();
michael@0 5959 aDisplay = display->mDisplay;
michael@0 5960 }
michael@0 5961 if (nsGkAtoms::menuFrame == parentType) {
michael@0 5962 return
michael@0 5963 (NS_STYLE_DISPLAY_POPUP == aDisplay) ==
michael@0 5964 (NS_STYLE_DISPLAY_POPUP == siblingDisplay);
michael@0 5965 }
michael@0 5966 // To have decent performance we want to return false in cases in which
michael@0 5967 // reordering the two siblings has no effect on display. To ensure
michael@0 5968 // correctness, we MUST return false in cases where the two siblings have
michael@0 5969 // the same desired parent type and live on different display lists.
michael@0 5970 // Specificaly, columns and column groups should only consider columns and
michael@0 5971 // column groups as valid siblings. Captions should only consider other
michael@0 5972 // captions. All other things should consider each other as valid
michael@0 5973 // siblings. The restriction in the |if| above on siblingDisplay is ok,
michael@0 5974 // because for correctness the only part that really needs to happen is to
michael@0 5975 // not consider captions, column groups, and row/header/footer groups
michael@0 5976 // siblings of each other. Treating a column or colgroup as a valid
michael@0 5977 // sibling of a non-table-related frame will just mean we end up reframing.
michael@0 5978 if ((siblingDisplay == NS_STYLE_DISPLAY_TABLE_CAPTION) !=
michael@0 5979 (aDisplay == NS_STYLE_DISPLAY_TABLE_CAPTION)) {
michael@0 5980 // One's a caption and the other is not. Not valid siblings.
michael@0 5981 return false;
michael@0 5982 }
michael@0 5983
michael@0 5984 if ((siblingDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP ||
michael@0 5985 siblingDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN) !=
michael@0 5986 (aDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP ||
michael@0 5987 aDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN)) {
michael@0 5988 // One's a column or column group and the other is not. Not valid
michael@0 5989 // siblings.
michael@0 5990 return false;
michael@0 5991 }
michael@0 5992 // Fall through; it's possible that the display type was overridden and
michael@0 5993 // a different sort of frame was constructed, so we may need to return false
michael@0 5994 // below.
michael@0 5995 }
michael@0 5996
michael@0 5997 if (IsFrameForFieldSet(parentFrame, parentType)) {
michael@0 5998 // Legends can be sibling of legends but not of other content in the fieldset
michael@0 5999 nsIAtom* sibType = aSibling->GetContentInsertionFrame()->GetType();
michael@0 6000 bool legendContent = aContent->IsHTML(nsGkAtoms::legend);
michael@0 6001
michael@0 6002 if ((legendContent && (nsGkAtoms::legendFrame != sibType)) ||
michael@0 6003 (!legendContent && (nsGkAtoms::legendFrame == sibType)))
michael@0 6004 return false;
michael@0 6005 }
michael@0 6006
michael@0 6007 return true;
michael@0 6008 }
michael@0 6009
michael@0 6010 nsIFrame*
michael@0 6011 nsCSSFrameConstructor::FindFrameForContentSibling(nsIContent* aContent,
michael@0 6012 nsIContent* aTargetContent,
michael@0 6013 uint8_t& aTargetContentDisplay,
michael@0 6014 bool aPrevSibling)
michael@0 6015 {
michael@0 6016 nsIFrame* sibling = aContent->GetPrimaryFrame();
michael@0 6017 if (!sibling || sibling->GetContent() != aContent) {
michael@0 6018 // XXX the GetContent() != aContent check is needed due to bug 135040.
michael@0 6019 // Remove it once that's fixed.
michael@0 6020 return nullptr;
michael@0 6021 }
michael@0 6022
michael@0 6023 // If the frame is out-of-flow, GetPrimaryFrame() will have returned the
michael@0 6024 // out-of-flow frame; we want the placeholder.
michael@0 6025 if (sibling->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
michael@0 6026 nsIFrame* placeholderFrame = GetPlaceholderFrameFor(sibling);
michael@0 6027 NS_ASSERTION(placeholderFrame, "no placeholder for out-of-flow frame");
michael@0 6028 sibling = placeholderFrame;
michael@0 6029 }
michael@0 6030
michael@0 6031 // The frame we have now should never be a continuation
michael@0 6032 NS_ASSERTION(!sibling->GetPrevContinuation(), "How did that happen?");
michael@0 6033
michael@0 6034 if (aPrevSibling) {
michael@0 6035 // The frame may be a ib-split frame (a split inline frame that
michael@0 6036 // contains a block). Get the last part of that split.
michael@0 6037 if (IsFramePartOfIBSplit(sibling)) {
michael@0 6038 sibling = GetLastIBSplitSibling(sibling, true);
michael@0 6039 }
michael@0 6040
michael@0 6041 // The frame may have a continuation. If so, we want the last
michael@0 6042 // non-overflow-container continuation as our previous sibling.
michael@0 6043 sibling = sibling->GetTailContinuation();
michael@0 6044 }
michael@0 6045
michael@0 6046 if (aTargetContent &&
michael@0 6047 !IsValidSibling(sibling, aTargetContent, aTargetContentDisplay)) {
michael@0 6048 sibling = nullptr;
michael@0 6049 }
michael@0 6050
michael@0 6051 return sibling;
michael@0 6052 }
michael@0 6053
michael@0 6054 nsIFrame*
michael@0 6055 nsCSSFrameConstructor::FindPreviousSibling(FlattenedChildIterator aIter,
michael@0 6056 uint8_t& aTargetContentDisplay)
michael@0 6057 {
michael@0 6058 nsIContent* child = aIter.Get();
michael@0 6059
michael@0 6060 // Note: not all content objects are associated with a frame (e.g., if it's
michael@0 6061 // `display: none') so keep looking until we find a previous frame
michael@0 6062 while (nsIContent* sibling = aIter.GetPreviousChild()) {
michael@0 6063 nsIFrame* prevSibling =
michael@0 6064 FindFrameForContentSibling(sibling, child, aTargetContentDisplay, true);
michael@0 6065
michael@0 6066 if (prevSibling) {
michael@0 6067 // Found a previous sibling, we're done!
michael@0 6068 return prevSibling;
michael@0 6069 }
michael@0 6070 }
michael@0 6071
michael@0 6072 return nullptr;
michael@0 6073 }
michael@0 6074
michael@0 6075 nsIFrame*
michael@0 6076 nsCSSFrameConstructor::FindNextSibling(FlattenedChildIterator aIter,
michael@0 6077 uint8_t& aTargetContentDisplay)
michael@0 6078 {
michael@0 6079 nsIContent* child = aIter.Get();
michael@0 6080
michael@0 6081 while (nsIContent* sibling = aIter.GetNextChild()) {
michael@0 6082 nsIFrame* nextSibling =
michael@0 6083 FindFrameForContentSibling(sibling, child, aTargetContentDisplay, false);
michael@0 6084
michael@0 6085 if (nextSibling) {
michael@0 6086 // We found a next sibling, we're done!
michael@0 6087 return nextSibling;
michael@0 6088 }
michael@0 6089 }
michael@0 6090
michael@0 6091 return nullptr;
michael@0 6092 }
michael@0 6093
michael@0 6094 // For fieldsets, returns the area frame, if the child is not a legend.
michael@0 6095 static nsIFrame*
michael@0 6096 GetAdjustedParentFrame(nsIFrame* aParentFrame,
michael@0 6097 nsIAtom* aParentFrameType,
michael@0 6098 nsIContent* aChildContent)
michael@0 6099 {
michael@0 6100 NS_PRECONDITION(nsGkAtoms::tableOuterFrame != aParentFrameType,
michael@0 6101 "Shouldn't be happening!");
michael@0 6102
michael@0 6103 nsIFrame* newParent = nullptr;
michael@0 6104
michael@0 6105 if (nsGkAtoms::fieldSetFrame == aParentFrameType) {
michael@0 6106 // If the parent is a fieldSet, use the fieldSet's area frame as the
michael@0 6107 // parent unless the new content is a legend.
michael@0 6108 if (!aChildContent->IsHTML(nsGkAtoms::legend)) {
michael@0 6109 newParent = GetFieldSetBlockFrame(aParentFrame);
michael@0 6110 }
michael@0 6111 }
michael@0 6112 return (newParent) ? newParent : aParentFrame;
michael@0 6113 }
michael@0 6114
michael@0 6115 nsIFrame*
michael@0 6116 nsCSSFrameConstructor::GetInsertionPrevSibling(nsIFrame*& aParentFrame,
michael@0 6117 nsIContent* aContainer,
michael@0 6118 nsIContent* aChild,
michael@0 6119 bool* aIsAppend,
michael@0 6120 bool* aIsRangeInsertSafe,
michael@0 6121 nsIContent* aStartSkipChild,
michael@0 6122 nsIContent* aEndSkipChild)
michael@0 6123 {
michael@0 6124 *aIsAppend = false;
michael@0 6125
michael@0 6126 // Find the frame that precedes the insertion point. Walk backwards
michael@0 6127 // from the parent frame to get the parent content, because if an
michael@0 6128 // XBL insertion point is involved, we'll need to use _that_ to find
michael@0 6129 // the preceding frame.
michael@0 6130
michael@0 6131 NS_PRECONDITION(aParentFrame, "Must have parent frame to start with");
michael@0 6132 nsIContent* container = aParentFrame->GetContent();
michael@0 6133
michael@0 6134 FlattenedChildIterator iter(container);
michael@0 6135 bool xblCase = iter.XBLInvolved() || container != aContainer;
michael@0 6136 if (xblCase || !aChild->IsRootOfAnonymousSubtree()) {
michael@0 6137 // The check for IsRootOfAnonymousSubtree() is because editor is
michael@0 6138 // severely broken and calls us directly for native anonymous
michael@0 6139 // nodes that it creates.
michael@0 6140 if (aStartSkipChild) {
michael@0 6141 iter.Seek(aStartSkipChild);
michael@0 6142 } else {
michael@0 6143 iter.Seek(aChild);
michael@0 6144 }
michael@0 6145 } else {
michael@0 6146 // Prime the iterator for the call to FindPreviousSibling.
michael@0 6147 iter.GetNextChild();
michael@0 6148 NS_WARNING("Someone passed native anonymous content directly into frame "
michael@0 6149 "construction. Stop doing that!");
michael@0 6150 }
michael@0 6151
michael@0 6152 // Note that FindPreviousSibling is passed the iterator by value, so that
michael@0 6153 // the later usage of the iterator starts from the same place.
michael@0 6154 uint8_t childDisplay = UNSET_DISPLAY;
michael@0 6155 nsIFrame* prevSibling = FindPreviousSibling(iter, childDisplay);
michael@0 6156
michael@0 6157 // Now, find the geometric parent so that we can handle
michael@0 6158 // continuations properly. Use the prev sibling if we have it;
michael@0 6159 // otherwise use the next sibling.
michael@0 6160 if (prevSibling) {
michael@0 6161 aParentFrame = prevSibling->GetParent()->GetContentInsertionFrame();
michael@0 6162 }
michael@0 6163 else {
michael@0 6164 // If there is no previous sibling, then find the frame that follows
michael@0 6165 if (aEndSkipChild) {
michael@0 6166 iter.Seek(aEndSkipChild);
michael@0 6167 iter.GetPreviousChild();
michael@0 6168 }
michael@0 6169 nsIFrame* nextSibling = FindNextSibling(iter, childDisplay);
michael@0 6170
michael@0 6171 if (nextSibling) {
michael@0 6172 aParentFrame = nextSibling->GetParent()->GetContentInsertionFrame();
michael@0 6173 }
michael@0 6174 else {
michael@0 6175 // No previous or next sibling, so treat this like an appended frame.
michael@0 6176 *aIsAppend = true;
michael@0 6177 if (IsFramePartOfIBSplit(aParentFrame)) {
michael@0 6178 // Since we're appending, we'll walk to the last anonymous frame
michael@0 6179 // that was created for the broken inline frame. But don't walk
michael@0 6180 // to the trailing inline if it's empty; stop at the block.
michael@0 6181 aParentFrame = GetLastIBSplitSibling(aParentFrame, false);
michael@0 6182 }
michael@0 6183 // Get continuation that parents the last child. This MUST be done
michael@0 6184 // before the AdjustAppendParentForAfterContent call.
michael@0 6185 aParentFrame = nsLayoutUtils::LastContinuationWithChild(aParentFrame);
michael@0 6186 // Deal with fieldsets
michael@0 6187 aParentFrame = ::GetAdjustedParentFrame(aParentFrame,
michael@0 6188 aParentFrame->GetType(),
michael@0 6189 aChild);
michael@0 6190 nsIFrame* appendAfterFrame;
michael@0 6191 aParentFrame =
michael@0 6192 ::AdjustAppendParentForAfterContent(mPresShell->GetPresContext(),
michael@0 6193 container, aParentFrame,
michael@0 6194 &appendAfterFrame);
michael@0 6195 prevSibling = ::FindAppendPrevSibling(aParentFrame, appendAfterFrame);
michael@0 6196 }
michael@0 6197 }
michael@0 6198
michael@0 6199 *aIsRangeInsertSafe = (childDisplay == UNSET_DISPLAY);
michael@0 6200 return prevSibling;
michael@0 6201 }
michael@0 6202
michael@0 6203 static bool
michael@0 6204 IsSpecialFramesetChild(nsIContent* aContent)
michael@0 6205 {
michael@0 6206 // IMPORTANT: This must match the conditions in nsHTMLFramesetFrame::Init.
michael@0 6207 return aContent->IsHTML() &&
michael@0 6208 (aContent->Tag() == nsGkAtoms::frameset ||
michael@0 6209 aContent->Tag() == nsGkAtoms::frame);
michael@0 6210 }
michael@0 6211
michael@0 6212 static void
michael@0 6213 InvalidateCanvasIfNeeded(nsIPresShell* presShell, nsIContent* node);
michael@0 6214
michael@0 6215 #ifdef MOZ_XUL
michael@0 6216
michael@0 6217 static
michael@0 6218 bool
michael@0 6219 IsXULListBox(nsIContent* aContainer)
michael@0 6220 {
michael@0 6221 return (aContainer->IsXUL() && aContainer->Tag() == nsGkAtoms::listbox);
michael@0 6222 }
michael@0 6223
michael@0 6224 static
michael@0 6225 nsListBoxBodyFrame*
michael@0 6226 MaybeGetListBoxBodyFrame(nsIContent* aContainer, nsIContent* aChild)
michael@0 6227 {
michael@0 6228 if (!aContainer)
michael@0 6229 return nullptr;
michael@0 6230
michael@0 6231 if (IsXULListBox(aContainer) &&
michael@0 6232 aChild->IsXUL() && aChild->Tag() == nsGkAtoms::listitem) {
michael@0 6233 nsCOMPtr<nsIDOMXULElement> xulElement = do_QueryInterface(aContainer);
michael@0 6234 nsCOMPtr<nsIBoxObject> boxObject;
michael@0 6235 xulElement->GetBoxObject(getter_AddRefs(boxObject));
michael@0 6236 nsCOMPtr<nsPIListBoxObject> listBoxObject = do_QueryInterface(boxObject);
michael@0 6237 if (listBoxObject) {
michael@0 6238 return listBoxObject->GetListBoxBody(false);
michael@0 6239 }
michael@0 6240 }
michael@0 6241
michael@0 6242 return nullptr;
michael@0 6243 }
michael@0 6244 #endif
michael@0 6245
michael@0 6246 void
michael@0 6247 nsCSSFrameConstructor::AddTextItemIfNeeded(nsFrameConstructorState& aState,
michael@0 6248 nsIFrame* aParentFrame,
michael@0 6249 nsIContent* aPossibleTextContent,
michael@0 6250 FrameConstructionItemList& aItems)
michael@0 6251 {
michael@0 6252 NS_PRECONDITION(aPossibleTextContent, "Must have node");
michael@0 6253 if (!aPossibleTextContent->IsNodeOfType(nsINode::eTEXT) ||
michael@0 6254 !aPossibleTextContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE)) {
michael@0 6255 // Not text, or not suppressed due to being all-whitespace (if it
michael@0 6256 // were being suppressed, it would have the
michael@0 6257 // NS_CREATE_FRAME_IF_NON_WHITESPACE flag)
michael@0 6258 return;
michael@0 6259 }
michael@0 6260 NS_ASSERTION(!aPossibleTextContent->GetPrimaryFrame(),
michael@0 6261 "Text node has a frame and NS_CREATE_FRAME_IF_NON_WHITESPACE");
michael@0 6262 AddFrameConstructionItems(aState, aPossibleTextContent, false,
michael@0 6263 aParentFrame, aItems);
michael@0 6264 }
michael@0 6265
michael@0 6266 void
michael@0 6267 nsCSSFrameConstructor::ReframeTextIfNeeded(nsIContent* aParentContent,
michael@0 6268 nsIContent* aContent)
michael@0 6269 {
michael@0 6270 if (!aContent->IsNodeOfType(nsINode::eTEXT) ||
michael@0 6271 !aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE)) {
michael@0 6272 // Not text, or not suppressed due to being all-whitespace (if it
michael@0 6273 // were being suppressed, it would have the
michael@0 6274 // NS_CREATE_FRAME_IF_NON_WHITESPACE flag)
michael@0 6275 return;
michael@0 6276 }
michael@0 6277 NS_ASSERTION(!aContent->GetPrimaryFrame(),
michael@0 6278 "Text node has a frame and NS_CREATE_FRAME_IF_NON_WHITESPACE");
michael@0 6279 ContentInserted(aParentContent, aContent, nullptr, false);
michael@0 6280 }
michael@0 6281
michael@0 6282 // For inserts aChild should be valid, for appends it should be null.
michael@0 6283 // Returns true if this operation can be lazy, false if not.
michael@0 6284 bool
michael@0 6285 nsCSSFrameConstructor::MaybeConstructLazily(Operation aOperation,
michael@0 6286 nsIContent* aContainer,
michael@0 6287 nsIContent* aChild)
michael@0 6288 {
michael@0 6289 if (mPresShell->GetPresContext()->IsChrome() || !aContainer ||
michael@0 6290 aContainer->IsInNativeAnonymousSubtree() || aContainer->IsXUL()) {
michael@0 6291 return false;
michael@0 6292 }
michael@0 6293
michael@0 6294 if (aOperation == CONTENTINSERT) {
michael@0 6295 if (aChild->IsRootOfAnonymousSubtree() ||
michael@0 6296 aChild->HasFlag(NODE_IS_IN_SHADOW_TREE) ||
michael@0 6297 aChild->IsEditable() || aChild->IsXUL()) {
michael@0 6298 return false;
michael@0 6299 }
michael@0 6300 } else { // CONTENTAPPEND
michael@0 6301 NS_ASSERTION(aOperation == CONTENTAPPEND,
michael@0 6302 "operation should be either insert or append");
michael@0 6303 for (nsIContent* child = aChild; child; child = child->GetNextSibling()) {
michael@0 6304 NS_ASSERTION(!child->IsRootOfAnonymousSubtree(),
michael@0 6305 "Should be coming through the CONTENTAPPEND case");
michael@0 6306 if (child->IsXUL() || child->IsEditable()) {
michael@0 6307 return false;
michael@0 6308 }
michael@0 6309 }
michael@0 6310 }
michael@0 6311
michael@0 6312 // We can construct lazily; just need to set suitable bits in the content
michael@0 6313 // tree.
michael@0 6314
michael@0 6315 // Walk up the tree setting the NODE_DESCENDANTS_NEED_FRAMES bit as we go.
michael@0 6316 nsIContent* content = aContainer;
michael@0 6317 #ifdef DEBUG
michael@0 6318 // If we hit a node with no primary frame, or the NODE_NEEDS_FRAME bit set
michael@0 6319 // we want to assert, but leaf frames that process their own children and may
michael@0 6320 // ignore anonymous children (eg framesets) make this complicated. So we set
michael@0 6321 // these two booleans if we encounter these situations and unset them if we
michael@0 6322 // hit a node with a leaf frame.
michael@0 6323 bool noPrimaryFrame = false;
michael@0 6324 bool needsFrameBitSet = false;
michael@0 6325 #endif
michael@0 6326 while (content &&
michael@0 6327 !content->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
michael@0 6328 #ifdef DEBUG
michael@0 6329 if (content->GetPrimaryFrame() && content->GetPrimaryFrame()->IsLeaf()) {
michael@0 6330 noPrimaryFrame = needsFrameBitSet = false;
michael@0 6331 }
michael@0 6332 if (!noPrimaryFrame && !content->GetPrimaryFrame()) {
michael@0 6333 noPrimaryFrame = true;
michael@0 6334 }
michael@0 6335 if (!needsFrameBitSet && content->HasFlag(NODE_NEEDS_FRAME)) {
michael@0 6336 needsFrameBitSet = true;
michael@0 6337 }
michael@0 6338 #endif
michael@0 6339 content->SetFlags(NODE_DESCENDANTS_NEED_FRAMES);
michael@0 6340 content = content->GetFlattenedTreeParent();
michael@0 6341 }
michael@0 6342 #ifdef DEBUG
michael@0 6343 if (content && content->GetPrimaryFrame() &&
michael@0 6344 content->GetPrimaryFrame()->IsLeaf()) {
michael@0 6345 noPrimaryFrame = needsFrameBitSet = false;
michael@0 6346 }
michael@0 6347 NS_ASSERTION(!noPrimaryFrame, "Ancestors of nodes with frames to be "
michael@0 6348 "constructed lazily should have frames");
michael@0 6349 NS_ASSERTION(!needsFrameBitSet, "Ancestors of nodes with frames to be "
michael@0 6350 "constructed lazily should not have NEEDS_FRAME bit set");
michael@0 6351 #endif
michael@0 6352
michael@0 6353 // Set NODE_NEEDS_FRAME on the new nodes.
michael@0 6354 if (aOperation == CONTENTINSERT) {
michael@0 6355 NS_ASSERTION(!aChild->GetPrimaryFrame() ||
michael@0 6356 aChild->GetPrimaryFrame()->GetContent() != aChild,
michael@0 6357 //XXX the aChild->GetPrimaryFrame()->GetContent() != aChild
michael@0 6358 // check is needed due to bug 135040. Remove it once that's
michael@0 6359 // fixed.
michael@0 6360 "setting NEEDS_FRAME on a node that already has a frame?");
michael@0 6361 aChild->SetFlags(NODE_NEEDS_FRAME);
michael@0 6362 } else { // CONTENTAPPEND
michael@0 6363 for (nsIContent* child = aChild; child; child = child->GetNextSibling()) {
michael@0 6364 NS_ASSERTION(!child->GetPrimaryFrame() ||
michael@0 6365 child->GetPrimaryFrame()->GetContent() != child,
michael@0 6366 //XXX the child->GetPrimaryFrame()->GetContent() != child
michael@0 6367 // check is needed due to bug 135040. Remove it once that's
michael@0 6368 // fixed.
michael@0 6369 "setting NEEDS_FRAME on a node that already has a frame?");
michael@0 6370 child->SetFlags(NODE_NEEDS_FRAME);
michael@0 6371 }
michael@0 6372 }
michael@0 6373
michael@0 6374 RestyleManager()->PostRestyleEventForLazyConstruction();
michael@0 6375 return true;
michael@0 6376 }
michael@0 6377
michael@0 6378 void
michael@0 6379 nsCSSFrameConstructor::CreateNeededFrames(nsIContent* aContent)
michael@0 6380 {
michael@0 6381 NS_ASSERTION(!aContent->HasFlag(NODE_NEEDS_FRAME),
michael@0 6382 "shouldn't get here with a content node that has needs frame bit set");
michael@0 6383 NS_ASSERTION(aContent->HasFlag(NODE_DESCENDANTS_NEED_FRAMES),
michael@0 6384 "should only get here with a content node that has descendants needing frames");
michael@0 6385
michael@0 6386 aContent->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES);
michael@0 6387
michael@0 6388 // We could either descend first (on nodes that don't have NODE_NEEDS_FRAME
michael@0 6389 // set) or issue content notifications for our kids first. In absence of
michael@0 6390 // anything definitive either way we'll go with the latter.
michael@0 6391
michael@0 6392 // It might be better to use GetChildArray and scan it completely first and
michael@0 6393 // then issue all notifications. (We have to scan it completely first because
michael@0 6394 // constructing frames can set attributes, which can change the storage of
michael@0 6395 // child lists).
michael@0 6396
michael@0 6397 // Scan the children of aContent to see what operations (if any) we need to
michael@0 6398 // perform.
michael@0 6399 uint32_t childCount = aContent->GetChildCount();
michael@0 6400 bool inRun = false;
michael@0 6401 nsIContent* firstChildInRun = nullptr;
michael@0 6402 for (uint32_t i = 0; i < childCount; i++) {
michael@0 6403 nsIContent* child = aContent->GetChildAt(i);
michael@0 6404 if (child->HasFlag(NODE_NEEDS_FRAME)) {
michael@0 6405 NS_ASSERTION(!child->GetPrimaryFrame() ||
michael@0 6406 child->GetPrimaryFrame()->GetContent() != child,
michael@0 6407 //XXX the child->GetPrimaryFrame()->GetContent() != child
michael@0 6408 // check is needed due to bug 135040. Remove it once that's
michael@0 6409 // fixed.
michael@0 6410 "NEEDS_FRAME set on a node that already has a frame?");
michael@0 6411 if (!inRun) {
michael@0 6412 inRun = true;
michael@0 6413 firstChildInRun = child;
michael@0 6414 }
michael@0 6415 } else {
michael@0 6416 if (inRun) {
michael@0 6417 inRun = false;
michael@0 6418 // generate a ContentRangeInserted for [startOfRun,i)
michael@0 6419 ContentRangeInserted(aContent, firstChildInRun, child, nullptr,
michael@0 6420 false);
michael@0 6421 }
michael@0 6422 }
michael@0 6423 }
michael@0 6424 if (inRun) {
michael@0 6425 ContentAppended(aContent, firstChildInRun, false);
michael@0 6426 }
michael@0 6427
michael@0 6428 // Now descend.
michael@0 6429 FlattenedChildIterator iter(aContent);
michael@0 6430 for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
michael@0 6431 if (child->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
michael@0 6432 CreateNeededFrames(child);
michael@0 6433 }
michael@0 6434 }
michael@0 6435 }
michael@0 6436
michael@0 6437 void nsCSSFrameConstructor::CreateNeededFrames()
michael@0 6438 {
michael@0 6439 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
michael@0 6440 "Someone forgot a script blocker");
michael@0 6441
michael@0 6442 Element* rootElement = mDocument->GetRootElement();
michael@0 6443 NS_ASSERTION(!rootElement || !rootElement->HasFlag(NODE_NEEDS_FRAME),
michael@0 6444 "root element should not have frame created lazily");
michael@0 6445 if (rootElement && rootElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
michael@0 6446 BeginUpdate();
michael@0 6447 CreateNeededFrames(rootElement);
michael@0 6448 EndUpdate();
michael@0 6449 }
michael@0 6450 }
michael@0 6451
michael@0 6452 void
michael@0 6453 nsCSSFrameConstructor::IssueSingleInsertNofications(nsIContent* aContainer,
michael@0 6454 nsIContent* aStartChild,
michael@0 6455 nsIContent* aEndChild,
michael@0 6456 bool aAllowLazyConstruction)
michael@0 6457 {
michael@0 6458 for (nsIContent* child = aStartChild;
michael@0 6459 child != aEndChild;
michael@0 6460 child = child->GetNextSibling()) {
michael@0 6461 if ((child->GetPrimaryFrame() ||
michael@0 6462 GetUndisplayedContent(child))
michael@0 6463 #ifdef MOZ_XUL
michael@0 6464 // Except listboxes suck, so do NOT skip anything here if
michael@0 6465 // we plan to notify a listbox.
michael@0 6466 && !MaybeGetListBoxBodyFrame(aContainer, child)
michael@0 6467 #endif
michael@0 6468 ) {
michael@0 6469 // Already have a frame or undisplayed entry for this content; a
michael@0 6470 // previous ContentInserted in this loop must have reconstructed
michael@0 6471 // its insertion parent. Skip it.
michael@0 6472 continue;
michael@0 6473 }
michael@0 6474 // Call ContentInserted with this node.
michael@0 6475 ContentInserted(aContainer, child, mTempFrameTreeState,
michael@0 6476 aAllowLazyConstruction);
michael@0 6477 }
michael@0 6478 }
michael@0 6479
michael@0 6480 nsIFrame*
michael@0 6481 nsCSSFrameConstructor::GetRangeInsertionPoint(nsIContent* aContainer,
michael@0 6482 nsIContent* aStartChild,
michael@0 6483 nsIContent* aEndChild,
michael@0 6484 bool aAllowLazyConstruction)
michael@0 6485 {
michael@0 6486 // See if we have an XBL insertion point. If so, then that's our
michael@0 6487 // real parent frame; if not, then the frame hasn't been built yet
michael@0 6488 // and we just bail.
michael@0 6489 bool multiple = false;
michael@0 6490 nsIFrame* insertionPoint = GetInsertionPoint(aContainer, nullptr, &multiple);
michael@0 6491 if (!insertionPoint && !multiple)
michael@0 6492 return nullptr; // Don't build the frames.
michael@0 6493
michael@0 6494 bool hasInsertion = false;
michael@0 6495 if (!multiple) {
michael@0 6496 // XXXbz XBL2/sXBL issue
michael@0 6497 nsIDocument* document = aStartChild->GetDocument();
michael@0 6498 // XXXbz how would |document| be null here?
michael@0 6499 if (document && aStartChild->GetXBLInsertionParent()) {
michael@0 6500 hasInsertion = true;
michael@0 6501 }
michael@0 6502 }
michael@0 6503
michael@0 6504 if (multiple || hasInsertion) {
michael@0 6505 // We have an insertion point. There are some additional tests we need to do
michael@0 6506 // in order to ensure that an append is a safe operation.
michael@0 6507 uint32_t childCount = 0;
michael@0 6508
michael@0 6509 if (!multiple) {
michael@0 6510 // We may need to make multiple ContentInserted calls instead. A
michael@0 6511 // reasonable heuristic to employ (in order to maintain good performance)
michael@0 6512 // is to find out if the insertion point's content node contains any
michael@0 6513 // explicit children. If it does not, then it is highly likely that
michael@0 6514 // an append is occurring. (Note it is not definite, and there are insane
michael@0 6515 // cases we will not deal with by employing this heuristic, but it beats
michael@0 6516 // always falling back to multiple ContentInserted calls).
michael@0 6517 //
michael@0 6518 // In the multiple insertion point case, we know we're going to need to do
michael@0 6519 // multiple ContentInserted calls anyway.
michael@0 6520 // XXXndeakin This test doesn't work in the new world. Or rather, it works, but
michael@0 6521 // it's slow
michael@0 6522 childCount = insertionPoint->GetContent()->GetChildCount();
michael@0 6523 }
michael@0 6524
michael@0 6525 // If we have multiple insertion points or if we have an insertion point
michael@0 6526 // and the operation is not a true append or if the insertion point already
michael@0 6527 // has explicit children, then we must fall back.
michael@0 6528 if (multiple || aEndChild != nullptr || childCount > 0) {
michael@0 6529 // Now comes the fun part. For each inserted child, make a
michael@0 6530 // ContentInserted call as if it had just gotten inserted and
michael@0 6531 // let ContentInserted handle the mess.
michael@0 6532 IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
michael@0 6533 aAllowLazyConstruction);
michael@0 6534 return nullptr;
michael@0 6535 }
michael@0 6536 }
michael@0 6537
michael@0 6538 return insertionPoint;
michael@0 6539 }
michael@0 6540
michael@0 6541 bool
michael@0 6542 nsCSSFrameConstructor::MaybeRecreateForFrameset(nsIFrame* aParentFrame,
michael@0 6543 nsIContent* aStartChild,
michael@0 6544 nsIContent* aEndChild)
michael@0 6545 {
michael@0 6546 if (aParentFrame->GetType() == nsGkAtoms::frameSetFrame) {
michael@0 6547 // Check whether we have any kids we care about.
michael@0 6548 for (nsIContent* cur = aStartChild;
michael@0 6549 cur != aEndChild;
michael@0 6550 cur = cur->GetNextSibling()) {
michael@0 6551 if (IsSpecialFramesetChild(cur)) {
michael@0 6552 // Just reframe the parent, since framesets are weird like that.
michael@0 6553 RecreateFramesForContent(aParentFrame->GetContent(), false);
michael@0 6554 return true;
michael@0 6555 }
michael@0 6556 }
michael@0 6557 }
michael@0 6558 return false;
michael@0 6559 }
michael@0 6560
michael@0 6561 nsresult
michael@0 6562 nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
michael@0 6563 nsIContent* aFirstNewContent,
michael@0 6564 bool aAllowLazyConstruction)
michael@0 6565 {
michael@0 6566 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
michael@0 6567 NS_PRECONDITION(mUpdateCount != 0,
michael@0 6568 "Should be in an update while creating frames");
michael@0 6569
michael@0 6570 #ifdef DEBUG
michael@0 6571 if (gNoisyContentUpdates) {
michael@0 6572 printf("nsCSSFrameConstructor::ContentAppended container=%p "
michael@0 6573 "first-child=%p lazy=%d\n",
michael@0 6574 static_cast<void*>(aContainer), aFirstNewContent,
michael@0 6575 aAllowLazyConstruction);
michael@0 6576 if (gReallyNoisyContentUpdates && aContainer) {
michael@0 6577 aContainer->List(stdout, 0);
michael@0 6578 }
michael@0 6579 }
michael@0 6580 #endif
michael@0 6581
michael@0 6582 #ifdef DEBUG
michael@0 6583 for (nsIContent* child = aFirstNewContent;
michael@0 6584 child;
michael@0 6585 child = child->GetNextSibling()) {
michael@0 6586 // XXX the GetContent() != child check is needed due to bug 135040.
michael@0 6587 // Remove it once that's fixed.
michael@0 6588 NS_ASSERTION(!child->GetPrimaryFrame() ||
michael@0 6589 child->GetPrimaryFrame()->GetContent() != child,
michael@0 6590 "asked to construct a frame for a node that already has a frame");
michael@0 6591 }
michael@0 6592 #endif
michael@0 6593
michael@0 6594 #ifdef MOZ_XUL
michael@0 6595 if (aContainer) {
michael@0 6596 int32_t namespaceID;
michael@0 6597 nsIAtom* tag =
michael@0 6598 mDocument->BindingManager()->ResolveTag(aContainer, &namespaceID);
michael@0 6599
michael@0 6600 // Just ignore tree tags, anyway we don't create any frames for them.
michael@0 6601 if (tag == nsGkAtoms::treechildren ||
michael@0 6602 tag == nsGkAtoms::treeitem ||
michael@0 6603 tag == nsGkAtoms::treerow)
michael@0 6604 return NS_OK;
michael@0 6605
michael@0 6606 }
michael@0 6607 #endif // MOZ_XUL
michael@0 6608
michael@0 6609 if (aContainer && aContainer->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
michael@0 6610 // Recreate frames if content is appended into a ShadowRoot
michael@0 6611 // because children of ShadowRoot are rendered in place of children
michael@0 6612 // of the host.
michael@0 6613 nsIContent* bindingParent = aContainer->GetBindingParent();
michael@0 6614 LAYOUT_PHASE_TEMP_EXIT();
michael@0 6615 nsresult rv = RecreateFramesForContent(bindingParent, false);
michael@0 6616 LAYOUT_PHASE_TEMP_REENTER();
michael@0 6617 return rv;
michael@0 6618 }
michael@0 6619
michael@0 6620 // Get the frame associated with the content
michael@0 6621 nsIFrame* parentFrame = GetFrameFor(aContainer);
michael@0 6622
michael@0 6623 // See comment in ContentRangeInserted for why this is necessary.
michael@0 6624 if (!parentFrame && !aContainer->IsActiveChildrenElement()) {
michael@0 6625 return NS_OK;
michael@0 6626 }
michael@0 6627
michael@0 6628 if (aAllowLazyConstruction &&
michael@0 6629 MaybeConstructLazily(CONTENTAPPEND, aContainer, aFirstNewContent)) {
michael@0 6630 return NS_OK;
michael@0 6631 }
michael@0 6632
michael@0 6633 LAYOUT_PHASE_TEMP_EXIT();
michael@0 6634 parentFrame = GetRangeInsertionPoint(aContainer,
michael@0 6635 aFirstNewContent, nullptr,
michael@0 6636 aAllowLazyConstruction);
michael@0 6637 LAYOUT_PHASE_TEMP_REENTER();
michael@0 6638 if (!parentFrame) {
michael@0 6639 return NS_OK;
michael@0 6640 }
michael@0 6641
michael@0 6642 LAYOUT_PHASE_TEMP_EXIT();
michael@0 6643 if (MaybeRecreateForFrameset(parentFrame, aFirstNewContent, nullptr)) {
michael@0 6644 LAYOUT_PHASE_TEMP_REENTER();
michael@0 6645 return NS_OK;
michael@0 6646 }
michael@0 6647 LAYOUT_PHASE_TEMP_REENTER();
michael@0 6648
michael@0 6649 if (parentFrame->IsLeaf()) {
michael@0 6650 // Nothing to do here; we shouldn't be constructing kids of leaves
michael@0 6651 // Clear lazy bits so we don't try to construct again.
michael@0 6652 ClearLazyBits(aFirstNewContent, nullptr);
michael@0 6653 return NS_OK;
michael@0 6654 }
michael@0 6655
michael@0 6656 if (parentFrame->IsFrameOfType(nsIFrame::eMathML)) {
michael@0 6657 LAYOUT_PHASE_TEMP_EXIT();
michael@0 6658 nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), false);
michael@0 6659 LAYOUT_PHASE_TEMP_REENTER();
michael@0 6660 return rv;
michael@0 6661 }
michael@0 6662
michael@0 6663 // If the frame we are manipulating is a ib-split frame (that is, one
michael@0 6664 // that's been created as a result of a block-in-inline situation) then we
michael@0 6665 // need to append to the last ib-split sibling, not to the frame itself.
michael@0 6666 bool parentIBSplit = IsFramePartOfIBSplit(parentFrame);
michael@0 6667 if (parentIBSplit) {
michael@0 6668 #ifdef DEBUG
michael@0 6669 if (gNoisyContentUpdates) {
michael@0 6670 printf("nsCSSFrameConstructor::ContentAppended: parentFrame=");
michael@0 6671 nsFrame::ListTag(stdout, parentFrame);
michael@0 6672 printf(" is ib-split\n");
michael@0 6673 }
michael@0 6674 #endif
michael@0 6675
michael@0 6676 // Since we're appending, we'll walk to the last anonymous frame
michael@0 6677 // that was created for the broken inline frame. But don't walk
michael@0 6678 // to the trailing inline if it's empty; stop at the block.
michael@0 6679 parentFrame = GetLastIBSplitSibling(parentFrame, false);
michael@0 6680 }
michael@0 6681
michael@0 6682 // Get continuation that parents the last child. This MUST be done
michael@0 6683 // before the AdjustAppendParentForAfterContent call.
michael@0 6684 parentFrame = nsLayoutUtils::LastContinuationWithChild(parentFrame);
michael@0 6685
michael@0 6686 // We should never get here with fieldsets, since they have multiple
michael@0 6687 // insertion points.
michael@0 6688 NS_ASSERTION(parentFrame->GetType() != nsGkAtoms::fieldSetFrame,
michael@0 6689 "Unexpected parent");
michael@0 6690
michael@0 6691 // Deal with possible :after generated content on the parent
michael@0 6692 nsIFrame* parentAfterFrame;
michael@0 6693 parentFrame =
michael@0 6694 ::AdjustAppendParentForAfterContent(mPresShell->GetPresContext(),
michael@0 6695 aContainer, parentFrame,
michael@0 6696 &parentAfterFrame);
michael@0 6697
michael@0 6698 // Create some new frames
michael@0 6699 nsFrameConstructorState state(mPresShell,
michael@0 6700 GetAbsoluteContainingBlock(parentFrame, FIXED_POS),
michael@0 6701 GetAbsoluteContainingBlock(parentFrame, ABS_POS),
michael@0 6702 GetFloatContainingBlock(parentFrame));
michael@0 6703 state.mTreeMatchContext.InitAncestors(aContainer->AsElement());
michael@0 6704
michael@0 6705 // See if the containing block has :first-letter style applied.
michael@0 6706 bool haveFirstLetterStyle = false, haveFirstLineStyle = false;
michael@0 6707 nsIFrame* containingBlock = state.mFloatedItems.containingBlock;
michael@0 6708 if (containingBlock) {
michael@0 6709 haveFirstLetterStyle = HasFirstLetterStyle(containingBlock);
michael@0 6710 haveFirstLineStyle =
michael@0 6711 ShouldHaveFirstLineStyle(containingBlock->GetContent(),
michael@0 6712 containingBlock->StyleContext());
michael@0 6713 }
michael@0 6714
michael@0 6715 if (haveFirstLetterStyle) {
michael@0 6716 // Before we get going, remove the current letter frames
michael@0 6717 RemoveLetterFrames(state.mPresContext, state.mPresShell,
michael@0 6718 containingBlock);
michael@0 6719 }
michael@0 6720
michael@0 6721 nsIAtom* frameType = parentFrame->GetType();
michael@0 6722
michael@0 6723 FlattenedChildIterator iter(aContainer);
michael@0 6724 bool haveNoXBLChildren = (!iter.XBLInvolved() || !iter.GetNextChild());
michael@0 6725 FrameConstructionItemList items;
michael@0 6726 if (aFirstNewContent->GetPreviousSibling() &&
michael@0 6727 GetParentType(frameType) == eTypeBlock &&
michael@0 6728 haveNoXBLChildren) {
michael@0 6729 // If there's a text node in the normal content list just before the new
michael@0 6730 // items, and it has no frame, make a frame construction item for it. If it
michael@0 6731 // doesn't need a frame, ConstructFramesFromItemList below won't give it
michael@0 6732 // one. No need to do all this if our parent type is not block, though,
michael@0 6733 // since WipeContainingBlock already handles that situation.
michael@0 6734 //
michael@0 6735 // Because we're appending, we don't need to worry about any text
michael@0 6736 // after the appended content; there can only be XBL anonymous content
michael@0 6737 // (text in an XBL binding is not suppressed) or generated content
michael@0 6738 // (and bare text nodes are not generated). Native anonymous content
michael@0 6739 // generated by frames never participates in inline layout.
michael@0 6740 AddTextItemIfNeeded(state, parentFrame,
michael@0 6741 aFirstNewContent->GetPreviousSibling(), items);
michael@0 6742 }
michael@0 6743 for (nsIContent* child = aFirstNewContent;
michael@0 6744 child;
michael@0 6745 child = child->GetNextSibling()) {
michael@0 6746 AddFrameConstructionItems(state, child, false, parentFrame, items);
michael@0 6747 }
michael@0 6748
michael@0 6749 nsIFrame* prevSibling = ::FindAppendPrevSibling(parentFrame, parentAfterFrame);
michael@0 6750
michael@0 6751 // Perform special check for diddling around with the frames in
michael@0 6752 // a ib-split inline frame.
michael@0 6753 // If we're appending before :after content, then we're not really
michael@0 6754 // appending, so let WipeContainingBlock know that.
michael@0 6755 LAYOUT_PHASE_TEMP_EXIT();
michael@0 6756 if (WipeContainingBlock(state, containingBlock, parentFrame, items,
michael@0 6757 true, prevSibling)) {
michael@0 6758 LAYOUT_PHASE_TEMP_REENTER();
michael@0 6759 return NS_OK;
michael@0 6760 }
michael@0 6761 LAYOUT_PHASE_TEMP_REENTER();
michael@0 6762
michael@0 6763 // If the parent is a block frame, and we're not in a special case
michael@0 6764 // where frames can be moved around, determine if the list is for the
michael@0 6765 // start or end of the block.
michael@0 6766 if (nsLayoutUtils::GetAsBlock(parentFrame) && !haveFirstLetterStyle &&
michael@0 6767 !haveFirstLineStyle && !parentIBSplit) {
michael@0 6768 items.SetLineBoundaryAtStart(!prevSibling ||
michael@0 6769 !prevSibling->IsInlineOutside() ||
michael@0 6770 prevSibling->GetType() == nsGkAtoms::brFrame);
michael@0 6771 // :after content can't be <br> so no need to check it
michael@0 6772 items.SetLineBoundaryAtEnd(!parentAfterFrame ||
michael@0 6773 !parentAfterFrame->IsInlineOutside());
michael@0 6774 }
michael@0 6775 // To suppress whitespace-only text frames, we have to verify that
michael@0 6776 // our container's DOM child list matches its flattened tree child list.
michael@0 6777 items.SetParentHasNoXBLChildren(haveNoXBLChildren);
michael@0 6778
michael@0 6779 nsFrameItems frameItems;
michael@0 6780 ConstructFramesFromItemList(state, items, parentFrame, frameItems);
michael@0 6781
michael@0 6782 for (nsIContent* child = aFirstNewContent;
michael@0 6783 child;
michael@0 6784 child = child->GetNextSibling()) {
michael@0 6785 // Invalidate now instead of before the WipeContainingBlock call, just in
michael@0 6786 // case we do wipe; in that case we don't need to do this walk at all.
michael@0 6787 // XXXbz does that matter? Would it make more sense to save some virtual
michael@0 6788 // GetChildAt calls instead and do this during construction of our
michael@0 6789 // FrameConstructionItemList?
michael@0 6790 InvalidateCanvasIfNeeded(mPresShell, child);
michael@0 6791 }
michael@0 6792
michael@0 6793 // if the container is a table and a caption was appended, it needs to be put
michael@0 6794 // in the outer table frame's additional child list.
michael@0 6795 nsFrameItems captionItems;
michael@0 6796 if (nsGkAtoms::tableFrame == frameType) {
michael@0 6797 // Pull out the captions. Note that we don't want to do that as we go,
michael@0 6798 // because processing a single caption can add a whole bunch of things to
michael@0 6799 // the frame items due to pseudoframe processing. So we'd have to pull
michael@0 6800 // captions from a list anyway; might as well do that here.
michael@0 6801 // XXXbz this is no longer true; we could pull captions directly out of the
michael@0 6802 // FrameConstructionItemList now.
michael@0 6803 PullOutCaptionFrames(frameItems, captionItems);
michael@0 6804 }
michael@0 6805
michael@0 6806 if (haveFirstLineStyle && parentFrame == containingBlock) {
michael@0 6807 // It's possible that some of the new frames go into a
michael@0 6808 // first-line frame. Look at them and see...
michael@0 6809 AppendFirstLineFrames(state, containingBlock->GetContent(),
michael@0 6810 containingBlock, frameItems);
michael@0 6811 }
michael@0 6812
michael@0 6813 // Notify the parent frame passing it the list of new frames
michael@0 6814 // Append the flowed frames to the principal child list; captions
michael@0 6815 // need special treatment
michael@0 6816 if (captionItems.NotEmpty()) { // append the caption to the outer table
michael@0 6817 NS_ASSERTION(nsGkAtoms::tableFrame == frameType, "how did that happen?");
michael@0 6818 nsIFrame* outerTable = parentFrame->GetParent();
michael@0 6819 AppendFrames(outerTable, nsIFrame::kCaptionList, captionItems);
michael@0 6820 }
michael@0 6821
michael@0 6822 if (frameItems.NotEmpty()) { // append the in-flow kids
michael@0 6823 AppendFramesToParent(state, parentFrame, frameItems, prevSibling);
michael@0 6824 }
michael@0 6825
michael@0 6826 // Recover first-letter frames
michael@0 6827 if (haveFirstLetterStyle) {
michael@0 6828 RecoverLetterFrames(containingBlock);
michael@0 6829 }
michael@0 6830
michael@0 6831 #ifdef DEBUG
michael@0 6832 if (gReallyNoisyContentUpdates) {
michael@0 6833 printf("nsCSSFrameConstructor::ContentAppended: resulting frame model:\n");
michael@0 6834 parentFrame->List(stdout, 0);
michael@0 6835 }
michael@0 6836 #endif
michael@0 6837
michael@0 6838 #ifdef ACCESSIBILITY
michael@0 6839 nsAccessibilityService* accService = nsIPresShell::AccService();
michael@0 6840 if (accService) {
michael@0 6841 accService->ContentRangeInserted(mPresShell, aContainer,
michael@0 6842 aFirstNewContent, nullptr);
michael@0 6843 }
michael@0 6844 #endif
michael@0 6845
michael@0 6846 return NS_OK;
michael@0 6847 }
michael@0 6848
michael@0 6849 #ifdef MOZ_XUL
michael@0 6850
michael@0 6851 enum content_operation
michael@0 6852 {
michael@0 6853 CONTENT_INSERTED,
michael@0 6854 CONTENT_REMOVED
michael@0 6855 };
michael@0 6856
michael@0 6857 // Helper function to lookup the listbox body frame and send a notification
michael@0 6858 // for insertion or removal of content
michael@0 6859 static
michael@0 6860 bool NotifyListBoxBody(nsPresContext* aPresContext,
michael@0 6861 nsIContent* aContainer,
michael@0 6862 nsIContent* aChild,
michael@0 6863 // Only used for the removed notification
michael@0 6864 nsIContent* aOldNextSibling,
michael@0 6865 nsIDocument* aDocument,
michael@0 6866 nsIFrame* aChildFrame,
michael@0 6867 content_operation aOperation)
michael@0 6868 {
michael@0 6869 nsListBoxBodyFrame* listBoxBodyFrame =
michael@0 6870 MaybeGetListBoxBodyFrame(aContainer, aChild);
michael@0 6871 if (listBoxBodyFrame) {
michael@0 6872 if (aOperation == CONTENT_REMOVED) {
michael@0 6873 // Except if we have an aChildFrame and its parent is not the right
michael@0 6874 // thing, then we don't do this. Pseudo frames are so much fun....
michael@0 6875 if (!aChildFrame || aChildFrame->GetParent() == listBoxBodyFrame) {
michael@0 6876 listBoxBodyFrame->OnContentRemoved(aPresContext, aContainer,
michael@0 6877 aChildFrame, aOldNextSibling);
michael@0 6878 return true;
michael@0 6879 }
michael@0 6880 } else {
michael@0 6881 listBoxBodyFrame->OnContentInserted(aPresContext, aChild);
michael@0 6882 return true;
michael@0 6883 }
michael@0 6884 }
michael@0 6885
michael@0 6886 return false;
michael@0 6887 }
michael@0 6888 #endif // MOZ_XUL
michael@0 6889
michael@0 6890 nsresult
michael@0 6891 nsCSSFrameConstructor::ContentInserted(nsIContent* aContainer,
michael@0 6892 nsIContent* aChild,
michael@0 6893 nsILayoutHistoryState* aFrameState,
michael@0 6894 bool aAllowLazyConstruction)
michael@0 6895 {
michael@0 6896 return ContentRangeInserted(aContainer,
michael@0 6897 aChild,
michael@0 6898 aChild->GetNextSibling(),
michael@0 6899 aFrameState,
michael@0 6900 aAllowLazyConstruction);
michael@0 6901 }
michael@0 6902
michael@0 6903 // ContentRangeInserted handles creating frames for a range of nodes that
michael@0 6904 // aren't at the end of their childlist. ContentRangeInserted isn't a real
michael@0 6905 // content notification, but rather it handles regular ContentInserted calls
michael@0 6906 // for a single node as well as the lazy construction of frames for a range of
michael@0 6907 // nodes when called from CreateNeededFrames. For a range of nodes to be
michael@0 6908 // suitable to have its frames constructed all at once they must meet the same
michael@0 6909 // conditions that ContentAppended imposes (GetRangeInsertionPoint checks
michael@0 6910 // these), plus more. Namely when finding the insertion prevsibling we must not
michael@0 6911 // need to consult something specific to any one node in the range, so that the
michael@0 6912 // insertion prevsibling would be the same for each node in the range. So we
michael@0 6913 // pass the first node in the range to GetInsertionPrevSibling, and if
michael@0 6914 // IsValidSibling (the only place GetInsertionPrevSibling might look at the
michael@0 6915 // passed in node itself) needs to resolve style on the node we record this and
michael@0 6916 // return that this range needs to be split up and inserted separately. Table
michael@0 6917 // captions need extra attention as we need to determine where to insert them
michael@0 6918 // in the caption list, while skipping any nodes in the range being inserted
michael@0 6919 // (because when we treat the caption frames the other nodes have had their
michael@0 6920 // frames constructed but not yet inserted into the frame tree).
michael@0 6921 nsresult
michael@0 6922 nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
michael@0 6923 nsIContent* aStartChild,
michael@0 6924 nsIContent* aEndChild,
michael@0 6925 nsILayoutHistoryState* aFrameState,
michael@0 6926 bool aAllowLazyConstruction)
michael@0 6927 {
michael@0 6928 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
michael@0 6929 NS_PRECONDITION(mUpdateCount != 0,
michael@0 6930 "Should be in an update while creating frames");
michael@0 6931
michael@0 6932 NS_PRECONDITION(aStartChild, "must always pass a child");
michael@0 6933
michael@0 6934 // XXXldb Do we need to re-resolve style to handle the CSS2 + combinator and
michael@0 6935 // the :empty pseudo-class?
michael@0 6936 #ifdef DEBUG
michael@0 6937 if (gNoisyContentUpdates) {
michael@0 6938 printf("nsCSSFrameConstructor::ContentRangeInserted container=%p "
michael@0 6939 "start-child=%p end-child=%p lazy=%d\n",
michael@0 6940 static_cast<void*>(aContainer),
michael@0 6941 static_cast<void*>(aStartChild), static_cast<void*>(aEndChild),
michael@0 6942 aAllowLazyConstruction);
michael@0 6943 if (gReallyNoisyContentUpdates) {
michael@0 6944 if (aContainer) {
michael@0 6945 aContainer->List(stdout,0);
michael@0 6946 } else {
michael@0 6947 aStartChild->List(stdout, 0);
michael@0 6948 }
michael@0 6949 }
michael@0 6950 }
michael@0 6951 #endif
michael@0 6952
michael@0 6953 #ifdef DEBUG
michael@0 6954 for (nsIContent* child = aStartChild;
michael@0 6955 child != aEndChild;
michael@0 6956 child = child->GetNextSibling()) {
michael@0 6957 // XXX the GetContent() != child check is needed due to bug 135040.
michael@0 6958 // Remove it once that's fixed.
michael@0 6959 NS_ASSERTION(!child->GetPrimaryFrame() ||
michael@0 6960 child->GetPrimaryFrame()->GetContent() != child,
michael@0 6961 "asked to construct a frame for a node that already has a frame");
michael@0 6962 }
michael@0 6963 #endif
michael@0 6964
michael@0 6965 bool isSingleInsert = (aStartChild->GetNextSibling() == aEndChild);
michael@0 6966 NS_ASSERTION(isSingleInsert || !aAllowLazyConstruction,
michael@0 6967 "range insert shouldn't be lazy");
michael@0 6968 NS_ASSERTION(isSingleInsert || aEndChild,
michael@0 6969 "range should not include all nodes after aStartChild");
michael@0 6970
michael@0 6971 #ifdef MOZ_XUL
michael@0 6972 if (aContainer && IsXULListBox(aContainer)) {
michael@0 6973 if (isSingleInsert) {
michael@0 6974 if (NotifyListBoxBody(mPresShell->GetPresContext(), aContainer,
michael@0 6975 // The insert case in NotifyListBoxBody
michael@0 6976 // doesn't use "old next sibling".
michael@0 6977 aStartChild, nullptr,
michael@0 6978 mDocument, nullptr, CONTENT_INSERTED)) {
michael@0 6979 return NS_OK;
michael@0 6980 }
michael@0 6981 } else {
michael@0 6982 // We don't handle a range insert to a listbox parent, issue single
michael@0 6983 // ContertInserted calls for each node inserted.
michael@0 6984 LAYOUT_PHASE_TEMP_EXIT();
michael@0 6985 IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
michael@0 6986 aAllowLazyConstruction);
michael@0 6987 LAYOUT_PHASE_TEMP_REENTER();
michael@0 6988 return NS_OK;
michael@0 6989 }
michael@0 6990 }
michael@0 6991 #endif // MOZ_XUL
michael@0 6992
michael@0 6993 // If we have a null parent, then this must be the document element being
michael@0 6994 // inserted, or some other child of the document in the DOM (might be a PI,
michael@0 6995 // say).
michael@0 6996 if (! aContainer) {
michael@0 6997 NS_ASSERTION(isSingleInsert,
michael@0 6998 "root node insertion should be a single insertion");
michael@0 6999 Element *docElement = mDocument->GetRootElement();
michael@0 7000
michael@0 7001 if (aStartChild != docElement) {
michael@0 7002 // Not the root element; just bail out
michael@0 7003 return NS_OK;
michael@0 7004 }
michael@0 7005
michael@0 7006 NS_PRECONDITION(nullptr == mRootElementFrame,
michael@0 7007 "root element frame already created");
michael@0 7008
michael@0 7009 // Create frames for the document element and its child elements
michael@0 7010 nsIFrame* docElementFrame =
michael@0 7011 ConstructDocElementFrame(docElement, aFrameState);
michael@0 7012
michael@0 7013 if (docElementFrame) {
michael@0 7014 InvalidateCanvasIfNeeded(mPresShell, aStartChild);
michael@0 7015 #ifdef DEBUG
michael@0 7016 if (gReallyNoisyContentUpdates) {
michael@0 7017 printf("nsCSSFrameConstructor::ContentRangeInserted: resulting frame "
michael@0 7018 "model:\n");
michael@0 7019 mFixedContainingBlock->List(stdout, 0);
michael@0 7020 }
michael@0 7021 #endif
michael@0 7022 }
michael@0 7023
michael@0 7024 if (aFrameState) {
michael@0 7025 // Restore frame state for the root scroll frame if there is one
michael@0 7026 nsIFrame* rootScrollFrame = mPresShell->GetRootScrollFrame();
michael@0 7027 if (rootScrollFrame) {
michael@0 7028 RestoreFrameStateFor(rootScrollFrame, aFrameState);
michael@0 7029 }
michael@0 7030 }
michael@0 7031
michael@0 7032 #ifdef ACCESSIBILITY
michael@0 7033 nsAccessibilityService* accService = nsIPresShell::AccService();
michael@0 7034 if (accService) {
michael@0 7035 accService->ContentRangeInserted(mPresShell, aContainer,
michael@0 7036 aStartChild, aEndChild);
michael@0 7037 }
michael@0 7038 #endif
michael@0 7039
michael@0 7040 return NS_OK;
michael@0 7041 }
michael@0 7042
michael@0 7043 if (aContainer->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
michael@0 7044 // Recreate frames if content is inserted into a ShadowRoot
michael@0 7045 // because children of ShadowRoot are rendered in place of
michael@0 7046 // the children of the host.
michael@0 7047 nsIContent* bindingParent = aContainer->GetBindingParent();
michael@0 7048 LAYOUT_PHASE_TEMP_EXIT();
michael@0 7049 nsresult rv = RecreateFramesForContent(bindingParent, false);
michael@0 7050 LAYOUT_PHASE_TEMP_REENTER();
michael@0 7051 return rv;
michael@0 7052 }
michael@0 7053
michael@0 7054 nsIFrame* parentFrame = GetFrameFor(aContainer);
michael@0 7055 // The xbl:children element won't have a frame, but default content can have the children as
michael@0 7056 // a parent. While its uncommon to change the structure of the default content itself, a label,
michael@0 7057 // for example, can be reframed by having its value attribute set or removed.
michael@0 7058 if (!parentFrame && !aContainer->IsActiveChildrenElement()) {
michael@0 7059 return NS_OK;
michael@0 7060 }
michael@0 7061
michael@0 7062 // Otherwise, we've got parent content. Find its frame.
michael@0 7063 NS_ASSERTION(!parentFrame || parentFrame->GetContent() == aContainer, "New XBL code is possibly wrong!");
michael@0 7064
michael@0 7065 if (aAllowLazyConstruction &&
michael@0 7066 MaybeConstructLazily(CONTENTINSERT, aContainer, aStartChild)) {
michael@0 7067 return NS_OK;
michael@0 7068 }
michael@0 7069
michael@0 7070 if (isSingleInsert) {
michael@0 7071 // See if we have an XBL insertion point. If so, then that's our
michael@0 7072 // real parent frame; if not, then the frame hasn't been built yet
michael@0 7073 // and we just bail.
michael@0 7074 parentFrame = GetInsertionPoint(aContainer, aStartChild);
michael@0 7075 } else {
michael@0 7076 // Get our insertion point. If we need to issue single ContentInserted's
michael@0 7077 // GetRangeInsertionPoint will take care of that for us.
michael@0 7078 LAYOUT_PHASE_TEMP_EXIT();
michael@0 7079 parentFrame = GetRangeInsertionPoint(aContainer, aStartChild, aEndChild,
michael@0 7080 aAllowLazyConstruction);
michael@0 7081 LAYOUT_PHASE_TEMP_REENTER();
michael@0 7082 }
michael@0 7083
michael@0 7084 if (!parentFrame) {
michael@0 7085 return NS_OK;
michael@0 7086 }
michael@0 7087
michael@0 7088 bool isAppend, isRangeInsertSafe;
michael@0 7089 nsIFrame* prevSibling =
michael@0 7090 GetInsertionPrevSibling(parentFrame, aContainer, aStartChild,
michael@0 7091 &isAppend, &isRangeInsertSafe);
michael@0 7092
michael@0 7093 // check if range insert is safe
michael@0 7094 if (!isSingleInsert && !isRangeInsertSafe) {
michael@0 7095 // must fall back to a single ContertInserted for each child in the range
michael@0 7096 LAYOUT_PHASE_TEMP_EXIT();
michael@0 7097 IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
michael@0 7098 aAllowLazyConstruction);
michael@0 7099 LAYOUT_PHASE_TEMP_REENTER();
michael@0 7100 return NS_OK;
michael@0 7101 }
michael@0 7102
michael@0 7103 nsIContent* container = parentFrame->GetContent();
michael@0 7104
michael@0 7105 nsIAtom* frameType = parentFrame->GetType();
michael@0 7106 LAYOUT_PHASE_TEMP_EXIT();
michael@0 7107 if (MaybeRecreateForFrameset(parentFrame, aStartChild, aEndChild)) {
michael@0 7108 LAYOUT_PHASE_TEMP_REENTER();
michael@0 7109 return NS_OK;
michael@0 7110 }
michael@0 7111 LAYOUT_PHASE_TEMP_REENTER();
michael@0 7112
michael@0 7113 // We should only get here with fieldsets when doing a single insert, because
michael@0 7114 // fieldsets have multiple insertion points.
michael@0 7115 NS_ASSERTION(isSingleInsert || frameType != nsGkAtoms::fieldSetFrame,
michael@0 7116 "Unexpected parent");
michael@0 7117 if (IsFrameForFieldSet(parentFrame, frameType) &&
michael@0 7118 aStartChild->Tag() == nsGkAtoms::legend) {
michael@0 7119 // Just reframe the parent, since figuring out whether this
michael@0 7120 // should be the new legend and then handling it is too complex.
michael@0 7121 // We could do a little better here --- check if the fieldset already
michael@0 7122 // has a legend which occurs earlier in its child list than this node,
michael@0 7123 // and if so, proceed. But we'd have to extend nsFieldSetFrame
michael@0 7124 // to locate this legend in the inserted frames and extract it.
michael@0 7125 LAYOUT_PHASE_TEMP_EXIT();
michael@0 7126 nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), false);
michael@0 7127 LAYOUT_PHASE_TEMP_REENTER();
michael@0 7128 return rv;
michael@0 7129 }
michael@0 7130
michael@0 7131 // Don't construct kids of leaves
michael@0 7132 if (parentFrame->IsLeaf()) {
michael@0 7133 // Clear lazy bits so we don't try to construct again.
michael@0 7134 ClearLazyBits(aStartChild, aEndChild);
michael@0 7135 return NS_OK;
michael@0 7136 }
michael@0 7137
michael@0 7138 if (parentFrame->IsFrameOfType(nsIFrame::eMathML)) {
michael@0 7139 LAYOUT_PHASE_TEMP_EXIT();
michael@0 7140 nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), false);
michael@0 7141 LAYOUT_PHASE_TEMP_REENTER();
michael@0 7142 return rv;
michael@0 7143 }
michael@0 7144
michael@0 7145 nsFrameConstructorState state(mPresShell,
michael@0 7146 GetAbsoluteContainingBlock(parentFrame, FIXED_POS),
michael@0 7147 GetAbsoluteContainingBlock(parentFrame, ABS_POS),
michael@0 7148 GetFloatContainingBlock(parentFrame),
michael@0 7149 aFrameState);
michael@0 7150 state.mTreeMatchContext.InitAncestors(aContainer ?
michael@0 7151 aContainer->AsElement() :
michael@0 7152 nullptr);
michael@0 7153
michael@0 7154 // Recover state for the containing block - we need to know if
michael@0 7155 // it has :first-letter or :first-line style applied to it. The
michael@0 7156 // reason we care is that the internal structure in these cases
michael@0 7157 // is not the normal structure and requires custom updating
michael@0 7158 // logic.
michael@0 7159 nsIFrame* containingBlock = state.mFloatedItems.containingBlock;
michael@0 7160 bool haveFirstLetterStyle = false;
michael@0 7161 bool haveFirstLineStyle = false;
michael@0 7162
michael@0 7163 // In order to shave off some cycles, we only dig up the
michael@0 7164 // containing block haveFirst* flags if the parent frame where
michael@0 7165 // the insertion/append is occurring is an inline or block
michael@0 7166 // container. For other types of containers this isn't relevant.
michael@0 7167 uint8_t parentDisplay = parentFrame->GetDisplay();
michael@0 7168
michael@0 7169 // Examine the parentFrame where the insertion is taking
michael@0 7170 // place. If it's a certain kind of container then some special
michael@0 7171 // processing is done.
michael@0 7172 if ((NS_STYLE_DISPLAY_BLOCK == parentDisplay) ||
michael@0 7173 (NS_STYLE_DISPLAY_LIST_ITEM == parentDisplay) ||
michael@0 7174 (NS_STYLE_DISPLAY_INLINE == parentDisplay) ||
michael@0 7175 (NS_STYLE_DISPLAY_INLINE_BLOCK == parentDisplay)) {
michael@0 7176 // Recover the special style flags for the containing block
michael@0 7177 if (containingBlock) {
michael@0 7178 haveFirstLetterStyle = HasFirstLetterStyle(containingBlock);
michael@0 7179 haveFirstLineStyle =
michael@0 7180 ShouldHaveFirstLineStyle(containingBlock->GetContent(),
michael@0 7181 containingBlock->StyleContext());
michael@0 7182 }
michael@0 7183
michael@0 7184 if (haveFirstLetterStyle) {
michael@0 7185 // If our current parentFrame is a Letter frame, use its parent as our
michael@0 7186 // new parent hint
michael@0 7187 if (parentFrame->GetType() == nsGkAtoms::letterFrame) {
michael@0 7188 // If parentFrame is out of flow, then we actually want the parent of
michael@0 7189 // the placeholder frame.
michael@0 7190 if (parentFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
michael@0 7191 nsPlaceholderFrame* placeholderFrame =
michael@0 7192 GetPlaceholderFrameFor(parentFrame);
michael@0 7193 NS_ASSERTION(placeholderFrame, "No placeholder for out-of-flow?");
michael@0 7194 parentFrame = placeholderFrame->GetParent();
michael@0 7195 } else {
michael@0 7196 parentFrame = parentFrame->GetParent();
michael@0 7197 }
michael@0 7198 }
michael@0 7199
michael@0 7200 // Remove the old letter frames before doing the insertion
michael@0 7201 RemoveLetterFrames(state.mPresContext, mPresShell,
michael@0 7202 state.mFloatedItems.containingBlock);
michael@0 7203
michael@0 7204 // Removing the letterframes messes around with the frame tree, removing
michael@0 7205 // and creating frames. We need to reget our prevsibling, parent frame,
michael@0 7206 // etc.
michael@0 7207 prevSibling = GetInsertionPrevSibling(parentFrame, aContainer,
michael@0 7208 aStartChild, &isAppend,
michael@0 7209 &isRangeInsertSafe);
michael@0 7210
michael@0 7211 // Need check whether a range insert is still safe.
michael@0 7212 if (!isSingleInsert && !isRangeInsertSafe) {
michael@0 7213 // Need to recover the letter frames first.
michael@0 7214 RecoverLetterFrames(state.mFloatedItems.containingBlock);
michael@0 7215
michael@0 7216 // must fall back to a single ContertInserted for each child in the range
michael@0 7217 LAYOUT_PHASE_TEMP_EXIT();
michael@0 7218 IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
michael@0 7219 aAllowLazyConstruction);
michael@0 7220 LAYOUT_PHASE_TEMP_REENTER();
michael@0 7221 return NS_OK;
michael@0 7222 }
michael@0 7223
michael@0 7224 container = parentFrame->GetContent();
michael@0 7225 frameType = parentFrame->GetType();
michael@0 7226 }
michael@0 7227 }
michael@0 7228
michael@0 7229 if (!prevSibling) {
michael@0 7230 // We're inserting the new frames as the first child. See if the
michael@0 7231 // parent has a :before pseudo-element
michael@0 7232 nsIFrame* firstChild = parentFrame->GetFirstPrincipalChild();
michael@0 7233
michael@0 7234 if (firstChild &&
michael@0 7235 nsLayoutUtils::IsGeneratedContentFor(container, firstChild,
michael@0 7236 nsCSSPseudoElements::before)) {
michael@0 7237 // Insert the new frames after the last continuation of the :before
michael@0 7238 prevSibling = firstChild->GetTailContinuation();
michael@0 7239 parentFrame = prevSibling->GetParent()->GetContentInsertionFrame();
michael@0 7240 // Don't change isAppend here; we'll can call AppendFrames as needed, and
michael@0 7241 // the change to our prevSibling doesn't affect that.
michael@0 7242 }
michael@0 7243 }
michael@0 7244
michael@0 7245 FrameConstructionItemList items;
michael@0 7246 ParentType parentType = GetParentType(frameType);
michael@0 7247 FlattenedChildIterator iter(aContainer);
michael@0 7248 bool haveNoXBLChildren = (!iter.XBLInvolved() || !iter.GetNextChild());
michael@0 7249 if (aStartChild->GetPreviousSibling() &&
michael@0 7250 parentType == eTypeBlock && haveNoXBLChildren) {
michael@0 7251 // If there's a text node in the normal content list just before the
michael@0 7252 // new nodes, and it has no frame, make a frame construction item for
michael@0 7253 // it, because it might need a frame now. No need to do this if our
michael@0 7254 // parent type is not block, though, since WipeContainingBlock
michael@0 7255 // already handles that sitation.
michael@0 7256 AddTextItemIfNeeded(state, parentFrame, aStartChild->GetPreviousSibling(),
michael@0 7257 items);
michael@0 7258 }
michael@0 7259
michael@0 7260 if (isSingleInsert) {
michael@0 7261 AddFrameConstructionItems(state, aStartChild,
michael@0 7262 aStartChild->IsRootOfAnonymousSubtree(),
michael@0 7263 parentFrame, items);
michael@0 7264 } else {
michael@0 7265 for (nsIContent* child = aStartChild;
michael@0 7266 child != aEndChild;
michael@0 7267 child = child->GetNextSibling()){
michael@0 7268 AddFrameConstructionItems(state, child, false, parentFrame, items);
michael@0 7269 }
michael@0 7270 }
michael@0 7271
michael@0 7272 if (aEndChild && parentType == eTypeBlock && haveNoXBLChildren) {
michael@0 7273 // If there's a text node in the normal content list just after the
michael@0 7274 // new nodes, and it has no frame, make a frame construction item for
michael@0 7275 // it, because it might need a frame now. No need to do this if our
michael@0 7276 // parent type is not block, though, since WipeContainingBlock
michael@0 7277 // already handles that sitation.
michael@0 7278 AddTextItemIfNeeded(state, parentFrame, aEndChild, items);
michael@0 7279 }
michael@0 7280
michael@0 7281 // Perform special check for diddling around with the frames in
michael@0 7282 // a special inline frame.
michael@0 7283 // If we're appending before :after content, then we're not really
michael@0 7284 // appending, so let WipeContainingBlock know that.
michael@0 7285 LAYOUT_PHASE_TEMP_EXIT();
michael@0 7286 if (WipeContainingBlock(state, containingBlock, parentFrame, items,
michael@0 7287 isAppend, prevSibling)) {
michael@0 7288 LAYOUT_PHASE_TEMP_REENTER();
michael@0 7289 return NS_OK;
michael@0 7290 }
michael@0 7291 LAYOUT_PHASE_TEMP_REENTER();
michael@0 7292
michael@0 7293 // If the container is a table and a caption will be appended, it needs to be
michael@0 7294 // put in the outer table frame's additional child list.
michael@0 7295 // We make no attempt here to set flags to indicate whether the list
michael@0 7296 // will be at the start or end of a block. It doesn't seem worthwhile.
michael@0 7297 nsFrameItems frameItems, captionItems;
michael@0 7298 ConstructFramesFromItemList(state, items, parentFrame, frameItems);
michael@0 7299
michael@0 7300 if (frameItems.NotEmpty()) {
michael@0 7301 for (nsIContent* child = aStartChild;
michael@0 7302 child != aEndChild;
michael@0 7303 child = child->GetNextSibling()){
michael@0 7304 InvalidateCanvasIfNeeded(mPresShell, child);
michael@0 7305 }
michael@0 7306
michael@0 7307 if (nsGkAtoms::tableFrame == frameType ||
michael@0 7308 nsGkAtoms::tableOuterFrame == frameType) {
michael@0 7309 PullOutCaptionFrames(frameItems, captionItems);
michael@0 7310 }
michael@0 7311 }
michael@0 7312
michael@0 7313 // If the parent of our current prevSibling is different from the frame we'll
michael@0 7314 // actually use as the parent, then the calculated insertion point is now
michael@0 7315 // invalid and as it is unknown where to insert correctly we append instead
michael@0 7316 // (bug 341858).
michael@0 7317 // This can affect our prevSibling and isAppend, but should not have any
michael@0 7318 // effect on the WipeContainingBlock above, since this should only happen
michael@0 7319 // when neither parent is a ib-split frame and should not affect whitespace
michael@0 7320 // handling inside table-related frames (and in fact, can only happen when
michael@0 7321 // one of the parents is an outer table and one is an inner table or when the
michael@0 7322 // parent is a fieldset or fieldset content frame). So it won't affect the
michael@0 7323 // {ib} or XUL box cases in WipeContainingBlock(), and the table pseudo
michael@0 7324 // handling will only be affected by us maybe thinking we're not inserting
michael@0 7325 // at the beginning, whereas we really are. That would have made us reframe
michael@0 7326 // unnecessarily, but that's ok.
michael@0 7327 // XXXbz we should push our frame construction item code up higher, so we
michael@0 7328 // know what our items are by the time we start figuring out previous
michael@0 7329 // siblings
michael@0 7330 if (prevSibling && frameItems.NotEmpty() &&
michael@0 7331 frameItems.FirstChild()->GetParent() != prevSibling->GetParent()) {
michael@0 7332 #ifdef DEBUG
michael@0 7333 nsIFrame* frame1 = frameItems.FirstChild()->GetParent();
michael@0 7334 nsIFrame* frame2 = prevSibling->GetParent();
michael@0 7335 NS_ASSERTION(!IsFramePartOfIBSplit(frame1) &&
michael@0 7336 !IsFramePartOfIBSplit(frame2),
michael@0 7337 "Neither should be ib-split");
michael@0 7338 NS_ASSERTION((frame1->GetType() == nsGkAtoms::tableFrame &&
michael@0 7339 frame2->GetType() == nsGkAtoms::tableOuterFrame) ||
michael@0 7340 (frame1->GetType() == nsGkAtoms::tableOuterFrame &&
michael@0 7341 frame2->GetType() == nsGkAtoms::tableFrame) ||
michael@0 7342 frame1->GetType() == nsGkAtoms::fieldSetFrame ||
michael@0 7343 (frame1->GetParent() &&
michael@0 7344 frame1->GetParent()->GetType() == nsGkAtoms::fieldSetFrame),
michael@0 7345 "Unexpected frame types");
michael@0 7346 #endif
michael@0 7347 isAppend = true;
michael@0 7348 nsIFrame* appendAfterFrame;
michael@0 7349 parentFrame =
michael@0 7350 ::AdjustAppendParentForAfterContent(mPresShell->GetPresContext(),
michael@0 7351 container,
michael@0 7352 frameItems.FirstChild()->GetParent(),
michael@0 7353 &appendAfterFrame);
michael@0 7354 prevSibling = ::FindAppendPrevSibling(parentFrame, appendAfterFrame);
michael@0 7355 }
michael@0 7356
michael@0 7357 if (haveFirstLineStyle && parentFrame == containingBlock) {
michael@0 7358 // It's possible that the new frame goes into a first-line
michael@0 7359 // frame. Look at it and see...
michael@0 7360 if (isAppend) {
michael@0 7361 // Use append logic when appending
michael@0 7362 AppendFirstLineFrames(state, containingBlock->GetContent(),
michael@0 7363 containingBlock, frameItems);
michael@0 7364 }
michael@0 7365 else {
michael@0 7366 // Use more complicated insert logic when inserting
michael@0 7367 // XXXbz this method is a no-op, so it's easy for the args being passed
michael@0 7368 // here to make no sense without anyone noticing... If it ever stops
michael@0 7369 // being a no-op, vet them carefully!
michael@0 7370 InsertFirstLineFrames(state, container, containingBlock, &parentFrame,
michael@0 7371 prevSibling, frameItems);
michael@0 7372 }
michael@0 7373 }
michael@0 7374
michael@0 7375 // We might have captions; put them into the caption list of the
michael@0 7376 // outer table frame.
michael@0 7377 if (captionItems.NotEmpty()) {
michael@0 7378 NS_ASSERTION(nsGkAtoms::tableFrame == frameType ||
michael@0 7379 nsGkAtoms::tableOuterFrame == frameType,
michael@0 7380 "parent for caption is not table?");
michael@0 7381 // We need to determine where to put the caption items; start with the
michael@0 7382 // the parent frame that has already been determined and get the insertion
michael@0 7383 // prevsibling of the first caption item.
michael@0 7384 nsIFrame* captionParent = parentFrame;
michael@0 7385 bool captionIsAppend;
michael@0 7386 nsIFrame* captionPrevSibling = nullptr;
michael@0 7387
michael@0 7388 // aIsRangeInsertSafe is ignored on purpose because it is irrelevant here.
michael@0 7389 bool ignored;
michael@0 7390 if (isSingleInsert) {
michael@0 7391 captionPrevSibling =
michael@0 7392 GetInsertionPrevSibling(captionParent, aContainer, aStartChild,
michael@0 7393 &captionIsAppend, &ignored);
michael@0 7394 } else {
michael@0 7395 nsIContent* firstCaption = captionItems.FirstChild()->GetContent();
michael@0 7396 // It is very important here that we skip the children in
michael@0 7397 // [aStartChild,aEndChild) when looking for a
michael@0 7398 // prevsibling.
michael@0 7399 captionPrevSibling =
michael@0 7400 GetInsertionPrevSibling(captionParent, aContainer, firstCaption,
michael@0 7401 &captionIsAppend, &ignored,
michael@0 7402 aStartChild, aEndChild);
michael@0 7403 }
michael@0 7404
michael@0 7405 nsIFrame* outerTable = nullptr;
michael@0 7406 if (GetCaptionAdjustedParent(captionParent, captionItems.FirstChild(),
michael@0 7407 &outerTable)) {
michael@0 7408 // If the parent is not an outer table frame we will try to add frames
michael@0 7409 // to a named child list that the parent does not honour and the frames
michael@0 7410 // will get lost
michael@0 7411 NS_ASSERTION(nsGkAtoms::tableOuterFrame == outerTable->GetType(),
michael@0 7412 "Pseudo frame construction failure; "
michael@0 7413 "a caption can be only a child of an outer table frame");
michael@0 7414
michael@0 7415 // If the parent of our current prevSibling is different from the frame
michael@0 7416 // we'll actually use as the parent, then the calculated insertion
michael@0 7417 // point is now invalid (bug 341382).
michael@0 7418 if (captionPrevSibling &&
michael@0 7419 captionPrevSibling->GetParent() != outerTable) {
michael@0 7420 captionPrevSibling = nullptr;
michael@0 7421 }
michael@0 7422 if (captionIsAppend) {
michael@0 7423 AppendFrames(outerTable, nsIFrame::kCaptionList, captionItems);
michael@0 7424 } else {
michael@0 7425 InsertFrames(outerTable, nsIFrame::kCaptionList,
michael@0 7426 captionPrevSibling, captionItems);
michael@0 7427 }
michael@0 7428 }
michael@0 7429 }
michael@0 7430
michael@0 7431 if (frameItems.NotEmpty()) {
michael@0 7432 // Notify the parent frame
michael@0 7433 if (isAppend) {
michael@0 7434 AppendFramesToParent(state, parentFrame, frameItems, prevSibling);
michael@0 7435 } else {
michael@0 7436 InsertFrames(parentFrame, kPrincipalList, prevSibling, frameItems);
michael@0 7437 }
michael@0 7438 }
michael@0 7439
michael@0 7440 if (haveFirstLetterStyle) {
michael@0 7441 // Recover the letter frames for the containing block when
michael@0 7442 // it has first-letter style.
michael@0 7443 RecoverLetterFrames(state.mFloatedItems.containingBlock);
michael@0 7444 }
michael@0 7445
michael@0 7446 #ifdef DEBUG
michael@0 7447 if (gReallyNoisyContentUpdates && parentFrame) {
michael@0 7448 printf("nsCSSFrameConstructor::ContentRangeInserted: resulting frame model:\n");
michael@0 7449 parentFrame->List(stdout, 0);
michael@0 7450 }
michael@0 7451 #endif
michael@0 7452
michael@0 7453 #ifdef ACCESSIBILITY
michael@0 7454 nsAccessibilityService* accService = nsIPresShell::AccService();
michael@0 7455 if (accService) {
michael@0 7456 accService->ContentRangeInserted(mPresShell, aContainer,
michael@0 7457 aStartChild, aEndChild);
michael@0 7458 }
michael@0 7459 #endif
michael@0 7460
michael@0 7461 return NS_OK;
michael@0 7462 }
michael@0 7463
michael@0 7464 nsresult
michael@0 7465 nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
michael@0 7466 nsIContent* aChild,
michael@0 7467 nsIContent* aOldNextSibling,
michael@0 7468 RemoveFlags aFlags,
michael@0 7469 bool* aDidReconstruct)
michael@0 7470 {
michael@0 7471 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
michael@0 7472 NS_PRECONDITION(mUpdateCount != 0,
michael@0 7473 "Should be in an update while destroying frames");
michael@0 7474
michael@0 7475 *aDidReconstruct = false;
michael@0 7476
michael@0 7477 // XXXldb Do we need to re-resolve style to handle the CSS2 + combinator and
michael@0 7478 // the :empty pseudo-class?
michael@0 7479
michael@0 7480 #ifdef DEBUG
michael@0 7481 if (gNoisyContentUpdates) {
michael@0 7482 printf("nsCSSFrameConstructor::ContentRemoved container=%p child=%p "
michael@0 7483 "old-next-sibling=%p\n",
michael@0 7484 static_cast<void*>(aContainer),
michael@0 7485 static_cast<void*>(aChild),
michael@0 7486 static_cast<void*>(aOldNextSibling));
michael@0 7487 if (gReallyNoisyContentUpdates) {
michael@0 7488 aContainer->List(stdout, 0);
michael@0 7489 }
michael@0 7490 }
michael@0 7491 #endif
michael@0 7492
michael@0 7493 nsPresContext *presContext = mPresShell->GetPresContext();
michael@0 7494 nsresult rv = NS_OK;
michael@0 7495
michael@0 7496 // Find the child frame that maps the content
michael@0 7497 nsIFrame* childFrame = aChild->GetPrimaryFrame();
michael@0 7498
michael@0 7499 if (!childFrame || childFrame->GetContent() != aChild) {
michael@0 7500 // XXXbz the GetContent() != aChild check is needed due to bug 135040.
michael@0 7501 // Remove it once that's fixed.
michael@0 7502 ClearUndisplayedContentIn(aChild, aContainer);
michael@0 7503 }
michael@0 7504
michael@0 7505 #ifdef MOZ_XUL
michael@0 7506 if (NotifyListBoxBody(presContext, aContainer, aChild, aOldNextSibling,
michael@0 7507 mDocument, childFrame, CONTENT_REMOVED))
michael@0 7508 return NS_OK;
michael@0 7509
michael@0 7510 #endif // MOZ_XUL
michael@0 7511
michael@0 7512 // If we're removing the root, then make sure to remove things starting at
michael@0 7513 // the viewport's child instead of the primary frame (which might even be
michael@0 7514 // null if the root had an XBL binding or display:none, even though the
michael@0 7515 // frames above it got created). We do the adjustment after the childFrame
michael@0 7516 // check above, because we do want to clear any undisplayed content we might
michael@0 7517 // have for the root. Detecting removal of a root is a little exciting; in
michael@0 7518 // particular, having a null aContainer is necessary but NOT sufficient. Due
michael@0 7519 // to how we process reframes, the content node might not even be in our
michael@0 7520 // document by now. So explicitly check whether the viewport's first kid's
michael@0 7521 // content node is aChild.
michael@0 7522 bool isRoot = false;
michael@0 7523 if (!aContainer) {
michael@0 7524 nsIFrame* viewport = GetRootFrame();
michael@0 7525 if (viewport) {
michael@0 7526 nsIFrame* firstChild = viewport->GetFirstPrincipalChild();
michael@0 7527 if (firstChild && firstChild->GetContent() == aChild) {
michael@0 7528 isRoot = true;
michael@0 7529 childFrame = firstChild;
michael@0 7530 NS_ASSERTION(!childFrame->GetNextSibling(), "How did that happen?");
michael@0 7531 }
michael@0 7532 }
michael@0 7533 }
michael@0 7534
michael@0 7535 if (aContainer && aContainer->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
michael@0 7536 // Recreate frames if content is removed from a ShadowRoot
michael@0 7537 // because it may contain an insertion point which can change
michael@0 7538 // how the host is rendered.
michael@0 7539 nsIContent* bindingParent = aContainer->GetBindingParent();
michael@0 7540 *aDidReconstruct = true;
michael@0 7541 LAYOUT_PHASE_TEMP_EXIT();
michael@0 7542 nsresult rv = RecreateFramesForContent(bindingParent, false);
michael@0 7543 LAYOUT_PHASE_TEMP_REENTER();
michael@0 7544 return rv;
michael@0 7545 }
michael@0 7546
michael@0 7547 if (childFrame) {
michael@0 7548 InvalidateCanvasIfNeeded(mPresShell, aChild);
michael@0 7549
michael@0 7550 // See whether we need to remove more than just childFrame
michael@0 7551 LAYOUT_PHASE_TEMP_EXIT();
michael@0 7552 if (MaybeRecreateContainerForFrameRemoval(childFrame, &rv)) {
michael@0 7553 LAYOUT_PHASE_TEMP_REENTER();
michael@0 7554 *aDidReconstruct = true;
michael@0 7555 return rv;
michael@0 7556 }
michael@0 7557 LAYOUT_PHASE_TEMP_REENTER();
michael@0 7558
michael@0 7559 // Get the childFrame's parent frame
michael@0 7560 nsIFrame* parentFrame = childFrame->GetParent();
michael@0 7561 nsIAtom* parentType = parentFrame->GetType();
michael@0 7562
michael@0 7563 if (parentType == nsGkAtoms::frameSetFrame &&
michael@0 7564 IsSpecialFramesetChild(aChild)) {
michael@0 7565 // Just reframe the parent, since framesets are weird like that.
michael@0 7566 *aDidReconstruct = true;
michael@0 7567 LAYOUT_PHASE_TEMP_EXIT();
michael@0 7568 nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), false);
michael@0 7569 LAYOUT_PHASE_TEMP_REENTER();
michael@0 7570 return rv;
michael@0 7571 }
michael@0 7572
michael@0 7573 // If we're a child of MathML, then we should reframe the MathML content.
michael@0 7574 // If we're non-MathML, then we would be wrapped in a block so we need to
michael@0 7575 // check our grandparent in that case.
michael@0 7576 nsIFrame* possibleMathMLAncestor = parentType == nsGkAtoms::blockFrame ?
michael@0 7577 parentFrame->GetParent() : parentFrame;
michael@0 7578 if (possibleMathMLAncestor->IsFrameOfType(nsIFrame::eMathML)) {
michael@0 7579 *aDidReconstruct = true;
michael@0 7580 LAYOUT_PHASE_TEMP_EXIT();
michael@0 7581 nsresult rv = RecreateFramesForContent(possibleMathMLAncestor->GetContent(), false);
michael@0 7582 LAYOUT_PHASE_TEMP_REENTER();
michael@0 7583 return rv;
michael@0 7584 }
michael@0 7585
michael@0 7586 // Undo XUL wrapping if it's no longer needed.
michael@0 7587 // (If we're in the XUL block-wrapping situation, parentFrame is the
michael@0 7588 // wrapper frame.)
michael@0 7589 nsIFrame* grandparentFrame = parentFrame->GetParent();
michael@0 7590 if (grandparentFrame && grandparentFrame->IsBoxFrame() &&
michael@0 7591 (grandparentFrame->GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK) &&
michael@0 7592 // check if this frame is the only one needing wrapping
michael@0 7593 aChild == AnyKidsNeedBlockParent(parentFrame->GetFirstPrincipalChild()) &&
michael@0 7594 !AnyKidsNeedBlockParent(childFrame->GetNextSibling())) {
michael@0 7595 *aDidReconstruct = true;
michael@0 7596 LAYOUT_PHASE_TEMP_EXIT();
michael@0 7597 nsresult rv = RecreateFramesForContent(grandparentFrame->GetContent(), true);
michael@0 7598 LAYOUT_PHASE_TEMP_REENTER();
michael@0 7599 return rv;
michael@0 7600 }
michael@0 7601
michael@0 7602 #ifdef ACCESSIBILITY
michael@0 7603 nsAccessibilityService* accService = nsIPresShell::AccService();
michael@0 7604 if (accService) {
michael@0 7605 accService->ContentRemoved(mPresShell, aContainer, aChild);
michael@0 7606 }
michael@0 7607 #endif
michael@0 7608
michael@0 7609 // Examine the containing-block for the removed content and see if
michael@0 7610 // :first-letter style applies.
michael@0 7611 nsIFrame* inflowChild = childFrame;
michael@0 7612 if (childFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
michael@0 7613 inflowChild = GetPlaceholderFrameFor(childFrame);
michael@0 7614 NS_ASSERTION(inflowChild, "No placeholder for out-of-flow?");
michael@0 7615 }
michael@0 7616 nsIFrame* containingBlock =
michael@0 7617 GetFloatContainingBlock(inflowChild->GetParent());
michael@0 7618 bool haveFLS = containingBlock && HasFirstLetterStyle(containingBlock);
michael@0 7619 if (haveFLS) {
michael@0 7620 // Trap out to special routine that handles adjusting a blocks
michael@0 7621 // frame tree when first-letter style is present.
michael@0 7622 #ifdef NOISY_FIRST_LETTER
michael@0 7623 printf("ContentRemoved: containingBlock=");
michael@0 7624 nsFrame::ListTag(stdout, containingBlock);
michael@0 7625 printf(" parentFrame=");
michael@0 7626 nsFrame::ListTag(stdout, parentFrame);
michael@0 7627 printf(" childFrame=");
michael@0 7628 nsFrame::ListTag(stdout, childFrame);
michael@0 7629 printf("\n");
michael@0 7630 #endif
michael@0 7631
michael@0 7632 // First update the containing blocks structure by removing the
michael@0 7633 // existing letter frames. This makes the subsequent logic
michael@0 7634 // simpler.
michael@0 7635 RemoveLetterFrames(presContext, mPresShell, containingBlock);
michael@0 7636
michael@0 7637 // Recover childFrame and parentFrame
michael@0 7638 childFrame = aChild->GetPrimaryFrame();
michael@0 7639 if (!childFrame || childFrame->GetContent() != aChild) {
michael@0 7640 // XXXbz the GetContent() != aChild check is needed due to bug 135040.
michael@0 7641 // Remove it once that's fixed.
michael@0 7642 ClearUndisplayedContentIn(aChild, aContainer);
michael@0 7643 return NS_OK;
michael@0 7644 }
michael@0 7645 parentFrame = childFrame->GetParent();
michael@0 7646 parentType = parentFrame->GetType();
michael@0 7647
michael@0 7648 #ifdef NOISY_FIRST_LETTER
michael@0 7649 printf(" ==> revised parentFrame=");
michael@0 7650 nsFrame::ListTag(stdout, parentFrame);
michael@0 7651 printf(" childFrame=");
michael@0 7652 nsFrame::ListTag(stdout, childFrame);
michael@0 7653 printf("\n");
michael@0 7654 #endif
michael@0 7655 }
michael@0 7656
michael@0 7657 #ifdef DEBUG
michael@0 7658 if (gReallyNoisyContentUpdates) {
michael@0 7659 printf("nsCSSFrameConstructor::ContentRemoved: childFrame=");
michael@0 7660 nsFrame::ListTag(stdout, childFrame);
michael@0 7661 putchar('\n');
michael@0 7662 parentFrame->List(stdout, 0);
michael@0 7663 }
michael@0 7664 #endif
michael@0 7665
michael@0 7666
michael@0 7667 // Notify the parent frame that it should delete the frame
michael@0 7668 if (childFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
michael@0 7669 childFrame = GetPlaceholderFrameFor(childFrame);
michael@0 7670 NS_ASSERTION(childFrame, "Missing placeholder frame for out of flow.");
michael@0 7671 parentFrame = childFrame->GetParent();
michael@0 7672 }
michael@0 7673 rv = RemoveFrame(nsLayoutUtils::GetChildListNameFor(childFrame),
michael@0 7674 childFrame);
michael@0 7675 //XXXfr NS_ENSURE_SUCCESS(rv, rv) ?
michael@0 7676
michael@0 7677 if (isRoot) {
michael@0 7678 mRootElementFrame = nullptr;
michael@0 7679 mRootElementStyleFrame = nullptr;
michael@0 7680 mDocElementContainingBlock = nullptr;
michael@0 7681 mPageSequenceFrame = nullptr;
michael@0 7682 mGfxScrollFrame = nullptr;
michael@0 7683 mHasRootAbsPosContainingBlock = false;
michael@0 7684 mFixedContainingBlock = GetRootFrame();
michael@0 7685 }
michael@0 7686
michael@0 7687 if (haveFLS && mRootElementFrame) {
michael@0 7688 RecoverLetterFrames(containingBlock);
michael@0 7689 }
michael@0 7690
michael@0 7691 // If we're just reconstructing frames for the element, then the
michael@0 7692 // following ContentInserted notification on the element will
michael@0 7693 // take care of fixing up any adjacent text nodes. We don't need
michael@0 7694 // to do this if the table parent type of our parent type is not
michael@0 7695 // eTypeBlock, though, because in that case the whitespace isn't
michael@0 7696 // being suppressed due to us anyway.
michael@0 7697 if (aContainer && !aChild->IsRootOfAnonymousSubtree() &&
michael@0 7698 aFlags != REMOVE_FOR_RECONSTRUCTION &&
michael@0 7699 GetParentType(parentType) == eTypeBlock) {
michael@0 7700 // Adjacent whitespace-only text nodes might have been suppressed if
michael@0 7701 // this node does not have inline ends. Create frames for them now
michael@0 7702 // if necessary.
michael@0 7703 // Reframe any text node just before the node being removed, if there is
michael@0 7704 // one, and if it's not the last child or the first child. If a whitespace
michael@0 7705 // textframe was being suppressed and it's now the last child or first
michael@0 7706 // child then it can stay suppressed since the parent must be a block
michael@0 7707 // and hence it's adjacent to a block end.
michael@0 7708 // If aOldNextSibling is null, then the text node before the node being
michael@0 7709 // removed is the last node, and we don't need to worry about it.
michael@0 7710 if (aOldNextSibling) {
michael@0 7711 nsIContent* prevSibling = aOldNextSibling->GetPreviousSibling();
michael@0 7712 if (prevSibling && prevSibling->GetPreviousSibling()) {
michael@0 7713 LAYOUT_PHASE_TEMP_EXIT();
michael@0 7714 ReframeTextIfNeeded(aContainer, prevSibling);
michael@0 7715 LAYOUT_PHASE_TEMP_REENTER();
michael@0 7716 }
michael@0 7717 }
michael@0 7718 // Reframe any text node just after the node being removed, if there is
michael@0 7719 // one, and if it's not the last child or the first child.
michael@0 7720 if (aOldNextSibling && aOldNextSibling->GetNextSibling() &&
michael@0 7721 aOldNextSibling->GetPreviousSibling()) {
michael@0 7722 LAYOUT_PHASE_TEMP_EXIT();
michael@0 7723 ReframeTextIfNeeded(aContainer, aOldNextSibling);
michael@0 7724 LAYOUT_PHASE_TEMP_REENTER();
michael@0 7725 }
michael@0 7726 }
michael@0 7727
michael@0 7728 #ifdef DEBUG
michael@0 7729 if (gReallyNoisyContentUpdates && parentFrame) {
michael@0 7730 printf("nsCSSFrameConstructor::ContentRemoved: resulting frame model:\n");
michael@0 7731 parentFrame->List(stdout, 0);
michael@0 7732 }
michael@0 7733 #endif
michael@0 7734 }
michael@0 7735
michael@0 7736 return rv;
michael@0 7737 }
michael@0 7738
michael@0 7739 /**
michael@0 7740 * This method invalidates the canvas when frames are removed or added for a
michael@0 7741 * node that might have its background propagated to the canvas, i.e., a
michael@0 7742 * document root node or an HTML BODY which is a child of the root node.
michael@0 7743 *
michael@0 7744 * @param aFrame a frame for a content node about to be removed or a frame that
michael@0 7745 * was just created for a content node that was inserted.
michael@0 7746 */
michael@0 7747 static void
michael@0 7748 InvalidateCanvasIfNeeded(nsIPresShell* presShell, nsIContent* node)
michael@0 7749 {
michael@0 7750 NS_PRECONDITION(presShell->GetRootFrame(), "What happened here?");
michael@0 7751 NS_PRECONDITION(presShell->GetPresContext(), "Say what?");
michael@0 7752
michael@0 7753 // Note that both in ContentRemoved and ContentInserted the content node
michael@0 7754 // will still have the right parent pointer, so looking at that is ok.
michael@0 7755
michael@0 7756 nsIContent* parent = node->GetParent();
michael@0 7757 if (parent) {
michael@0 7758 // Has a parent; might not be what we want
michael@0 7759 nsIContent* grandParent = parent->GetParent();
michael@0 7760 if (grandParent) {
michael@0 7761 // Has a grandparent, so not what we want
michael@0 7762 return;
michael@0 7763 }
michael@0 7764
michael@0 7765 // Check whether it's an HTML body
michael@0 7766 if (node->Tag() != nsGkAtoms::body ||
michael@0 7767 !node->IsHTML()) {
michael@0 7768 return;
michael@0 7769 }
michael@0 7770 }
michael@0 7771
michael@0 7772 // At this point the node has no parent or it's an HTML <body> child of the
michael@0 7773 // root. We might not need to invalidate in this case (eg we might be in
michael@0 7774 // XHTML or something), but chances are we want to. Play it safe.
michael@0 7775 // Invalidate the viewport.
michael@0 7776
michael@0 7777 nsIFrame* rootFrame = presShell->GetRootFrame();
michael@0 7778 rootFrame->InvalidateFrameSubtree();
michael@0 7779 }
michael@0 7780
michael@0 7781 nsIFrame*
michael@0 7782 nsCSSFrameConstructor::EnsureFrameForTextNode(nsGenericDOMDataNode* aContent)
michael@0 7783 {
michael@0 7784 if (aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) &&
michael@0 7785 !mAlwaysCreateFramesForIgnorableWhitespace) {
michael@0 7786 // Text frame may have been suppressed. Disable suppression and signal
michael@0 7787 // that a flush should be performed. We do this on a document-wide
michael@0 7788 // basis so that pages that repeatedly query metrics for
michael@0 7789 // collapsed-whitespace text nodes don't trigger pathological behavior.
michael@0 7790 mAlwaysCreateFramesForIgnorableWhitespace = true;
michael@0 7791 nsAutoScriptBlocker blocker;
michael@0 7792 BeginUpdate();
michael@0 7793 ReconstructDocElementHierarchy();
michael@0 7794 EndUpdate();
michael@0 7795 }
michael@0 7796 return aContent->GetPrimaryFrame();
michael@0 7797 }
michael@0 7798
michael@0 7799 nsresult
michael@0 7800 nsCSSFrameConstructor::CharacterDataChanged(nsIContent* aContent,
michael@0 7801 CharacterDataChangeInfo* aInfo)
michael@0 7802 {
michael@0 7803 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
michael@0 7804 nsresult rv = NS_OK;
michael@0 7805
michael@0 7806 if ((aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) &&
michael@0 7807 !aContent->TextIsOnlyWhitespace()) ||
michael@0 7808 (aContent->HasFlag(NS_REFRAME_IF_WHITESPACE) &&
michael@0 7809 aContent->TextIsOnlyWhitespace())) {
michael@0 7810 #ifdef DEBUG
michael@0 7811 nsIFrame* frame = aContent->GetPrimaryFrame();
michael@0 7812 NS_ASSERTION(!frame || !frame->IsGeneratedContentFrame(),
michael@0 7813 "Bit should never be set on generated content");
michael@0 7814 #endif
michael@0 7815 LAYOUT_PHASE_TEMP_EXIT();
michael@0 7816 nsresult rv = RecreateFramesForContent(aContent, false);
michael@0 7817 LAYOUT_PHASE_TEMP_REENTER();
michael@0 7818 return rv;
michael@0 7819 }
michael@0 7820
michael@0 7821 // Find the child frame
michael@0 7822 nsIFrame* frame = aContent->GetPrimaryFrame();
michael@0 7823
michael@0 7824 // Notify the first frame that maps the content. It will generate a reflow
michael@0 7825 // command
michael@0 7826
michael@0 7827 // It's possible the frame whose content changed isn't inserted into the
michael@0 7828 // frame hierarchy yet, or that there is no frame that maps the content
michael@0 7829 if (nullptr != frame) {
michael@0 7830 #if 0
michael@0 7831 NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
michael@0 7832 ("nsCSSFrameConstructor::CharacterDataChanged: content=%p[%s] subcontent=%p frame=%p",
michael@0 7833 aContent, ContentTag(aContent, 0),
michael@0 7834 aSubContent, frame));
michael@0 7835 #endif
michael@0 7836
michael@0 7837 // Special check for text content that is a child of a letter frame. If
michael@0 7838 // this happens, we should remove the letter frame, do whatever we're
michael@0 7839 // planning to do with this notification, then put the letter frame back.
michael@0 7840 // Note that this is basically what RecreateFramesForContent ends up doing;
michael@0 7841 // the reason we dont' want to call that here is that our text content
michael@0 7842 // could be native anonymous, in which case RecreateFramesForContent would
michael@0 7843 // completely barf on it. And recreating the non-anonymous ancestor would
michael@0 7844 // just lead us to come back into this notification (e.g. if quotes or
michael@0 7845 // counters are involved), leading to a loop.
michael@0 7846 nsIFrame* block = GetFloatContainingBlock(frame);
michael@0 7847 bool haveFirstLetterStyle = false;
michael@0 7848 if (block) {
michael@0 7849 // See if the block has first-letter style applied to it.
michael@0 7850 haveFirstLetterStyle = HasFirstLetterStyle(block);
michael@0 7851 if (haveFirstLetterStyle) {
michael@0 7852 RemoveLetterFrames(mPresShell->GetPresContext(), mPresShell,
michael@0 7853 block);
michael@0 7854 // Reget |frame|, since we might have killed it.
michael@0 7855 // Do we really need to call CharacterDataChanged in this case, though?
michael@0 7856 frame = aContent->GetPrimaryFrame();
michael@0 7857 NS_ASSERTION(frame, "Should have frame here!");
michael@0 7858 }
michael@0 7859 }
michael@0 7860
michael@0 7861 frame->CharacterDataChanged(aInfo);
michael@0 7862
michael@0 7863 if (haveFirstLetterStyle) {
michael@0 7864 RecoverLetterFrames(block);
michael@0 7865 }
michael@0 7866 }
michael@0 7867
michael@0 7868 return rv;
michael@0 7869 }
michael@0 7870
michael@0 7871 void
michael@0 7872 nsCSSFrameConstructor::BeginUpdate() {
michael@0 7873 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
michael@0 7874 "Someone forgot a script blocker");
michael@0 7875
michael@0 7876 nsRootPresContext* rootPresContext =
michael@0 7877 mPresShell->GetPresContext()->GetRootPresContext();
michael@0 7878 if (rootPresContext) {
michael@0 7879 rootPresContext->IncrementDOMGeneration();
michael@0 7880 }
michael@0 7881
michael@0 7882 ++sGlobalGenerationNumber;
michael@0 7883 ++mUpdateCount;
michael@0 7884 }
michael@0 7885
michael@0 7886 void
michael@0 7887 nsCSSFrameConstructor::EndUpdate()
michael@0 7888 {
michael@0 7889 if (mUpdateCount == 1) {
michael@0 7890 // This is the end of our last update. Before we decrement
michael@0 7891 // mUpdateCount, recalc quotes and counters as needed.
michael@0 7892
michael@0 7893 RecalcQuotesAndCounters();
michael@0 7894 NS_ASSERTION(mUpdateCount == 1, "Odd update count");
michael@0 7895 }
michael@0 7896 NS_ASSERTION(mUpdateCount, "Negative mUpdateCount!");
michael@0 7897 --mUpdateCount;
michael@0 7898 }
michael@0 7899
michael@0 7900 void
michael@0 7901 nsCSSFrameConstructor::RecalcQuotesAndCounters()
michael@0 7902 {
michael@0 7903 if (mQuotesDirty) {
michael@0 7904 mQuotesDirty = false;
michael@0 7905 mQuoteList.RecalcAll();
michael@0 7906 }
michael@0 7907
michael@0 7908 if (mCountersDirty) {
michael@0 7909 mCountersDirty = false;
michael@0 7910 mCounterManager.RecalcAll();
michael@0 7911 }
michael@0 7912
michael@0 7913 NS_ASSERTION(!mQuotesDirty, "Quotes updates will be lost");
michael@0 7914 NS_ASSERTION(!mCountersDirty, "Counter updates will be lost");
michael@0 7915 }
michael@0 7916
michael@0 7917 void
michael@0 7918 nsCSSFrameConstructor::WillDestroyFrameTree()
michael@0 7919 {
michael@0 7920 #if defined(DEBUG_dbaron_off)
michael@0 7921 mCounterManager.Dump();
michael@0 7922 #endif
michael@0 7923
michael@0 7924 mIsDestroyingFrameTree = true;
michael@0 7925
michael@0 7926 // Prevent frame tree destruction from being O(N^2)
michael@0 7927 mQuoteList.Clear();
michael@0 7928 mCounterManager.Clear();
michael@0 7929
michael@0 7930 // Remove our presshell as a style flush observer. But leave
michael@0 7931 // RestyleManager::mObservingRefreshDriver true so we don't readd to
michael@0 7932 // it even if someone tries to post restyle events on us from this
michael@0 7933 // point on for some reason.
michael@0 7934 mPresShell->GetPresContext()->RefreshDriver()->
michael@0 7935 RemoveStyleFlushObserver(mPresShell);
michael@0 7936
michael@0 7937 nsFrameManager::Destroy();
michael@0 7938 }
michael@0 7939
michael@0 7940 //STATIC
michael@0 7941
michael@0 7942 // XXXbz I'd really like this method to go away. Once we have inline-block and
michael@0 7943 // I can just use that for sized broken images, that can happen, maybe.
michael@0 7944 void nsCSSFrameConstructor::GetAlternateTextFor(nsIContent* aContent,
michael@0 7945 nsIAtom* aTag, // content object's tag
michael@0 7946 nsXPIDLString& aAltText)
michael@0 7947 {
michael@0 7948 // The "alt" attribute specifies alternate text that is rendered
michael@0 7949 // when the image can not be displayed
michael@0 7950
michael@0 7951 // If there's no "alt" attribute, and aContent is an input
michael@0 7952 // element, then use the value of the "value" attribute
michael@0 7953 if (!aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aAltText) &&
michael@0 7954 nsGkAtoms::input == aTag) {
michael@0 7955 // If there's no "value" attribute either, then use the localized string
michael@0 7956 // for "Submit" as the alternate text.
michael@0 7957 if (!aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aAltText)) {
michael@0 7958 nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
michael@0 7959 "Submit", aAltText);
michael@0 7960 }
michael@0 7961 }
michael@0 7962 }
michael@0 7963
michael@0 7964 nsIFrame*
michael@0 7965 nsCSSFrameConstructor::CreateContinuingOuterTableFrame(nsIPresShell* aPresShell,
michael@0 7966 nsPresContext* aPresContext,
michael@0 7967 nsIFrame* aFrame,
michael@0 7968 nsIFrame* aParentFrame,
michael@0 7969 nsIContent* aContent,
michael@0 7970 nsStyleContext* aStyleContext)
michael@0 7971 {
michael@0 7972 nsIFrame* newFrame = NS_NewTableOuterFrame(aPresShell, aStyleContext);
michael@0 7973
michael@0 7974 newFrame->Init(aContent, aParentFrame, aFrame);
michael@0 7975
michael@0 7976 // Create a continuing inner table frame, and if there's a caption then
michael@0 7977 // replicate the caption
michael@0 7978 nsFrameItems newChildFrames;
michael@0 7979
michael@0 7980 nsIFrame* childFrame = aFrame->GetFirstPrincipalChild();
michael@0 7981 if (childFrame) {
michael@0 7982 nsIFrame* continuingTableFrame =
michael@0 7983 CreateContinuingFrame(aPresContext, childFrame, newFrame);
michael@0 7984 newChildFrames.AddChild(continuingTableFrame);
michael@0 7985
michael@0 7986 NS_ASSERTION(!childFrame->GetNextSibling(),"there can be only one inner table frame");
michael@0 7987 }
michael@0 7988
michael@0 7989 // Set the outer table's initial child list
michael@0 7990 newFrame->SetInitialChildList(kPrincipalList, newChildFrames);
michael@0 7991
michael@0 7992 return newFrame;
michael@0 7993 }
michael@0 7994
michael@0 7995 nsIFrame*
michael@0 7996 nsCSSFrameConstructor::CreateContinuingTableFrame(nsIPresShell* aPresShell,
michael@0 7997 nsPresContext* aPresContext,
michael@0 7998 nsIFrame* aFrame,
michael@0 7999 nsIFrame* aParentFrame,
michael@0 8000 nsIContent* aContent,
michael@0 8001 nsStyleContext* aStyleContext)
michael@0 8002 {
michael@0 8003 nsIFrame* newFrame = NS_NewTableFrame(aPresShell, aStyleContext);
michael@0 8004
michael@0 8005 newFrame->Init(aContent, aParentFrame, aFrame);
michael@0 8006
michael@0 8007 // Replicate any header/footer frames
michael@0 8008 nsFrameItems childFrames;
michael@0 8009 nsIFrame* childFrame = aFrame->GetFirstPrincipalChild();
michael@0 8010 for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
michael@0 8011 // See if it's a header/footer, possibly wrapped in a scroll frame.
michael@0 8012 nsTableRowGroupFrame* rowGroupFrame =
michael@0 8013 static_cast<nsTableRowGroupFrame*>(childFrame);
michael@0 8014 // If the row group was continued, then don't replicate it.
michael@0 8015 nsIFrame* rgNextInFlow = rowGroupFrame->GetNextInFlow();
michael@0 8016 if (rgNextInFlow) {
michael@0 8017 rowGroupFrame->SetRepeatable(false);
michael@0 8018 }
michael@0 8019 else if (rowGroupFrame->IsRepeatable()) {
michael@0 8020 // Replicate the header/footer frame.
michael@0 8021 nsTableRowGroupFrame* headerFooterFrame;
michael@0 8022 nsFrameItems childItems;
michael@0 8023 nsFrameConstructorState state(mPresShell,
michael@0 8024 GetAbsoluteContainingBlock(newFrame, FIXED_POS),
michael@0 8025 GetAbsoluteContainingBlock(newFrame, ABS_POS),
michael@0 8026 nullptr);
michael@0 8027 state.mCreatingExtraFrames = true;
michael@0 8028
michael@0 8029 nsStyleContext* const headerFooterStyleContext = rowGroupFrame->StyleContext();
michael@0 8030 headerFooterFrame = static_cast<nsTableRowGroupFrame*>
michael@0 8031 (NS_NewTableRowGroupFrame(aPresShell, headerFooterStyleContext));
michael@0 8032
michael@0 8033 nsIContent* headerFooter = rowGroupFrame->GetContent();
michael@0 8034 headerFooterFrame->Init(headerFooter, newFrame, nullptr);
michael@0 8035
michael@0 8036 nsFrameConstructorSaveState absoluteSaveState;
michael@0 8037 MakeTablePartAbsoluteContainingBlockIfNeeded(state,
michael@0 8038 headerFooterStyleContext->StyleDisplay(),
michael@0 8039 absoluteSaveState,
michael@0 8040 headerFooterFrame);
michael@0 8041
michael@0 8042 ProcessChildren(state, headerFooter, rowGroupFrame->StyleContext(),
michael@0 8043 headerFooterFrame, true, childItems, false,
michael@0 8044 nullptr);
michael@0 8045 NS_ASSERTION(state.mFloatedItems.IsEmpty(), "unexpected floated element");
michael@0 8046 headerFooterFrame->SetInitialChildList(kPrincipalList, childItems);
michael@0 8047 headerFooterFrame->SetRepeatable(true);
michael@0 8048
michael@0 8049 // Table specific initialization
michael@0 8050 headerFooterFrame->InitRepeatedFrame(aPresContext, rowGroupFrame);
michael@0 8051
michael@0 8052 // XXX Deal with absolute and fixed frames...
michael@0 8053 childFrames.AddChild(headerFooterFrame);
michael@0 8054 }
michael@0 8055 }
michael@0 8056
michael@0 8057 // Set the table frame's initial child list
michael@0 8058 newFrame->SetInitialChildList(kPrincipalList, childFrames);
michael@0 8059
michael@0 8060 return newFrame;
michael@0 8061 }
michael@0 8062
michael@0 8063 nsIFrame*
michael@0 8064 nsCSSFrameConstructor::CreateContinuingFrame(nsPresContext* aPresContext,
michael@0 8065 nsIFrame* aFrame,
michael@0 8066 nsIFrame* aParentFrame,
michael@0 8067 bool aIsFluid)
michael@0 8068 {
michael@0 8069 nsIPresShell* shell = aPresContext->PresShell();
michael@0 8070 nsStyleContext* styleContext = aFrame->StyleContext();
michael@0 8071 nsIFrame* newFrame = nullptr;
michael@0 8072 nsIFrame* nextContinuation = aFrame->GetNextContinuation();
michael@0 8073 nsIFrame* nextInFlow = aFrame->GetNextInFlow();
michael@0 8074
michael@0 8075 // Use the frame type to determine what type of frame to create
michael@0 8076 nsIAtom* frameType = aFrame->GetType();
michael@0 8077 nsIContent* content = aFrame->GetContent();
michael@0 8078
michael@0 8079 NS_ASSERTION(aFrame->GetSplittableType() != NS_FRAME_NOT_SPLITTABLE,
michael@0 8080 "why CreateContinuingFrame for a non-splittable frame?");
michael@0 8081
michael@0 8082 if (nsGkAtoms::textFrame == frameType) {
michael@0 8083 newFrame = NS_NewContinuingTextFrame(shell, styleContext);
michael@0 8084 newFrame->Init(content, aParentFrame, aFrame);
michael@0 8085 } else if (nsGkAtoms::inlineFrame == frameType) {
michael@0 8086 newFrame = NS_NewInlineFrame(shell, styleContext);
michael@0 8087 newFrame->Init(content, aParentFrame, aFrame);
michael@0 8088 } else if (nsGkAtoms::blockFrame == frameType) {
michael@0 8089 newFrame = NS_NewBlockFrame(shell, styleContext);
michael@0 8090 newFrame->Init(content, aParentFrame, aFrame);
michael@0 8091 #ifdef MOZ_XUL
michael@0 8092 } else if (nsGkAtoms::XULLabelFrame == frameType) {
michael@0 8093 newFrame = NS_NewXULLabelFrame(shell, styleContext);
michael@0 8094 newFrame->Init(content, aParentFrame, aFrame);
michael@0 8095 #endif
michael@0 8096 } else if (nsGkAtoms::columnSetFrame == frameType) {
michael@0 8097 newFrame = NS_NewColumnSetFrame(shell, styleContext, nsFrameState(0));
michael@0 8098 newFrame->Init(content, aParentFrame, aFrame);
michael@0 8099 } else if (nsGkAtoms::pageFrame == frameType) {
michael@0 8100 nsIFrame* canvasFrame;
michael@0 8101 newFrame = ConstructPageFrame(shell, aPresContext, aParentFrame, aFrame,
michael@0 8102 canvasFrame);
michael@0 8103 } else if (nsGkAtoms::tableOuterFrame == frameType) {
michael@0 8104 newFrame =
michael@0 8105 CreateContinuingOuterTableFrame(shell, aPresContext, aFrame, aParentFrame,
michael@0 8106 content, styleContext);
michael@0 8107
michael@0 8108 } else if (nsGkAtoms::tableFrame == frameType) {
michael@0 8109 newFrame =
michael@0 8110 CreateContinuingTableFrame(shell, aPresContext, aFrame, aParentFrame,
michael@0 8111 content, styleContext);
michael@0 8112
michael@0 8113 } else if (nsGkAtoms::tableRowGroupFrame == frameType) {
michael@0 8114 newFrame = NS_NewTableRowGroupFrame(shell, styleContext);
michael@0 8115 newFrame->Init(content, aParentFrame, aFrame);
michael@0 8116 if (newFrame->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
michael@0 8117 nsTableFrame::RegisterPositionedTablePart(newFrame);
michael@0 8118 }
michael@0 8119 } else if (nsGkAtoms::tableRowFrame == frameType) {
michael@0 8120 newFrame = NS_NewTableRowFrame(shell, styleContext);
michael@0 8121
michael@0 8122 newFrame->Init(content, aParentFrame, aFrame);
michael@0 8123 if (newFrame->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
michael@0 8124 nsTableFrame::RegisterPositionedTablePart(newFrame);
michael@0 8125 }
michael@0 8126
michael@0 8127 // Create a continuing frame for each table cell frame
michael@0 8128 nsFrameItems newChildList;
michael@0 8129 nsIFrame* cellFrame = aFrame->GetFirstPrincipalChild();
michael@0 8130 while (cellFrame) {
michael@0 8131 // See if it's a table cell frame
michael@0 8132 if (IS_TABLE_CELL(cellFrame->GetType())) {
michael@0 8133 nsIFrame* continuingCellFrame =
michael@0 8134 CreateContinuingFrame(aPresContext, cellFrame, newFrame);
michael@0 8135 newChildList.AddChild(continuingCellFrame);
michael@0 8136 }
michael@0 8137 cellFrame = cellFrame->GetNextSibling();
michael@0 8138 }
michael@0 8139
michael@0 8140 // Set the table cell's initial child list
michael@0 8141 newFrame->SetInitialChildList(kPrincipalList, newChildList);
michael@0 8142
michael@0 8143 } else if (IS_TABLE_CELL(frameType)) {
michael@0 8144 // Warning: If you change this and add a wrapper frame around table cell
michael@0 8145 // frames, make sure Bug 368554 doesn't regress!
michael@0 8146 // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp.
michael@0 8147 newFrame = NS_NewTableCellFrame(shell, styleContext, IsBorderCollapse(aParentFrame));
michael@0 8148
michael@0 8149 newFrame->Init(content, aParentFrame, aFrame);
michael@0 8150 if (newFrame->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
michael@0 8151 nsTableFrame::RegisterPositionedTablePart(newFrame);
michael@0 8152 }
michael@0 8153
michael@0 8154 // Create a continuing area frame
michael@0 8155 nsIFrame* blockFrame = aFrame->GetFirstPrincipalChild();
michael@0 8156 nsIFrame* continuingBlockFrame =
michael@0 8157 CreateContinuingFrame(aPresContext, blockFrame, newFrame);
michael@0 8158
michael@0 8159 // Set the table cell's initial child list
michael@0 8160 SetInitialSingleChild(newFrame, continuingBlockFrame);
michael@0 8161 } else if (nsGkAtoms::lineFrame == frameType) {
michael@0 8162 newFrame = NS_NewFirstLineFrame(shell, styleContext);
michael@0 8163 newFrame->Init(content, aParentFrame, aFrame);
michael@0 8164 } else if (nsGkAtoms::letterFrame == frameType) {
michael@0 8165 newFrame = NS_NewFirstLetterFrame(shell, styleContext);
michael@0 8166 newFrame->Init(content, aParentFrame, aFrame);
michael@0 8167 } else if (nsGkAtoms::imageFrame == frameType) {
michael@0 8168 newFrame = NS_NewImageFrame(shell, styleContext);
michael@0 8169 newFrame->Init(content, aParentFrame, aFrame);
michael@0 8170 } else if (nsGkAtoms::imageControlFrame == frameType) {
michael@0 8171 newFrame = NS_NewImageControlFrame(shell, styleContext);
michael@0 8172 newFrame->Init(content, aParentFrame, aFrame);
michael@0 8173 } else if (nsGkAtoms::placeholderFrame == frameType) {
michael@0 8174 // create a continuing out of flow frame
michael@0 8175 nsIFrame* oofFrame = nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
michael@0 8176 nsIFrame* oofContFrame =
michael@0 8177 CreateContinuingFrame(aPresContext, oofFrame, aParentFrame);
michael@0 8178 newFrame =
michael@0 8179 CreatePlaceholderFrameFor(shell, content, oofContFrame, styleContext,
michael@0 8180 aParentFrame, aFrame,
michael@0 8181 aFrame->GetStateBits() & PLACEHOLDER_TYPE_MASK);
michael@0 8182 } else if (nsGkAtoms::fieldSetFrame == frameType) {
michael@0 8183 newFrame = NS_NewFieldSetFrame(shell, styleContext);
michael@0 8184
michael@0 8185 newFrame->Init(content, aParentFrame, aFrame);
michael@0 8186
michael@0 8187 // Create a continuing area frame
michael@0 8188 // XXXbz we really shouldn't have to do this by hand!
michael@0 8189 nsIFrame* blockFrame = GetFieldSetBlockFrame(aFrame);
michael@0 8190 if (blockFrame) {
michael@0 8191 nsIFrame* continuingBlockFrame =
michael@0 8192 CreateContinuingFrame(aPresContext, blockFrame, newFrame);
michael@0 8193 // Set the fieldset's initial child list
michael@0 8194 SetInitialSingleChild(newFrame, continuingBlockFrame);
michael@0 8195 } else {
michael@0 8196 MOZ_ASSERT(aFrame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER,
michael@0 8197 "FieldSet block may only be null for overflow containers");
michael@0 8198 }
michael@0 8199 } else if (nsGkAtoms::legendFrame == frameType) {
michael@0 8200 newFrame = NS_NewLegendFrame(shell, styleContext);
michael@0 8201 newFrame->Init(content, aParentFrame, aFrame);
michael@0 8202 } else if (nsGkAtoms::flexContainerFrame == frameType) {
michael@0 8203 newFrame = NS_NewFlexContainerFrame(shell, styleContext);
michael@0 8204 newFrame->Init(content, aParentFrame, aFrame);
michael@0 8205 } else {
michael@0 8206 NS_RUNTIMEABORT("unexpected frame type");
michael@0 8207 }
michael@0 8208
michael@0 8209 // Init() set newFrame to be a fluid continuation of aFrame.
michael@0 8210 // If we want a non-fluid continuation, we need to call SetPrevContinuation()
michael@0 8211 // to reset NS_FRAME_IS_FLUID_CONTINUATION.
michael@0 8212 if (!aIsFluid) {
michael@0 8213 newFrame->SetPrevContinuation(aFrame);
michael@0 8214 }
michael@0 8215
michael@0 8216 // A continuation of generated content is also generated content
michael@0 8217 if (aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT) {
michael@0 8218 newFrame->AddStateBits(NS_FRAME_GENERATED_CONTENT);
michael@0 8219 }
michael@0 8220
michael@0 8221 // A continuation of nsIAnonymousContentCreator content is also
michael@0 8222 // nsIAnonymousContentCreator created content
michael@0 8223 if (aFrame->GetStateBits() & NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT) {
michael@0 8224 newFrame->AddStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT);
michael@0 8225 }
michael@0 8226
michael@0 8227 // A continuation of an out-of-flow is also an out-of-flow
michael@0 8228 if (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
michael@0 8229 newFrame->AddStateBits(NS_FRAME_OUT_OF_FLOW);
michael@0 8230 }
michael@0 8231
michael@0 8232 if (nextInFlow) {
michael@0 8233 nextInFlow->SetPrevInFlow(newFrame);
michael@0 8234 newFrame->SetNextInFlow(nextInFlow);
michael@0 8235 } else if (nextContinuation) {
michael@0 8236 nextContinuation->SetPrevContinuation(newFrame);
michael@0 8237 newFrame->SetNextContinuation(nextContinuation);
michael@0 8238 }
michael@0 8239
michael@0 8240 NS_POSTCONDITION(!newFrame->GetNextSibling(), "unexpected sibling");
michael@0 8241 return newFrame;
michael@0 8242 }
michael@0 8243
michael@0 8244 nsresult
michael@0 8245 nsCSSFrameConstructor::ReplicateFixedFrames(nsPageContentFrame* aParentFrame)
michael@0 8246 {
michael@0 8247 // Now deal with fixed-pos things.... They should appear on all pages,
michael@0 8248 // so we want to move over the placeholders when processing the child
michael@0 8249 // of the pageContentFrame.
michael@0 8250
michael@0 8251 nsIFrame* prevPageContentFrame = aParentFrame->GetPrevInFlow();
michael@0 8252 if (!prevPageContentFrame) {
michael@0 8253 return NS_OK;
michael@0 8254 }
michael@0 8255 nsIFrame* canvasFrame = aParentFrame->GetFirstPrincipalChild();
michael@0 8256 nsIFrame* prevCanvasFrame = prevPageContentFrame->GetFirstPrincipalChild();
michael@0 8257 if (!canvasFrame || !prevCanvasFrame) {
michael@0 8258 // document's root element frame missing
michael@0 8259 return NS_ERROR_UNEXPECTED;
michael@0 8260 }
michael@0 8261
michael@0 8262 nsFrameItems fixedPlaceholders;
michael@0 8263 nsIFrame* firstFixed = prevPageContentFrame->GetFirstChild(nsIFrame::kFixedList);
michael@0 8264 if (!firstFixed) {
michael@0 8265 return NS_OK;
michael@0 8266 }
michael@0 8267
michael@0 8268 // Don't allow abs-pos descendants of the fixed content to escape the content.
michael@0 8269 // This should not normally be possible (because fixed-pos elements should
michael@0 8270 // be absolute containers) but fixed-pos tables currently aren't abs-pos
michael@0 8271 // containers.
michael@0 8272 nsFrameConstructorState state(mPresShell, aParentFrame,
michael@0 8273 nullptr,
michael@0 8274 mRootElementFrame);
michael@0 8275 state.mCreatingExtraFrames = true;
michael@0 8276
michael@0 8277 // We can't use an ancestor filter here, because we're not going to
michael@0 8278 // be usefully recurring down the tree. This means that other
michael@0 8279 // places in frame construction can't assume a filter is
michael@0 8280 // initialized!
michael@0 8281
michael@0 8282 // Iterate across fixed frames and replicate each whose placeholder is a
michael@0 8283 // descendant of aFrame. (We don't want to explicitly copy placeholders that
michael@0 8284 // are within fixed frames, because that would cause duplicates on the new
michael@0 8285 // page - bug 389619)
michael@0 8286 for (nsIFrame* fixed = firstFixed; fixed; fixed = fixed->GetNextSibling()) {
michael@0 8287 nsIFrame* prevPlaceholder = GetPlaceholderFrameFor(fixed);
michael@0 8288 if (prevPlaceholder &&
michael@0 8289 nsLayoutUtils::IsProperAncestorFrame(prevCanvasFrame, prevPlaceholder)) {
michael@0 8290 // We want to use the same style as the primary style frame for
michael@0 8291 // our content
michael@0 8292 nsIContent* content = fixed->GetContent();
michael@0 8293 nsStyleContext* styleContext =
michael@0 8294 nsLayoutUtils::GetStyleFrame(content)->StyleContext();
michael@0 8295 FrameConstructionItemList items;
michael@0 8296 AddFrameConstructionItemsInternal(state, content, canvasFrame,
michael@0 8297 content->Tag(),
michael@0 8298 content->GetNameSpaceID(),
michael@0 8299 true,
michael@0 8300 styleContext,
michael@0 8301 ITEM_ALLOW_XBL_BASE |
michael@0 8302 ITEM_ALLOW_PAGE_BREAK,
michael@0 8303 nullptr, items);
michael@0 8304 ConstructFramesFromItemList(state, items, canvasFrame, fixedPlaceholders);
michael@0 8305 }
michael@0 8306 }
michael@0 8307
michael@0 8308 // Add the placeholders to our primary child list.
michael@0 8309 // XXXbz this is a little screwed up, since the fixed frames will have
michael@0 8310 // broken auto-positioning. Oh, well.
michael@0 8311 NS_ASSERTION(!canvasFrame->GetFirstPrincipalChild(),
michael@0 8312 "leaking frames; doc root continuation must be empty");
michael@0 8313 canvasFrame->SetInitialChildList(kPrincipalList, fixedPlaceholders);
michael@0 8314 return NS_OK;
michael@0 8315 }
michael@0 8316
michael@0 8317 nsIFrame*
michael@0 8318 nsCSSFrameConstructor::GetInsertionPoint(nsIContent* aContainer,
michael@0 8319 nsIContent* aChildContent,
michael@0 8320 bool* aMultiple)
michael@0 8321 {
michael@0 8322 nsBindingManager *bindingManager = mDocument->BindingManager();
michael@0 8323
michael@0 8324 nsIContent* insertionElement;
michael@0 8325 if (aChildContent) {
michael@0 8326 // We've got an explicit insertion child. Check to see if it's
michael@0 8327 // anonymous.
michael@0 8328 if (aChildContent->GetBindingParent() == aContainer) {
michael@0 8329 // This child content is anonymous. Don't use the insertion
michael@0 8330 // point, since that's only for the explicit kids.
michael@0 8331 return GetFrameFor(aContainer);
michael@0 8332 }
michael@0 8333
michael@0 8334 insertionElement = bindingManager->FindNestedInsertionPoint(aContainer, aChildContent);
michael@0 8335 }
michael@0 8336 else {
michael@0 8337 bool multiple;
michael@0 8338 insertionElement = bindingManager->FindNestedSingleInsertionPoint(aContainer, &multiple);
michael@0 8339
michael@0 8340 if (multiple) {
michael@0 8341 if (aMultiple) {
michael@0 8342 *aMultiple = true;
michael@0 8343 }
michael@0 8344 return nullptr;
michael@0 8345 }
michael@0 8346 }
michael@0 8347
michael@0 8348 if (!insertionElement) {
michael@0 8349 insertionElement = aContainer;
michael@0 8350 }
michael@0 8351
michael@0 8352 nsIFrame* insertionPoint = GetFrameFor(insertionElement);
michael@0 8353
michael@0 8354 // fieldsets have multiple insertion points. Note that we might
michael@0 8355 // have to look at insertionElement here...
michael@0 8356 if (aMultiple && insertionElement->IsHTML(nsGkAtoms::fieldset)) {
michael@0 8357 *aMultiple = true;
michael@0 8358 }
michael@0 8359
michael@0 8360 return insertionPoint;
michael@0 8361 }
michael@0 8362
michael@0 8363 // Capture state for the frame tree rooted at the frame associated with the
michael@0 8364 // content object, aContent
michael@0 8365 void
michael@0 8366 nsCSSFrameConstructor::CaptureStateForFramesOf(nsIContent* aContent,
michael@0 8367 nsILayoutHistoryState* aHistoryState)
michael@0 8368 {
michael@0 8369 if (!aHistoryState) {
michael@0 8370 return;
michael@0 8371 }
michael@0 8372 nsIFrame* frame = aContent->GetPrimaryFrame();
michael@0 8373 if (frame == mRootElementFrame) {
michael@0 8374 frame = mFixedContainingBlock;
michael@0 8375 }
michael@0 8376 for ( ; frame;
michael@0 8377 frame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame)) {
michael@0 8378 CaptureFrameState(frame, aHistoryState);
michael@0 8379 }
michael@0 8380 }
michael@0 8381
michael@0 8382 static bool EqualURIs(mozilla::css::URLValue *aURI1,
michael@0 8383 mozilla::css::URLValue *aURI2)
michael@0 8384 {
michael@0 8385 return aURI1 == aURI2 || // handle null==null, and optimize
michael@0 8386 (aURI1 && aURI2 && aURI1->URIEquals(*aURI2));
michael@0 8387 }
michael@0 8388
michael@0 8389 nsresult
michael@0 8390 nsCSSFrameConstructor::MaybeRecreateFramesForElement(Element* aElement)
michael@0 8391 {
michael@0 8392 nsRefPtr<nsStyleContext> oldContext = GetUndisplayedContent(aElement);
michael@0 8393 if (!oldContext) {
michael@0 8394 return NS_OK;
michael@0 8395 }
michael@0 8396
michael@0 8397 // The parent has a frame, so try resolving a new context.
michael@0 8398 nsRefPtr<nsStyleContext> newContext = mPresShell->StyleSet()->
michael@0 8399 ResolveStyleFor(aElement, oldContext->GetParent());
michael@0 8400
michael@0 8401 ChangeUndisplayedContent(aElement, newContext);
michael@0 8402 const nsStyleDisplay* disp = newContext->StyleDisplay();
michael@0 8403 if (disp->mDisplay == NS_STYLE_DISPLAY_NONE) {
michael@0 8404 // We can skip trying to recreate frames here, but only if our style
michael@0 8405 // context does not have a binding URI that differs from our old one.
michael@0 8406 // Otherwise, we should try to recreate, because we may want to apply the
michael@0 8407 // new binding
michael@0 8408 if (!disp->mBinding) {
michael@0 8409 return NS_OK;
michael@0 8410 }
michael@0 8411 const nsStyleDisplay* oldDisp = oldContext->PeekStyleDisplay();
michael@0 8412 if (oldDisp && EqualURIs(disp->mBinding, oldDisp->mBinding)) {
michael@0 8413 return NS_OK;
michael@0 8414 }
michael@0 8415 }
michael@0 8416
michael@0 8417 return RecreateFramesForContent(aElement, false);
michael@0 8418 }
michael@0 8419
michael@0 8420 static nsIFrame*
michael@0 8421 FindFirstNonWhitespaceChild(nsIFrame* aParentFrame)
michael@0 8422 {
michael@0 8423 nsIFrame* f = aParentFrame->GetFirstPrincipalChild();
michael@0 8424 while (f && f->GetType() == nsGkAtoms::textFrame &&
michael@0 8425 f->GetContent()->TextIsOnlyWhitespace()) {
michael@0 8426 f = f->GetNextSibling();
michael@0 8427 }
michael@0 8428 return f;
michael@0 8429 }
michael@0 8430
michael@0 8431 static nsIFrame*
michael@0 8432 FindNextNonWhitespaceSibling(nsIFrame* aFrame)
michael@0 8433 {
michael@0 8434 nsIFrame* f = aFrame;
michael@0 8435 do {
michael@0 8436 f = f->GetNextSibling();
michael@0 8437 } while (f && f->GetType() == nsGkAtoms::textFrame &&
michael@0 8438 f->GetContent()->TextIsOnlyWhitespace());
michael@0 8439 return f;
michael@0 8440 }
michael@0 8441
michael@0 8442 static nsIFrame*
michael@0 8443 FindPreviousNonWhitespaceSibling(nsIFrame* aFrame)
michael@0 8444 {
michael@0 8445 nsIFrame* f = aFrame;
michael@0 8446 do {
michael@0 8447 f = f->GetPrevSibling();
michael@0 8448 } while (f && f->GetType() == nsGkAtoms::textFrame &&
michael@0 8449 f->GetContent()->TextIsOnlyWhitespace());
michael@0 8450 return f;
michael@0 8451 }
michael@0 8452
michael@0 8453 bool
michael@0 8454 nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame,
michael@0 8455 nsresult* aResult)
michael@0 8456 {
michael@0 8457 NS_PRECONDITION(aFrame, "Must have a frame");
michael@0 8458 NS_PRECONDITION(aFrame->GetParent(), "Frame shouldn't be root");
michael@0 8459 NS_PRECONDITION(aResult, "Null out param?");
michael@0 8460 NS_PRECONDITION(aFrame == aFrame->FirstContinuation(),
michael@0 8461 "aFrame not the result of GetPrimaryFrame()?");
michael@0 8462
michael@0 8463 if (IsFramePartOfIBSplit(aFrame)) {
michael@0 8464 // The removal functions can't handle removal of an {ib} split directly; we
michael@0 8465 // need to rebuild the containing block.
michael@0 8466 #ifdef DEBUG
michael@0 8467 if (gNoisyContentUpdates) {
michael@0 8468 printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
michael@0 8469 "frame=");
michael@0 8470 nsFrame::ListTag(stdout, aFrame);
michael@0 8471 printf(" is ib-split\n");
michael@0 8472 }
michael@0 8473 #endif
michael@0 8474
michael@0 8475 *aResult = ReframeContainingBlock(aFrame);
michael@0 8476 return true;
michael@0 8477 }
michael@0 8478
michael@0 8479 if (aFrame->GetContentInsertionFrame()->GetType() == nsGkAtoms::legendFrame &&
michael@0 8480 aFrame->GetParent()->GetType() == nsGkAtoms::fieldSetFrame) {
michael@0 8481 // When we remove the legend for a fieldset, we should reframe
michael@0 8482 // the fieldset to ensure another legend is used, if there is one
michael@0 8483 *aResult = RecreateFramesForContent(aFrame->GetParent()->GetContent(), false);
michael@0 8484 return true;
michael@0 8485 }
michael@0 8486
michael@0 8487 // Now check for possibly needing to reconstruct due to a pseudo parent
michael@0 8488 nsIFrame* inFlowFrame =
michael@0 8489 (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) ?
michael@0 8490 GetPlaceholderFrameFor(aFrame) : aFrame;
michael@0 8491 MOZ_ASSERT(inFlowFrame, "How did that happen?");
michael@0 8492 MOZ_ASSERT(inFlowFrame == inFlowFrame->FirstContinuation(),
michael@0 8493 "placeholder for primary frame has previous continuations?");
michael@0 8494 nsIFrame* parent = inFlowFrame->GetParent();
michael@0 8495 if (IsTablePseudo(parent)) {
michael@0 8496 if (FindFirstNonWhitespaceChild(parent) == inFlowFrame ||
michael@0 8497 !FindNextNonWhitespaceSibling(inFlowFrame->LastContinuation()) ||
michael@0 8498 // If we're a table-column-group, then the GetFirstChild check above is
michael@0 8499 // not going to catch cases when we're the first child.
michael@0 8500 (inFlowFrame->GetType() == nsGkAtoms::tableColGroupFrame &&
michael@0 8501 parent->GetFirstChild(nsIFrame::kColGroupList) == inFlowFrame) ||
michael@0 8502 // Similar if we're a table-caption.
michael@0 8503 (inFlowFrame->GetType() == nsGkAtoms::tableCaptionFrame &&
michael@0 8504 parent->GetFirstChild(nsIFrame::kCaptionList) == inFlowFrame)) {
michael@0 8505 // We're the first or last frame in the pseudo. Need to reframe.
michael@0 8506 // Good enough to recreate frames for |parent|'s content
michael@0 8507 *aResult = RecreateFramesForContent(parent->GetContent(), true);
michael@0 8508 return true;
michael@0 8509 }
michael@0 8510 }
michael@0 8511
michael@0 8512 // Might need to reconstruct things if this frame's nextSibling is a table
michael@0 8513 // pseudo, since removal of this frame might mean that this pseudo needs to
michael@0 8514 // get merged with the frame's prevSibling if that's also a table pseudo.
michael@0 8515 nsIFrame* nextSibling =
michael@0 8516 FindNextNonWhitespaceSibling(inFlowFrame->LastContinuation());
michael@0 8517 NS_ASSERTION(!IsTablePseudo(inFlowFrame), "Shouldn't happen here");
michael@0 8518 if (nextSibling && IsTablePseudo(nextSibling)) {
michael@0 8519 nsIFrame* prevSibling = FindPreviousNonWhitespaceSibling(inFlowFrame);
michael@0 8520 if (prevSibling && IsTablePseudo(prevSibling)) {
michael@0 8521 #ifdef DEBUG
michael@0 8522 if (gNoisyContentUpdates) {
michael@0 8523 printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
michael@0 8524 "frame=");
michael@0 8525 nsFrame::ListTag(stdout, aFrame);
michael@0 8526 printf(" has a table pseudo next sibling of different type and a "
michael@0 8527 "table pseudo prevsibling\n");
michael@0 8528 }
michael@0 8529 #endif
michael@0 8530 // Good enough to recreate frames for aFrame's parent's content; even if
michael@0 8531 // aFrame's parent is a table pseudo, that'll be the right content node.
michael@0 8532 *aResult = RecreateFramesForContent(parent->GetContent(), true);
michael@0 8533 return true;
michael@0 8534 }
michael@0 8535 }
michael@0 8536
michael@0 8537 // Might need to reconstruct things if the removed frame's nextSibling is an
michael@0 8538 // anonymous flex item. The removed frame might've been what divided two
michael@0 8539 // runs of inline content into two anonymous flex items, which would now
michael@0 8540 // need to be merged.
michael@0 8541 // NOTE: It's fine that we've advanced nextSibling past whitespace (up above);
michael@0 8542 // we're only interested in anonymous flex items here, and those can never
michael@0 8543 // be adjacent to whitespace, since they absorb contiguous runs of inline
michael@0 8544 // non-replaced content (including whitespace).
michael@0 8545 if (nextSibling && IsAnonymousFlexItem(nextSibling)) {
michael@0 8546 NS_ABORT_IF_FALSE(parent->GetType() == nsGkAtoms::flexContainerFrame,
michael@0 8547 "anonymous flex items should only exist as children "
michael@0 8548 "of flex container frames");
michael@0 8549 #ifdef DEBUG
michael@0 8550 if (gNoisyContentUpdates) {
michael@0 8551 printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
michael@0 8552 "frame=");
michael@0 8553 nsFrame::ListTag(stdout, aFrame);
michael@0 8554 printf(" has an anonymous flex item as its next sibling\n");
michael@0 8555 }
michael@0 8556 #endif // DEBUG
michael@0 8557 // Recreate frames for the flex container (the removed frame's parent)
michael@0 8558 *aResult = RecreateFramesForContent(parent->GetContent(), true);
michael@0 8559 return true;
michael@0 8560 }
michael@0 8561
michael@0 8562 // Might need to reconstruct things if the removed frame's nextSibling is
michael@0 8563 // null and its parent is an anonymous flex item. (This might be the last
michael@0 8564 // remaining child of that anonymous flex item, which can then go away.)
michael@0 8565 if (!nextSibling && IsAnonymousFlexItem(parent)) {
michael@0 8566 NS_ABORT_IF_FALSE(parent->GetParent() &&
michael@0 8567 parent->GetParent()->GetType() == nsGkAtoms::flexContainerFrame,
michael@0 8568 "anonymous flex items should only exist as children "
michael@0 8569 "of flex container frames");
michael@0 8570 #ifdef DEBUG
michael@0 8571 if (gNoisyContentUpdates) {
michael@0 8572 printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
michael@0 8573 "frame=");
michael@0 8574 nsFrame::ListTag(stdout, aFrame);
michael@0 8575 printf(" has an anonymous flex item as its parent\n");
michael@0 8576 }
michael@0 8577 #endif // DEBUG
michael@0 8578 // Recreate frames for the flex container (the removed frame's grandparent)
michael@0 8579 *aResult = RecreateFramesForContent(parent->GetParent()->GetContent(),
michael@0 8580 true);
michael@0 8581 return true;
michael@0 8582 }
michael@0 8583
michael@0 8584 #ifdef MOZ_XUL
michael@0 8585 if (aFrame->GetType() == nsGkAtoms::popupSetFrame) {
michael@0 8586 nsIRootBox* rootBox = nsIRootBox::GetRootBox(mPresShell);
michael@0 8587 if (rootBox && rootBox->GetPopupSetFrame() == aFrame) {
michael@0 8588 *aResult = ReconstructDocElementHierarchy();
michael@0 8589 return true;
michael@0 8590 }
michael@0 8591 }
michael@0 8592 #endif
michael@0 8593
michael@0 8594 // Reconstruct if inflowFrame is parent's only child, and parent is, or has,
michael@0 8595 // a non-fluid continuation, i.e. it was split by bidi resolution
michael@0 8596 if (!inFlowFrame->GetPrevSibling() &&
michael@0 8597 !inFlowFrame->GetNextSibling() &&
michael@0 8598 ((parent->GetPrevContinuation() && !parent->GetPrevInFlow()) ||
michael@0 8599 (parent->GetNextContinuation() && !parent->GetNextInFlow()))) {
michael@0 8600 *aResult = RecreateFramesForContent(parent->GetContent(), true);
michael@0 8601 return true;
michael@0 8602 }
michael@0 8603
michael@0 8604 // We might still need to reconstruct things if the parent of inFlowFrame is
michael@0 8605 // ib-split, since in that case the removal of aFrame might affect the
michael@0 8606 // splitting of its parent.
michael@0 8607 if (!IsFramePartOfIBSplit(parent)) {
michael@0 8608 return false;
michael@0 8609 }
michael@0 8610
michael@0 8611 // If inFlowFrame is not the only in-flow child of |parent|, then removing
michael@0 8612 // it will change nothing about the {ib} split.
michael@0 8613 if (inFlowFrame != parent->GetFirstPrincipalChild() ||
michael@0 8614 inFlowFrame->LastContinuation()->GetNextSibling()) {
michael@0 8615 return false;
michael@0 8616 }
michael@0 8617
michael@0 8618 // If the parent is the first or last part of the {ib} split, then
michael@0 8619 // removing one of its kids will have no effect on the splitting.
michael@0 8620 // Get the first continuation up front so we don't have to do it twice.
michael@0 8621 nsIFrame* parentFirstContinuation = parent->FirstContinuation();
michael@0 8622 if (!GetIBSplitSibling(parentFirstContinuation) ||
michael@0 8623 !GetIBSplitPrevSibling(parentFirstContinuation)) {
michael@0 8624 return false;
michael@0 8625 }
michael@0 8626
michael@0 8627 #ifdef DEBUG
michael@0 8628 if (gNoisyContentUpdates) {
michael@0 8629 printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
michael@0 8630 "frame=");
michael@0 8631 nsFrame::ListTag(stdout, parent);
michael@0 8632 printf(" is ib-split\n");
michael@0 8633 }
michael@0 8634 #endif
michael@0 8635
michael@0 8636 *aResult = ReframeContainingBlock(parent);
michael@0 8637 return true;
michael@0 8638 }
michael@0 8639
michael@0 8640 nsresult
michael@0 8641 nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent,
michael@0 8642 bool aAsyncInsert)
michael@0 8643 {
michael@0 8644 NS_PRECONDITION(!aAsyncInsert || aContent->IsElement(),
michael@0 8645 "Can only insert elements async");
michael@0 8646 // If there is no document, we don't want to recreate frames for it. (You
michael@0 8647 // shouldn't generally be giving this method content without a document
michael@0 8648 // anyway).
michael@0 8649 // Rebuilding the frame tree can have bad effects, especially if it's the
michael@0 8650 // frame tree for chrome (see bug 157322).
michael@0 8651 NS_ENSURE_TRUE(aContent->GetDocument(), NS_ERROR_FAILURE);
michael@0 8652
michael@0 8653 // Is the frame ib-split? If so, we need to reframe the containing
michael@0 8654 // block *here*, rather than trying to remove and re-insert the
michael@0 8655 // content (which would otherwise result in *two* nested reframe
michael@0 8656 // containing block from ContentRemoved() and ContentInserted(),
michael@0 8657 // below!). We'd really like to optimize away one of those
michael@0 8658 // containing block reframes, hence the code here.
michael@0 8659
michael@0 8660 nsIFrame* frame = aContent->GetPrimaryFrame();
michael@0 8661 if (frame && frame->IsFrameOfType(nsIFrame::eMathML)) {
michael@0 8662 // Reframe the topmost MathML element to prevent exponential blowup
michael@0 8663 // (see bug 397518)
michael@0 8664 while (true) {
michael@0 8665 nsIContent* parentContent = aContent->GetParent();
michael@0 8666 nsIFrame* parentContentFrame = parentContent->GetPrimaryFrame();
michael@0 8667 if (!parentContentFrame || !parentContentFrame->IsFrameOfType(nsIFrame::eMathML))
michael@0 8668 break;
michael@0 8669 aContent = parentContent;
michael@0 8670 frame = parentContentFrame;
michael@0 8671 }
michael@0 8672 }
michael@0 8673
michael@0 8674 if (frame) {
michael@0 8675 nsIFrame* nonGeneratedAncestor = nsLayoutUtils::GetNonGeneratedAncestor(frame);
michael@0 8676 if (nonGeneratedAncestor->GetContent() != aContent) {
michael@0 8677 return RecreateFramesForContent(nonGeneratedAncestor->GetContent(), aAsyncInsert);
michael@0 8678 }
michael@0 8679
michael@0 8680 if (frame->GetStateBits() & NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT) {
michael@0 8681 // Recreate the frames for the entire nsIAnonymousContentCreator tree
michael@0 8682 // since |frame| or one of its descendants may need an nsStyleContext
michael@0 8683 // that associates it to a CSS pseudo-element, and only the
michael@0 8684 // nsIAnonymousContentCreator that created this content knows how to make
michael@0 8685 // that happen.
michael@0 8686 nsIAnonymousContentCreator* acc = nullptr;
michael@0 8687 nsIFrame* ancestor = frame->GetParent();
michael@0 8688 while (!(acc = do_QueryFrame(ancestor))) {
michael@0 8689 ancestor = ancestor->GetParent();
michael@0 8690 }
michael@0 8691 NS_ASSERTION(acc, "Where is the nsIAnonymousContentCreator? We may fail "
michael@0 8692 "to recreate its content correctly");
michael@0 8693 // nsSVGUseFrame is special, and we know this is unnecessary for it.
michael@0 8694 if (ancestor->GetType() != nsGkAtoms::svgUseFrame) {
michael@0 8695 NS_ASSERTION(aContent->IsInNativeAnonymousSubtree(),
michael@0 8696 "Why is NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT set?");
michael@0 8697 return RecreateFramesForContent(ancestor->GetContent(), aAsyncInsert);
michael@0 8698 }
michael@0 8699 }
michael@0 8700
michael@0 8701 nsIFrame* parent = frame->GetParent();
michael@0 8702 nsIContent* parentContent = parent ? parent->GetContent() : nullptr;
michael@0 8703 // If the parent frame is a leaf then the subsequent insert will fail to
michael@0 8704 // create a frame, so we need to recreate the parent content. This happens
michael@0 8705 // with native anonymous content from the editor.
michael@0 8706 if (parent && parent->IsLeaf() && parentContent &&
michael@0 8707 parentContent != aContent) {
michael@0 8708 return RecreateFramesForContent(parentContent, aAsyncInsert);
michael@0 8709 }
michael@0 8710 }
michael@0 8711
michael@0 8712 nsresult rv = NS_OK;
michael@0 8713
michael@0 8714 if (frame && MaybeRecreateContainerForFrameRemoval(frame, &rv)) {
michael@0 8715 return rv;
michael@0 8716 }
michael@0 8717
michael@0 8718 nsINode* containerNode = aContent->GetParentNode();
michael@0 8719 // XXXbz how can containerNode be null here?
michael@0 8720 if (containerNode) {
michael@0 8721 // Before removing the frames associated with the content object,
michael@0 8722 // ask them to save their state onto a temporary state object.
michael@0 8723 CaptureStateForFramesOf(aContent, mTempFrameTreeState);
michael@0 8724
michael@0 8725 // Need the nsIContent parent, which might be null here, since we need to
michael@0 8726 // pass it to ContentInserted and ContentRemoved.
michael@0 8727 nsCOMPtr<nsIContent> container = aContent->GetParent();
michael@0 8728
michael@0 8729 // Remove the frames associated with the content object.
michael@0 8730 bool didReconstruct;
michael@0 8731 rv = ContentRemoved(container, aContent,
michael@0 8732 aContent->IsRootOfAnonymousSubtree() ?
michael@0 8733 nullptr :
michael@0 8734 aContent->GetNextSibling(),
michael@0 8735 REMOVE_FOR_RECONSTRUCTION, &didReconstruct);
michael@0 8736
michael@0 8737 if (NS_SUCCEEDED(rv) && !didReconstruct) {
michael@0 8738 // Now, recreate the frames associated with this content object. If
michael@0 8739 // ContentRemoved triggered reconstruction, then we don't need to do this
michael@0 8740 // because the frames will already have been built.
michael@0 8741 if (aAsyncInsert) {
michael@0 8742 RestyleManager()->PostRestyleEvent(
michael@0 8743 aContent->AsElement(), nsRestyleHint(0), nsChangeHint_ReconstructFrame);
michael@0 8744 } else {
michael@0 8745 rv = ContentInserted(container, aContent, mTempFrameTreeState, false);
michael@0 8746 }
michael@0 8747 }
michael@0 8748 }
michael@0 8749
michael@0 8750 return rv;
michael@0 8751 }
michael@0 8752
michael@0 8753 //////////////////////////////////////////////////////////////////////
michael@0 8754
michael@0 8755 // Block frame construction code
michael@0 8756
michael@0 8757 already_AddRefed<nsStyleContext>
michael@0 8758 nsCSSFrameConstructor::GetFirstLetterStyle(nsIContent* aContent,
michael@0 8759 nsStyleContext* aStyleContext)
michael@0 8760 {
michael@0 8761 if (aContent) {
michael@0 8762 return mPresShell->StyleSet()->
michael@0 8763 ResolvePseudoElementStyle(aContent->AsElement(),
michael@0 8764 nsCSSPseudoElements::ePseudo_firstLetter,
michael@0 8765 aStyleContext,
michael@0 8766 nullptr);
michael@0 8767 }
michael@0 8768 return nullptr;
michael@0 8769 }
michael@0 8770
michael@0 8771 already_AddRefed<nsStyleContext>
michael@0 8772 nsCSSFrameConstructor::GetFirstLineStyle(nsIContent* aContent,
michael@0 8773 nsStyleContext* aStyleContext)
michael@0 8774 {
michael@0 8775 if (aContent) {
michael@0 8776 return mPresShell->StyleSet()->
michael@0 8777 ResolvePseudoElementStyle(aContent->AsElement(),
michael@0 8778 nsCSSPseudoElements::ePseudo_firstLine,
michael@0 8779 aStyleContext,
michael@0 8780 nullptr);
michael@0 8781 }
michael@0 8782 return nullptr;
michael@0 8783 }
michael@0 8784
michael@0 8785 // Predicate to see if a given content (block element) has
michael@0 8786 // first-letter style applied to it.
michael@0 8787 bool
michael@0 8788 nsCSSFrameConstructor::ShouldHaveFirstLetterStyle(nsIContent* aContent,
michael@0 8789 nsStyleContext* aStyleContext)
michael@0 8790 {
michael@0 8791 return nsLayoutUtils::HasPseudoStyle(aContent, aStyleContext,
michael@0 8792 nsCSSPseudoElements::ePseudo_firstLetter,
michael@0 8793 mPresShell->GetPresContext());
michael@0 8794 }
michael@0 8795
michael@0 8796 bool
michael@0 8797 nsCSSFrameConstructor::HasFirstLetterStyle(nsIFrame* aBlockFrame)
michael@0 8798 {
michael@0 8799 NS_PRECONDITION(aBlockFrame, "Need a frame");
michael@0 8800 NS_ASSERTION(nsLayoutUtils::GetAsBlock(aBlockFrame),
michael@0 8801 "Not a block frame?");
michael@0 8802
michael@0 8803 return (aBlockFrame->GetStateBits() & NS_BLOCK_HAS_FIRST_LETTER_STYLE) != 0;
michael@0 8804 }
michael@0 8805
michael@0 8806 bool
michael@0 8807 nsCSSFrameConstructor::ShouldHaveFirstLineStyle(nsIContent* aContent,
michael@0 8808 nsStyleContext* aStyleContext)
michael@0 8809 {
michael@0 8810 bool hasFirstLine =
michael@0 8811 nsLayoutUtils::HasPseudoStyle(aContent, aStyleContext,
michael@0 8812 nsCSSPseudoElements::ePseudo_firstLine,
michael@0 8813 mPresShell->GetPresContext());
michael@0 8814 if (hasFirstLine) {
michael@0 8815 // But disable for fieldsets
michael@0 8816 int32_t namespaceID;
michael@0 8817 nsIAtom* tag = mDocument->BindingManager()->ResolveTag(aContent,
michael@0 8818 &namespaceID);
michael@0 8819 // This check must match the one in FindHTMLData.
michael@0 8820 hasFirstLine = tag != nsGkAtoms::fieldset ||
michael@0 8821 namespaceID != kNameSpaceID_XHTML;
michael@0 8822 }
michael@0 8823
michael@0 8824 return hasFirstLine;
michael@0 8825 }
michael@0 8826
michael@0 8827 void
michael@0 8828 nsCSSFrameConstructor::ShouldHaveSpecialBlockStyle(nsIContent* aContent,
michael@0 8829 nsStyleContext* aStyleContext,
michael@0 8830 bool* aHaveFirstLetterStyle,
michael@0 8831 bool* aHaveFirstLineStyle)
michael@0 8832 {
michael@0 8833 *aHaveFirstLetterStyle =
michael@0 8834 ShouldHaveFirstLetterStyle(aContent, aStyleContext);
michael@0 8835 *aHaveFirstLineStyle =
michael@0 8836 ShouldHaveFirstLineStyle(aContent, aStyleContext);
michael@0 8837 }
michael@0 8838
michael@0 8839 /* static */
michael@0 8840 const nsCSSFrameConstructor::PseudoParentData
michael@0 8841 nsCSSFrameConstructor::sPseudoParentData[eParentTypeCount] = {
michael@0 8842 { // Cell
michael@0 8843 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
michael@0 8844 FCDATA_USE_CHILD_ITEMS |
michael@0 8845 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow),
michael@0 8846 &nsCSSFrameConstructor::ConstructTableCell),
michael@0 8847 &nsCSSAnonBoxes::tableCell
michael@0 8848 },
michael@0 8849 { // Row
michael@0 8850 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
michael@0 8851 FCDATA_USE_CHILD_ITEMS |
michael@0 8852 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup),
michael@0 8853 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup),
michael@0 8854 &nsCSSAnonBoxes::tableRow
michael@0 8855 },
michael@0 8856 { // Row group
michael@0 8857 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
michael@0 8858 FCDATA_USE_CHILD_ITEMS |
michael@0 8859 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
michael@0 8860 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup),
michael@0 8861 &nsCSSAnonBoxes::tableRowGroup
michael@0 8862 },
michael@0 8863 { // Column group
michael@0 8864 FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
michael@0 8865 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_USE_CHILD_ITEMS |
michael@0 8866 FCDATA_SKIP_ABSPOS_PUSH |
michael@0 8867 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
michael@0 8868 NS_NewTableColGroupFrame),
michael@0 8869 &nsCSSAnonBoxes::tableColGroup
michael@0 8870 },
michael@0 8871 { // Table
michael@0 8872 FULL_CTOR_FCDATA(FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS,
michael@0 8873 &nsCSSFrameConstructor::ConstructTable),
michael@0 8874 &nsCSSAnonBoxes::table
michael@0 8875 }
michael@0 8876 };
michael@0 8877
michael@0 8878 void
michael@0 8879 nsCSSFrameConstructor::CreateNeededAnonFlexItems(
michael@0 8880 nsFrameConstructorState& aState,
michael@0 8881 FrameConstructionItemList& aItems,
michael@0 8882 nsIFrame* aParentFrame)
michael@0 8883 {
michael@0 8884 if (aItems.IsEmpty() ||
michael@0 8885 aParentFrame->GetType() != nsGkAtoms::flexContainerFrame) {
michael@0 8886 return;
michael@0 8887 }
michael@0 8888
michael@0 8889 FCItemIterator iter(aItems);
michael@0 8890 do {
michael@0 8891 // Advance iter past children that don't want to be wrapped
michael@0 8892 if (iter.SkipItemsThatDontNeedAnonFlexItem(aState)) {
michael@0 8893 // Hit the end of the items without finding any remaining children that
michael@0 8894 // need to be wrapped. We're finished!
michael@0 8895 return;
michael@0 8896 }
michael@0 8897
michael@0 8898 // If our next potentially-wrappable child is whitespace, then see if
michael@0 8899 // there's anything wrappable immediately after it. If not, we just drop
michael@0 8900 // the whitespace and move on. (We're not supposed to create any anonymous
michael@0 8901 // flex items that _only_ contain whitespace).
michael@0 8902 // (BUT if this is generated content, then we don't give whitespace nodes
michael@0 8903 // any special treatment, because they're probably not really whitespace --
michael@0 8904 // they're just temporarily empty, waiting for their generated text.)
michael@0 8905 // XXXdholbert If this node's generated text will *actually end up being
michael@0 8906 // entirely whitespace*, then we technically should still skip over it, per
michael@0 8907 // the flexbox spec. I'm not bothering with that at this point, since it's
michael@0 8908 // a pretty extreme edge case.
michael@0 8909 if (!aParentFrame->IsGeneratedContentFrame() &&
michael@0 8910 iter.item().IsWhitespace(aState)) {
michael@0 8911 FCItemIterator afterWhitespaceIter(iter);
michael@0 8912 bool hitEnd = afterWhitespaceIter.SkipWhitespace(aState);
michael@0 8913 bool nextChildNeedsAnonFlexItem =
michael@0 8914 !hitEnd && afterWhitespaceIter.item().NeedsAnonFlexItem(aState);
michael@0 8915
michael@0 8916 if (!nextChildNeedsAnonFlexItem) {
michael@0 8917 // There's nothing after the whitespace that we need to wrap, so we
michael@0 8918 // just drop this run of whitespace.
michael@0 8919 iter.DeleteItemsTo(afterWhitespaceIter);
michael@0 8920 if (hitEnd) {
michael@0 8921 // Nothing left to do -- we're finished!
michael@0 8922 return;
michael@0 8923 }
michael@0 8924 // else, we have a next child and it does not want to be wrapped. So,
michael@0 8925 // we jump back to the beginning of the loop to skip over that child
michael@0 8926 // (and anything else non-wrappable after it)
michael@0 8927 NS_ABORT_IF_FALSE(!iter.IsDone() &&
michael@0 8928 !iter.item().NeedsAnonFlexItem(aState),
michael@0 8929 "hitEnd and/or nextChildNeedsAnonFlexItem lied");
michael@0 8930 continue;
michael@0 8931 }
michael@0 8932 }
michael@0 8933
michael@0 8934 // Now |iter| points to the first child that needs to be wrapped in an
michael@0 8935 // anonymous flex item. Now we see how many children after it also want
michael@0 8936 // to be wrapped in an anonymous flex item.
michael@0 8937 FCItemIterator endIter(iter); // iterator to find the end of the group
michael@0 8938 endIter.SkipItemsThatNeedAnonFlexItem(aState);
michael@0 8939
michael@0 8940 NS_ASSERTION(iter != endIter,
michael@0 8941 "Should've had at least one wrappable child to seek past");
michael@0 8942
michael@0 8943 // Now, we create the anonymous flex item to contain the children
michael@0 8944 // between |iter| and |endIter|.
michael@0 8945 nsIAtom* pseudoType = nsCSSAnonBoxes::anonymousFlexItem;
michael@0 8946 nsStyleContext* parentStyle = aParentFrame->StyleContext();
michael@0 8947 nsIContent* parentContent = aParentFrame->GetContent();
michael@0 8948 already_AddRefed<nsStyleContext> wrapperStyle =
michael@0 8949 mPresShell->StyleSet()->ResolveAnonymousBoxStyle(pseudoType, parentStyle);
michael@0 8950
michael@0 8951 static const FrameConstructionData sBlockFormattingContextFCData =
michael@0 8952 FCDATA_DECL(FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS,
michael@0 8953 NS_NewBlockFormattingContext);
michael@0 8954
michael@0 8955 FrameConstructionItem* newItem =
michael@0 8956 new FrameConstructionItem(&sBlockFormattingContextFCData,
michael@0 8957 // Use the content of our parent frame
michael@0 8958 parentContent,
michael@0 8959 // Lie about the tag; it doesn't matter anyway
michael@0 8960 pseudoType,
michael@0 8961 iter.item().mNameSpaceID,
michael@0 8962 // no pending binding
michael@0 8963 nullptr,
michael@0 8964 wrapperStyle,
michael@0 8965 true, nullptr);
michael@0 8966
michael@0 8967 newItem->mIsAllInline = newItem->mHasInlineEnds =
michael@0 8968 newItem->mStyleContext->StyleDisplay()->IsInlineOutsideStyle();
michael@0 8969 newItem->mIsBlock = !newItem->mIsAllInline;
michael@0 8970
michael@0 8971 NS_ABORT_IF_FALSE(!newItem->mIsAllInline && newItem->mIsBlock,
michael@0 8972 "expecting anonymous flex items to be block-level "
michael@0 8973 "(this will make a difference when we encounter "
michael@0 8974 "'flex-align: baseline')");
michael@0 8975
michael@0 8976 // Anonymous flex items induce line boundaries around their
michael@0 8977 // contents.
michael@0 8978 newItem->mChildItems.SetLineBoundaryAtStart(true);
michael@0 8979 newItem->mChildItems.SetLineBoundaryAtEnd(true);
michael@0 8980 // The parent of the items in aItems is also the parent of the items
michael@0 8981 // in mChildItems
michael@0 8982 newItem->mChildItems.SetParentHasNoXBLChildren(
michael@0 8983 aItems.ParentHasNoXBLChildren());
michael@0 8984
michael@0 8985 // Eat up all items between |iter| and |endIter| and put them in our
michael@0 8986 // wrapper. This advances |iter| to point to |endIter|.
michael@0 8987 iter.AppendItemsToList(endIter, newItem->mChildItems);
michael@0 8988
michael@0 8989 iter.InsertItem(newItem);
michael@0 8990 } while (!iter.IsDone());
michael@0 8991 }
michael@0 8992
michael@0 8993 /*
michael@0 8994 * This function works as follows: we walk through the child list (aItems) and
michael@0 8995 * find items that cannot have aParentFrame as their parent. We wrap
michael@0 8996 * continuous runs of such items into a FrameConstructionItem for a frame that
michael@0 8997 * gets them closer to their desired parents. For example, a run of non-row
michael@0 8998 * children of a row-group will get wrapped in a row. When we later construct
michael@0 8999 * the frame for this wrapper (in this case for the row), it'll be the correct
michael@0 9000 * parent for the cells in the set of items we wrapped or we'll wrap cells
michael@0 9001 * around everything else. At the end of this method, aItems is guaranteed to
michael@0 9002 * contain only items for frames that can be direct kids of aParentFrame.
michael@0 9003 */
michael@0 9004 void
michael@0 9005 nsCSSFrameConstructor::CreateNeededTablePseudos(nsFrameConstructorState& aState,
michael@0 9006 FrameConstructionItemList& aItems,
michael@0 9007 nsIFrame* aParentFrame)
michael@0 9008 {
michael@0 9009 ParentType ourParentType = GetParentType(aParentFrame);
michael@0 9010 if (aItems.AllWantParentType(ourParentType)) {
michael@0 9011 // Nothing to do here
michael@0 9012 return;
michael@0 9013 }
michael@0 9014
michael@0 9015 FCItemIterator iter(aItems);
michael@0 9016 do {
michael@0 9017 if (iter.SkipItemsWantingParentType(ourParentType)) {
michael@0 9018 // Nothing else to do here; we're finished
michael@0 9019 return;
michael@0 9020 }
michael@0 9021
michael@0 9022 // Now we're pointing to the first child that wants a different parent
michael@0 9023 // type.
michael@0 9024
michael@0 9025 // Now try to figure out what kids we can group together. We can generally
michael@0 9026 // group everything that has a different desired parent type from us. Two
michael@0 9027 // exceptions to this:
michael@0 9028 // 1) If our parent type is table, we can't group columns with anything
michael@0 9029 // else other than whitespace.
michael@0 9030 // 2) Whitespace that lies between two things we can group which both want
michael@0 9031 // a non-block parent should be dropped, even if we can't group them
michael@0 9032 // with each other and even if the whitespace wants a parent of
michael@0 9033 // ourParentType. Ends of the list count as things that don't want a
michael@0 9034 // block parent (so that for example we'll drop a whitespace-only list).
michael@0 9035
michael@0 9036 FCItemIterator endIter(iter); /* iterator to find the end of the group */
michael@0 9037 ParentType groupingParentType = endIter.item().DesiredParentType();
michael@0 9038 if (aItems.AllWantParentType(groupingParentType) &&
michael@0 9039 groupingParentType != eTypeBlock) {
michael@0 9040 // Just group them all and be done with it. We need the check for
michael@0 9041 // eTypeBlock here to catch the "all the items are whitespace" case
michael@0 9042 // described above.
michael@0 9043 endIter.SetToEnd();
michael@0 9044 } else {
michael@0 9045 // Locate the end of the group.
michael@0 9046
michael@0 9047 // Keep track of the type the previous item wanted, in case we have to
michael@0 9048 // deal with whitespace. Start it off with ourParentType, since that's
michael@0 9049 // the last thing |iter| would have skipped over.
michael@0 9050 ParentType prevParentType = ourParentType;
michael@0 9051 do {
michael@0 9052 /* Walk an iterator past any whitespace that we might be able to drop from the list */
michael@0 9053 FCItemIterator spaceEndIter(endIter);
michael@0 9054 if (prevParentType != eTypeBlock &&
michael@0 9055 !aParentFrame->IsGeneratedContentFrame() &&
michael@0 9056 spaceEndIter.item().IsWhitespace(aState)) {
michael@0 9057 bool trailingSpaces = spaceEndIter.SkipWhitespace(aState);
michael@0 9058
michael@0 9059 // We drop the whitespace if these are not trailing spaces and the next item
michael@0 9060 // does not want a block parent (see case 2 above)
michael@0 9061 // if these are trailing spaces and aParentFrame is a tabular container
michael@0 9062 // according to rule 1.3 of CSS 2.1 Sec 17.2.1. (Being a tabular container
michael@0 9063 // pretty much means ourParentType != eTypeBlock besides the eTypeColGroup case,
michael@0 9064 // which won't reach here.)
michael@0 9065 if ((trailingSpaces && ourParentType != eTypeBlock) ||
michael@0 9066 (!trailingSpaces && spaceEndIter.item().DesiredParentType() !=
michael@0 9067 eTypeBlock)) {
michael@0 9068 bool updateStart = (iter == endIter);
michael@0 9069 endIter.DeleteItemsTo(spaceEndIter);
michael@0 9070 NS_ASSERTION(trailingSpaces == endIter.IsDone(), "These should match");
michael@0 9071
michael@0 9072 if (updateStart) {
michael@0 9073 iter = endIter;
michael@0 9074 }
michael@0 9075
michael@0 9076 if (trailingSpaces) {
michael@0 9077 break; /* Found group end */
michael@0 9078 }
michael@0 9079
michael@0 9080 if (updateStart) {
michael@0 9081 // Update groupingParentType, since it might have been eTypeBlock
michael@0 9082 // just because of the whitespace.
michael@0 9083 groupingParentType = iter.item().DesiredParentType();
michael@0 9084 }
michael@0 9085 }
michael@0 9086 }
michael@0 9087
michael@0 9088 // Now endIter points to a non-whitespace item or a non-droppable
michael@0 9089 // whitespace item. In the latter case, if this is the end of the group
michael@0 9090 // we'll traverse this whitespace again. But it'll all just be quick
michael@0 9091 // DesiredParentType() checks which will match ourParentType (that's
michael@0 9092 // what it means that this is the group end), so it's OK.
michael@0 9093 prevParentType = endIter.item().DesiredParentType();
michael@0 9094 if (prevParentType == ourParentType) {
michael@0 9095 // End the group at endIter.
michael@0 9096 break;
michael@0 9097 }
michael@0 9098
michael@0 9099 if (ourParentType == eTypeTable &&
michael@0 9100 (prevParentType == eTypeColGroup) !=
michael@0 9101 (groupingParentType == eTypeColGroup)) {
michael@0 9102 // Either we started with columns and now found something else, or vice
michael@0 9103 // versa. In any case, end the grouping.
michael@0 9104 break;
michael@0 9105 }
michael@0 9106
michael@0 9107 // Include the whitespace we didn't drop (if any) in the group, since
michael@0 9108 // this is not the end of the group. Note that this doesn't change
michael@0 9109 // prevParentType, since if we didn't drop the whitespace then we ended
michael@0 9110 // at something that wants a block parent.
michael@0 9111 endIter = spaceEndIter;
michael@0 9112
michael@0 9113 endIter.Next();
michael@0 9114 } while (!endIter.IsDone());
michael@0 9115 }
michael@0 9116
michael@0 9117 if (iter == endIter) {
michael@0 9118 // Nothing to wrap here; just skipped some whitespace
michael@0 9119 continue;
michael@0 9120 }
michael@0 9121
michael@0 9122 // Now group together all the items between iter and endIter. The right
michael@0 9123 // parent type to use depends on ourParentType.
michael@0 9124 ParentType wrapperType;
michael@0 9125 switch (ourParentType) {
michael@0 9126 case eTypeBlock:
michael@0 9127 wrapperType = eTypeTable;
michael@0 9128 break;
michael@0 9129 case eTypeRow:
michael@0 9130 // The parent type for a cell is eTypeBlock, since that's what a cell
michael@0 9131 // looks like to its kids.
michael@0 9132 wrapperType = eTypeBlock;
michael@0 9133 break;
michael@0 9134 case eTypeRowGroup:
michael@0 9135 wrapperType = eTypeRow;
michael@0 9136 break;
michael@0 9137 case eTypeTable:
michael@0 9138 // Either colgroup or rowgroup, depending on what we're grouping.
michael@0 9139 wrapperType = groupingParentType == eTypeColGroup ?
michael@0 9140 eTypeColGroup : eTypeRowGroup;
michael@0 9141 break;
michael@0 9142 default:
michael@0 9143 MOZ_CRASH("Colgroups should be suppresing non-col child items");
michael@0 9144 }
michael@0 9145
michael@0 9146 const PseudoParentData& pseudoData = sPseudoParentData[wrapperType];
michael@0 9147 nsIAtom* pseudoType = *pseudoData.mPseudoType;
michael@0 9148 nsStyleContext* parentStyle = aParentFrame->StyleContext();
michael@0 9149 nsIContent* parentContent = aParentFrame->GetContent();
michael@0 9150
michael@0 9151 if (pseudoType == nsCSSAnonBoxes::table &&
michael@0 9152 parentStyle->StyleDisplay()->mDisplay == NS_STYLE_DISPLAY_INLINE) {
michael@0 9153 pseudoType = nsCSSAnonBoxes::inlineTable;
michael@0 9154 }
michael@0 9155
michael@0 9156 already_AddRefed<nsStyleContext> wrapperStyle =
michael@0 9157 mPresShell->StyleSet()->ResolveAnonymousBoxStyle(pseudoType, parentStyle);
michael@0 9158 FrameConstructionItem* newItem =
michael@0 9159 new FrameConstructionItem(&pseudoData.mFCData,
michael@0 9160 // Use the content of our parent frame
michael@0 9161 parentContent,
michael@0 9162 // Lie about the tag; it doesn't matter anyway
michael@0 9163 pseudoType,
michael@0 9164 // The namespace does matter, however; it needs
michael@0 9165 // to match that of our first child item to
michael@0 9166 // match the old behavior
michael@0 9167 iter.item().mNameSpaceID,
michael@0 9168 // no pending binding
michael@0 9169 nullptr,
michael@0 9170 wrapperStyle,
michael@0 9171 true, nullptr);
michael@0 9172
michael@0 9173 // Here we're cheating a tad... technically, table-internal items should be
michael@0 9174 // inline if aParentFrame is inline, but they'll get wrapped in an
michael@0 9175 // inline-table in the end, so it'll all work out. In any case, arguably
michael@0 9176 // we don't need to maintain this state at this point... but it's better
michael@0 9177 // to, I guess.
michael@0 9178 newItem->mIsAllInline = newItem->mHasInlineEnds =
michael@0 9179 newItem->mStyleContext->StyleDisplay()->IsInlineOutsideStyle();
michael@0 9180
michael@0 9181 // Table pseudo frames always induce line boundaries around their
michael@0 9182 // contents.
michael@0 9183 newItem->mChildItems.SetLineBoundaryAtStart(true);
michael@0 9184 newItem->mChildItems.SetLineBoundaryAtEnd(true);
michael@0 9185 // The parent of the items in aItems is also the parent of the items
michael@0 9186 // in mChildItems
michael@0 9187 newItem->mChildItems.SetParentHasNoXBLChildren(
michael@0 9188 aItems.ParentHasNoXBLChildren());
michael@0 9189
michael@0 9190 // Eat up all items between |iter| and |endIter| and put them in our wrapper
michael@0 9191 // Advances |iter| to point to |endIter|.
michael@0 9192 iter.AppendItemsToList(endIter, newItem->mChildItems);
michael@0 9193
michael@0 9194 iter.InsertItem(newItem);
michael@0 9195
michael@0 9196 // Now |iter| points to the item that was the first one we didn't wrap;
michael@0 9197 // loop and see whether we need to skip it or wrap it in something
michael@0 9198 // different.
michael@0 9199 } while (!iter.IsDone());
michael@0 9200 }
michael@0 9201
michael@0 9202 inline void
michael@0 9203 nsCSSFrameConstructor::ConstructFramesFromItemList(nsFrameConstructorState& aState,
michael@0 9204 FrameConstructionItemList& aItems,
michael@0 9205 nsIFrame* aParentFrame,
michael@0 9206 nsFrameItems& aFrameItems)
michael@0 9207 {
michael@0 9208 CreateNeededTablePseudos(aState, aItems, aParentFrame);
michael@0 9209 CreateNeededAnonFlexItems(aState, aItems, aParentFrame);
michael@0 9210
michael@0 9211 aItems.SetTriedConstructingFrames();
michael@0 9212 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
michael@0 9213 NS_ASSERTION(iter.item().DesiredParentType() == GetParentType(aParentFrame),
michael@0 9214 "Needed pseudos didn't get created; expect bad things");
michael@0 9215 ConstructFramesFromItem(aState, iter, aParentFrame, aFrameItems);
michael@0 9216 }
michael@0 9217
michael@0 9218 NS_ASSERTION(!aState.mHavePendingPopupgroup,
michael@0 9219 "Should have proccessed it by now");
michael@0 9220 }
michael@0 9221
michael@0 9222 void
michael@0 9223 nsCSSFrameConstructor::AddFCItemsForAnonymousContent(
michael@0 9224 nsFrameConstructorState& aState,
michael@0 9225 nsIFrame* aFrame,
michael@0 9226 nsTArray<nsIAnonymousContentCreator::ContentInfo>& aAnonymousItems,
michael@0 9227 FrameConstructionItemList& aItemsToConstruct,
michael@0 9228 uint32_t aExtraFlags)
michael@0 9229 {
michael@0 9230 for (uint32_t i = 0; i < aAnonymousItems.Length(); ++i) {
michael@0 9231 nsIContent* content = aAnonymousItems[i].mContent;
michael@0 9232 #ifdef DEBUG
michael@0 9233 nsIAnonymousContentCreator* creator = do_QueryFrame(aFrame);
michael@0 9234 NS_ASSERTION(!creator || !creator->CreateFrameFor(content),
michael@0 9235 "If you need to use CreateFrameFor, you need to call "
michael@0 9236 "CreateAnonymousFrames manually and not follow the standard "
michael@0 9237 "ProcessChildren() codepath for this frame");
michael@0 9238 #endif
michael@0 9239 // Assert some things about this content
michael@0 9240 NS_ABORT_IF_FALSE(!(content->GetFlags() &
michael@0 9241 (NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME)),
michael@0 9242 "Should not be marked as needing frames");
michael@0 9243 NS_ABORT_IF_FALSE(!content->IsElement() ||
michael@0 9244 !(content->GetFlags() & ELEMENT_ALL_RESTYLE_FLAGS),
michael@0 9245 "Should have no pending restyle flags");
michael@0 9246 NS_ABORT_IF_FALSE(!content->GetPrimaryFrame(),
michael@0 9247 "Should have no existing frame");
michael@0 9248 NS_ABORT_IF_FALSE(!content->IsNodeOfType(nsINode::eCOMMENT) &&
michael@0 9249 !content->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION),
michael@0 9250 "Why is someone creating garbage anonymous content");
michael@0 9251
michael@0 9252 nsRefPtr<nsStyleContext> styleContext;
michael@0 9253 TreeMatchContext::AutoFlexItemStyleFixupSkipper
michael@0 9254 flexItemStyleFixupSkipper(aState.mTreeMatchContext);
michael@0 9255 if (aAnonymousItems[i].mStyleContext) {
michael@0 9256 styleContext = aAnonymousItems[i].mStyleContext.forget();
michael@0 9257 } else {
michael@0 9258 styleContext = ResolveStyleContext(aFrame, content, &aState);
michael@0 9259 }
michael@0 9260
michael@0 9261 nsTArray<nsIAnonymousContentCreator::ContentInfo>* anonChildren = nullptr;
michael@0 9262 if (!aAnonymousItems[i].mChildren.IsEmpty()) {
michael@0 9263 anonChildren = &aAnonymousItems[i].mChildren;
michael@0 9264 }
michael@0 9265
michael@0 9266 uint32_t flags = ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK |
michael@0 9267 ITEM_IS_ANONYMOUSCONTENTCREATOR_CONTENT | aExtraFlags;
michael@0 9268
michael@0 9269 AddFrameConstructionItemsInternal(aState, content, aFrame,
michael@0 9270 content->Tag(), content->GetNameSpaceID(),
michael@0 9271 true, styleContext, flags,
michael@0 9272 anonChildren, aItemsToConstruct);
michael@0 9273 }
michael@0 9274 }
michael@0 9275
michael@0 9276 void
michael@0 9277 nsCSSFrameConstructor::ProcessChildren(nsFrameConstructorState& aState,
michael@0 9278 nsIContent* aContent,
michael@0 9279 nsStyleContext* aStyleContext,
michael@0 9280 nsIFrame* aFrame,
michael@0 9281 const bool aCanHaveGeneratedContent,
michael@0 9282 nsFrameItems& aFrameItems,
michael@0 9283 const bool aAllowBlockStyles,
michael@0 9284 PendingBinding* aPendingBinding,
michael@0 9285 nsIFrame* aPossiblyLeafFrame)
michael@0 9286 {
michael@0 9287 NS_PRECONDITION(aFrame, "Must have parent frame here");
michael@0 9288 NS_PRECONDITION(aFrame->GetContentInsertionFrame() == aFrame,
michael@0 9289 "Parent frame in ProcessChildren should be its own "
michael@0 9290 "content insertion frame");
michael@0 9291 const uint32_t kMaxDepth = 2 * MAX_REFLOW_DEPTH;
michael@0 9292 static_assert(kMaxDepth <= UINT16_MAX, "mCurrentDepth type is too narrow");
michael@0 9293 AutoRestore<uint16_t> savedDepth(mCurrentDepth);
michael@0 9294 if (mCurrentDepth != UINT16_MAX) {
michael@0 9295 ++mCurrentDepth;
michael@0 9296 }
michael@0 9297
michael@0 9298 if (!aPossiblyLeafFrame) {
michael@0 9299 aPossiblyLeafFrame = aFrame;
michael@0 9300 }
michael@0 9301
michael@0 9302 // XXXbz ideally, this would do all the pushing of various
michael@0 9303 // containing blocks as needed, so callers don't have to do it...
michael@0 9304
michael@0 9305 bool haveFirstLetterStyle = false, haveFirstLineStyle = false;
michael@0 9306 if (aAllowBlockStyles) {
michael@0 9307 ShouldHaveSpecialBlockStyle(aContent, aStyleContext, &haveFirstLetterStyle,
michael@0 9308 &haveFirstLineStyle);
michael@0 9309 }
michael@0 9310
michael@0 9311 // The logic here needs to match the logic in GetFloatContainingBlock()
michael@0 9312 nsFrameConstructorSaveState floatSaveState;
michael@0 9313 if (ShouldSuppressFloatingOfDescendants(aFrame)) {
michael@0 9314 aState.PushFloatContainingBlock(nullptr, floatSaveState);
michael@0 9315 } else if (aFrame->IsFloatContainingBlock()) {
michael@0 9316 aState.PushFloatContainingBlock(aFrame, floatSaveState);
michael@0 9317 }
michael@0 9318
michael@0 9319 nsFrameConstructorState::PendingBindingAutoPusher pusher(aState,
michael@0 9320 aPendingBinding);
michael@0 9321
michael@0 9322 FrameConstructionItemList itemsToConstruct;
michael@0 9323
michael@0 9324 // If we have first-letter or first-line style then frames can get
michael@0 9325 // moved around so don't set these flags.
michael@0 9326 if (aAllowBlockStyles && !haveFirstLetterStyle && !haveFirstLineStyle) {
michael@0 9327 itemsToConstruct.SetLineBoundaryAtStart(true);
michael@0 9328 itemsToConstruct.SetLineBoundaryAtEnd(true);
michael@0 9329 }
michael@0 9330
michael@0 9331 // Create any anonymous frames we need here. This must happen before the
michael@0 9332 // non-anonymous children are processed to ensure that popups are never
michael@0 9333 // constructed before the popupset.
michael@0 9334 nsAutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> anonymousItems;
michael@0 9335 GetAnonymousContent(aContent, aPossiblyLeafFrame, anonymousItems);
michael@0 9336 #ifdef DEBUG
michael@0 9337 for (uint32_t i = 0; i < anonymousItems.Length(); ++i) {
michael@0 9338 NS_ABORT_IF_FALSE(anonymousItems[i].mContent->IsRootOfAnonymousSubtree(),
michael@0 9339 "Content should know it's an anonymous subtree");
michael@0 9340 }
michael@0 9341 #endif
michael@0 9342 AddFCItemsForAnonymousContent(aState, aFrame, anonymousItems,
michael@0 9343 itemsToConstruct);
michael@0 9344
michael@0 9345 if (!aPossiblyLeafFrame->IsLeaf()) {
michael@0 9346 // :before/:after content should have the same style context parent
michael@0 9347 // as normal kids.
michael@0 9348 // Note that we don't use this style context for looking up things like
michael@0 9349 // special block styles because in some cases involving table pseudo-frames
michael@0 9350 // it has nothing to do with the parent frame's desired behavior.
michael@0 9351 nsStyleContext* styleContext;
michael@0 9352
michael@0 9353 if (aCanHaveGeneratedContent) {
michael@0 9354 aFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT);
michael@0 9355 styleContext =
michael@0 9356 nsFrame::CorrectStyleParentFrame(aFrame, nullptr)->StyleContext();
michael@0 9357 // Probe for generated content before
michael@0 9358 CreateGeneratedContentItem(aState, aFrame, aContent, styleContext,
michael@0 9359 nsCSSPseudoElements::ePseudo_before,
michael@0 9360 itemsToConstruct);
michael@0 9361 }
michael@0 9362
michael@0 9363 const bool addChildItems = MOZ_LIKELY(mCurrentDepth < kMaxDepth);
michael@0 9364 if (!addChildItems) {
michael@0 9365 NS_WARNING("ProcessChildren max depth exceeded");
michael@0 9366 }
michael@0 9367
michael@0 9368 FlattenedChildIterator iter(aContent);
michael@0 9369 for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
michael@0 9370 // Get the parent of the content and check if it is a XBL children element
michael@0 9371 // (if the content is a children element then parent != aContent because the
michael@0 9372 // FlattenedChildIterator will transitively iterate through <xbl:children>
michael@0 9373 // for default content). Push the children element as an ancestor here because
michael@0 9374 // it does not have a frame and would not otherwise be pushed as an ancestor.
michael@0 9375 nsIContent* parent = child->GetParent();
michael@0 9376 MOZ_ASSERT(parent, "Parent must be non-null because we are iterating children.");
michael@0 9377 TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext);
michael@0 9378 if (parent != aContent && parent->IsElement()) {
michael@0 9379 if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
michael@0 9380 ancestorPusher.PushAncestorAndStyleScope(parent->AsElement());
michael@0 9381 } else {
michael@0 9382 ancestorPusher.PushStyleScope(parent->AsElement());
michael@0 9383 }
michael@0 9384 }
michael@0 9385
michael@0 9386 // Frame construction item construction should not post
michael@0 9387 // restyles, so removing restyle flags here is safe.
michael@0 9388 if (child->IsElement()) {
michael@0 9389 child->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
michael@0 9390 }
michael@0 9391 if (addChildItems) {
michael@0 9392 AddFrameConstructionItems(aState, child, iter.XBLInvolved(), aFrame,
michael@0 9393 itemsToConstruct);
michael@0 9394 } else {
michael@0 9395 ClearLazyBits(child, child->GetNextSibling());
michael@0 9396 }
michael@0 9397 }
michael@0 9398 itemsToConstruct.SetParentHasNoXBLChildren(!iter.XBLInvolved());
michael@0 9399
michael@0 9400 if (aCanHaveGeneratedContent) {
michael@0 9401 // Probe for generated content after
michael@0 9402 CreateGeneratedContentItem(aState, aFrame, aContent, styleContext,
michael@0 9403 nsCSSPseudoElements::ePseudo_after,
michael@0 9404 itemsToConstruct);
michael@0 9405 }
michael@0 9406 } else {
michael@0 9407 ClearLazyBits(aContent->GetFirstChild(), nullptr);
michael@0 9408 }
michael@0 9409
michael@0 9410 ConstructFramesFromItemList(aState, itemsToConstruct, aFrame, aFrameItems);
michael@0 9411
michael@0 9412 NS_ASSERTION(!aAllowBlockStyles || !aFrame->IsBoxFrame(),
michael@0 9413 "can't be both block and box");
michael@0 9414
michael@0 9415 if (haveFirstLetterStyle) {
michael@0 9416 WrapFramesInFirstLetterFrame(aContent, aFrame, aFrameItems);
michael@0 9417 }
michael@0 9418 if (haveFirstLineStyle) {
michael@0 9419 WrapFramesInFirstLineFrame(aState, aContent, aFrame, nullptr,
michael@0 9420 aFrameItems);
michael@0 9421 }
michael@0 9422
michael@0 9423 // We might end up with first-line frames that change
michael@0 9424 // AnyKidsNeedBlockParent() without changing itemsToConstruct, but that
michael@0 9425 // should never happen for cases whan aFrame->IsBoxFrame().
michael@0 9426 NS_ASSERTION(!haveFirstLineStyle || !aFrame->IsBoxFrame(),
michael@0 9427 "Shouldn't have first-line style if we're a box");
michael@0 9428 NS_ASSERTION(!aFrame->IsBoxFrame() ||
michael@0 9429 itemsToConstruct.AnyItemsNeedBlockParent() ==
michael@0 9430 (AnyKidsNeedBlockParent(aFrameItems.FirstChild()) != nullptr),
michael@0 9431 "Something went awry in our block parent calculations");
michael@0 9432
michael@0 9433 if (aFrame->IsBoxFrame() && itemsToConstruct.AnyItemsNeedBlockParent()) {
michael@0 9434 // XXXbz we could do this on the FrameConstructionItemList level,
michael@0 9435 // no? And if we cared we could look through the item list
michael@0 9436 // instead of groveling through the framelist here..
michael@0 9437 nsStyleContext *frameStyleContext = aFrame->StyleContext();
michael@0 9438 // Report a warning for non-GC frames, for chrome:
michael@0 9439 if (!aFrame->IsGeneratedContentFrame() &&
michael@0 9440 mPresShell->GetPresContext()->IsChrome()) {
michael@0 9441 nsIContent *badKid = AnyKidsNeedBlockParent(aFrameItems.FirstChild());
michael@0 9442 nsDependentAtomString parentTag(aContent->Tag()), kidTag(badKid->Tag());
michael@0 9443 const char16_t* params[] = { parentTag.get(), kidTag.get() };
michael@0 9444 const nsStyleDisplay *display = frameStyleContext->StyleDisplay();
michael@0 9445 const char *message =
michael@0 9446 (display->mDisplay == NS_STYLE_DISPLAY_INLINE_BOX)
michael@0 9447 ? "NeededToWrapXULInlineBox" : "NeededToWrapXUL";
michael@0 9448 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
michael@0 9449 NS_LITERAL_CSTRING("FrameConstructor"),
michael@0 9450 mDocument,
michael@0 9451 nsContentUtils::eXUL_PROPERTIES,
michael@0 9452 message,
michael@0 9453 params, ArrayLength(params));
michael@0 9454 }
michael@0 9455
michael@0 9456 nsRefPtr<nsStyleContext> blockSC = mPresShell->StyleSet()->
michael@0 9457 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::mozXULAnonymousBlock,
michael@0 9458 frameStyleContext);
michael@0 9459 nsIFrame *blockFrame = NS_NewBlockFrame(mPresShell, blockSC);
michael@0 9460 // We might, in theory, want to set NS_BLOCK_FLOAT_MGR and
michael@0 9461 // NS_BLOCK_MARGIN_ROOT, but I think it's a bad idea given that
michael@0 9462 // a real block placed here wouldn't get those set on it.
michael@0 9463
michael@0 9464 InitAndRestoreFrame(aState, aContent, aFrame, blockFrame, false);
michael@0 9465
michael@0 9466 NS_ASSERTION(!blockFrame->HasView(), "need to do view reparenting");
michael@0 9467 ReparentFrames(this, blockFrame, aFrameItems);
michael@0 9468
michael@0 9469 blockFrame->SetInitialChildList(kPrincipalList, aFrameItems);
michael@0 9470 NS_ASSERTION(aFrameItems.IsEmpty(), "How did that happen?");
michael@0 9471 aFrameItems.Clear();
michael@0 9472 aFrameItems.AddChild(blockFrame);
michael@0 9473
michael@0 9474 aFrame->AddStateBits(NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK);
michael@0 9475 }
michael@0 9476 }
michael@0 9477
michael@0 9478 //----------------------------------------------------------------------
michael@0 9479
michael@0 9480 // Support for :first-line style
michael@0 9481
michael@0 9482 // Special routine to handle placing a list of frames into a block
michael@0 9483 // frame that has first-line style. The routine ensures that the first
michael@0 9484 // collection of inline frames end up in a first-line frame.
michael@0 9485 // NOTE: aState may have containing block information related to a
michael@0 9486 // different part of the frame tree than where the first line occurs.
michael@0 9487 // In particular aState may be set up for where ContentInserted or
michael@0 9488 // ContentAppended is inserting content, which may be some
michael@0 9489 // non-first-in-flow continuation of the block to which the first-line
michael@0 9490 // belongs. So this function needs to be careful about how it uses
michael@0 9491 // aState.
michael@0 9492 void
michael@0 9493 nsCSSFrameConstructor::WrapFramesInFirstLineFrame(
michael@0 9494 nsFrameConstructorState& aState,
michael@0 9495 nsIContent* aBlockContent,
michael@0 9496 nsIFrame* aBlockFrame,
michael@0 9497 nsIFrame* aLineFrame,
michael@0 9498 nsFrameItems& aFrameItems)
michael@0 9499 {
michael@0 9500 // Find the part of aFrameItems that we want to put in the first-line
michael@0 9501 nsFrameList::FrameLinkEnumerator link(aFrameItems);
michael@0 9502 while (!link.AtEnd() && link.NextFrame()->IsInlineOutside()) {
michael@0 9503 link.Next();
michael@0 9504 }
michael@0 9505
michael@0 9506 nsFrameList firstLineChildren = aFrameItems.ExtractHead(link);
michael@0 9507
michael@0 9508 if (firstLineChildren.IsEmpty()) {
michael@0 9509 // Nothing is supposed to go into the first-line; nothing to do
michael@0 9510 return;
michael@0 9511 }
michael@0 9512
michael@0 9513 if (!aLineFrame) {
michael@0 9514 // Create line frame
michael@0 9515 nsStyleContext* parentStyle =
michael@0 9516 nsFrame::CorrectStyleParentFrame(aBlockFrame,
michael@0 9517 nsCSSPseudoElements::firstLine)->
michael@0 9518 StyleContext();
michael@0 9519 nsRefPtr<nsStyleContext> firstLineStyle = GetFirstLineStyle(aBlockContent,
michael@0 9520 parentStyle);
michael@0 9521
michael@0 9522 aLineFrame = NS_NewFirstLineFrame(mPresShell, firstLineStyle);
michael@0 9523
michael@0 9524 // Initialize the line frame
michael@0 9525 InitAndRestoreFrame(aState, aBlockContent, aBlockFrame, aLineFrame);
michael@0 9526
michael@0 9527 // The lineFrame will be the block's first child; the rest of the
michael@0 9528 // frame list (after lastInlineFrame) will be the second and
michael@0 9529 // subsequent children; insert lineFrame into aFrameItems.
michael@0 9530 aFrameItems.InsertFrame(nullptr, nullptr, aLineFrame);
michael@0 9531
michael@0 9532 NS_ASSERTION(aLineFrame->StyleContext() == firstLineStyle,
michael@0 9533 "Bogus style context on line frame");
michael@0 9534 }
michael@0 9535
michael@0 9536 // Give the inline frames to the lineFrame <b>after</b> reparenting them
michael@0 9537 ReparentFrames(this, aLineFrame, firstLineChildren);
michael@0 9538 if (aLineFrame->PrincipalChildList().IsEmpty() &&
michael@0 9539 (aLineFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
michael@0 9540 aLineFrame->SetInitialChildList(kPrincipalList, firstLineChildren);
michael@0 9541 } else {
michael@0 9542 AppendFrames(aLineFrame, kPrincipalList, firstLineChildren);
michael@0 9543 }
michael@0 9544 }
michael@0 9545
michael@0 9546 // Special routine to handle appending a new frame to a block frame's
michael@0 9547 // child list. Takes care of placing the new frame into the right
michael@0 9548 // place when first-line style is present.
michael@0 9549 void
michael@0 9550 nsCSSFrameConstructor::AppendFirstLineFrames(
michael@0 9551 nsFrameConstructorState& aState,
michael@0 9552 nsIContent* aBlockContent,
michael@0 9553 nsIFrame* aBlockFrame,
michael@0 9554 nsFrameItems& aFrameItems)
michael@0 9555 {
michael@0 9556 // It's possible that aBlockFrame needs to have a first-line frame
michael@0 9557 // created because it doesn't currently have any children.
michael@0 9558 const nsFrameList& blockKids = aBlockFrame->PrincipalChildList();
michael@0 9559 if (blockKids.IsEmpty()) {
michael@0 9560 WrapFramesInFirstLineFrame(aState, aBlockContent,
michael@0 9561 aBlockFrame, nullptr, aFrameItems);
michael@0 9562 return;
michael@0 9563 }
michael@0 9564
michael@0 9565 // Examine the last block child - if it's a first-line frame then
michael@0 9566 // appended frames need special treatment.
michael@0 9567 nsIFrame* lastBlockKid = blockKids.LastChild();
michael@0 9568 if (lastBlockKid->GetType() != nsGkAtoms::lineFrame) {
michael@0 9569 // No first-line frame at the end of the list, therefore there is
michael@0 9570 // an intervening block between any first-line frame the frames
michael@0 9571 // we are appending. Therefore, we don't need any special
michael@0 9572 // treatment of the appended frames.
michael@0 9573 return;
michael@0 9574 }
michael@0 9575
michael@0 9576 WrapFramesInFirstLineFrame(aState, aBlockContent, aBlockFrame,
michael@0 9577 lastBlockKid, aFrameItems);
michael@0 9578 }
michael@0 9579
michael@0 9580 // Special routine to handle inserting a new frame into a block
michael@0 9581 // frame's child list. Takes care of placing the new frame into the
michael@0 9582 // right place when first-line style is present.
michael@0 9583 nsresult
michael@0 9584 nsCSSFrameConstructor::InsertFirstLineFrames(
michael@0 9585 nsFrameConstructorState& aState,
michael@0 9586 nsIContent* aContent,
michael@0 9587 nsIFrame* aBlockFrame,
michael@0 9588 nsIFrame** aParentFrame,
michael@0 9589 nsIFrame* aPrevSibling,
michael@0 9590 nsFrameItems& aFrameItems)
michael@0 9591 {
michael@0 9592 nsresult rv = NS_OK;
michael@0 9593 // XXXbz If you make this method actually do something, check to
michael@0 9594 // make sure that the caller is passing what you expect. In
michael@0 9595 // particular, which content is aContent? And audit the rest of
michael@0 9596 // this code too; it makes bogus assumptions and may not build.
michael@0 9597 #if 0
michael@0 9598 nsIFrame* parentFrame = *aParentFrame;
michael@0 9599 nsIFrame* newFrame = aFrameItems.childList;
michael@0 9600 bool isInline = IsInlineOutside(newFrame);
michael@0 9601
michael@0 9602 if (!aPrevSibling) {
michael@0 9603 // Insertion will become the first frame. Two cases: we either
michael@0 9604 // already have a first-line frame or we don't.
michael@0 9605 nsIFrame* firstBlockKid = aBlockFrame->GetFirstPrincipalChild();
michael@0 9606 if (firstBlockKid->GetType() == nsGkAtoms::lineFrame) {
michael@0 9607 // We already have a first-line frame
michael@0 9608 nsIFrame* lineFrame = firstBlockKid;
michael@0 9609
michael@0 9610 if (isInline) {
michael@0 9611 // Easy case: the new inline frame will go into the lineFrame.
michael@0 9612 ReparentFrame(this, lineFrame, newFrame);
michael@0 9613 InsertFrames(lineFrame, kPrincipalList, nullptr, newFrame);
michael@0 9614
michael@0 9615 // Since the frame is going into the lineFrame, don't let it
michael@0 9616 // go into the block too.
michael@0 9617 aFrameItems.childList = nullptr;
michael@0 9618 aFrameItems.lastChild = nullptr;
michael@0 9619 }
michael@0 9620 else {
michael@0 9621 // Harder case: We are about to insert a block level element
michael@0 9622 // before the first-line frame.
michael@0 9623 // XXX need a method to steal away frames from the line-frame
michael@0 9624 }
michael@0 9625 }
michael@0 9626 else {
michael@0 9627 // We do not have a first-line frame
michael@0 9628 if (isInline) {
michael@0 9629 // We now need a first-line frame to contain the inline frame.
michael@0 9630 nsIFrame* lineFrame = NS_NewFirstLineFrame(firstLineStyle);
michael@0 9631
michael@0 9632 if (NS_SUCCEEDED(rv)) {
michael@0 9633 // Lookup first-line style context
michael@0 9634 nsStyleContext* parentStyle =
michael@0 9635 nsFrame::CorrectStyleParentFrame(aBlockFrame,
michael@0 9636 nsCSSPseudoElements::firstLine)->
michael@0 9637 StyleContext();
michael@0 9638 nsRefPtr<nsStyleContext> firstLineStyle =
michael@0 9639 GetFirstLineStyle(aContent, parentStyle);
michael@0 9640
michael@0 9641 // Initialize the line frame
michael@0 9642 InitAndRestoreFrame(aState, aContent, aBlockFrame, lineFrame);
michael@0 9643
michael@0 9644 // Make sure the caller inserts the lineFrame into the
michael@0 9645 // blocks list of children.
michael@0 9646 aFrameItems.childList = lineFrame;
michael@0 9647 aFrameItems.lastChild = lineFrame;
michael@0 9648
michael@0 9649 // Give the inline frames to the lineFrame <b>after</b>
michael@0 9650 // reparenting them
michael@0 9651 NS_ASSERTION(lineFrame->StyleContext() == firstLineStyle,
michael@0 9652 "Bogus style context on line frame");
michael@0 9653 ReparentFrame(aPresContext, lineFrame, newFrame);
michael@0 9654 lineFrame->SetInitialChildList(kPrincipalList, newFrame);
michael@0 9655 }
michael@0 9656 }
michael@0 9657 else {
michael@0 9658 // Easy case: the regular insertion logic can insert the new
michael@0 9659 // frame because it's a block frame.
michael@0 9660 }
michael@0 9661 }
michael@0 9662 }
michael@0 9663 else {
michael@0 9664 // Insertion will not be the first frame.
michael@0 9665 nsIFrame* prevSiblingParent = aPrevSibling->GetParent();
michael@0 9666 if (prevSiblingParent == aBlockFrame) {
michael@0 9667 // Easy case: The prev-siblings parent is the block
michael@0 9668 // frame. Therefore the prev-sibling is not currently in a
michael@0 9669 // line-frame. Therefore the new frame which is going after it,
michael@0 9670 // regardless of type, is not going into a line-frame.
michael@0 9671 }
michael@0 9672 else {
michael@0 9673 // If the prevSiblingParent is not the block-frame then it must
michael@0 9674 // be a line-frame (if it were a letter-frame, that logic would
michael@0 9675 // already have adjusted the prev-sibling to be the
michael@0 9676 // letter-frame).
michael@0 9677 if (isInline) {
michael@0 9678 // Easy case: the insertion can go where the caller thinks it
michael@0 9679 // should go (which is into prevSiblingParent).
michael@0 9680 }
michael@0 9681 else {
michael@0 9682 // Block elements don't end up in line-frames, therefore
michael@0 9683 // change the insertion point to aBlockFrame. However, there
michael@0 9684 // might be more inline elements following aPrevSibling that
michael@0 9685 // need to be pulled out of the line-frame and become children
michael@0 9686 // of the block.
michael@0 9687 nsIFrame* nextSibling = aPrevSibling->GetNextSibling();
michael@0 9688 nsIFrame* nextLineFrame = prevSiblingParent->GetNextInFlow();
michael@0 9689 if (nextSibling || nextLineFrame) {
michael@0 9690 // Oy. We have work to do. Create a list of the new frames
michael@0 9691 // that are going into the block by stripping them away from
michael@0 9692 // the line-frame(s).
michael@0 9693 if (nextSibling) {
michael@0 9694 nsLineFrame* lineFrame = (nsLineFrame*) prevSiblingParent;
michael@0 9695 nsFrameList tail = lineFrame->StealFramesAfter(aPrevSibling);
michael@0 9696 // XXX do something with 'tail'
michael@0 9697 }
michael@0 9698
michael@0 9699 nsLineFrame* nextLineFrame = (nsLineFrame*) lineFrame;
michael@0 9700 for (;;) {
michael@0 9701 nextLineFrame = nextLineFrame->GetNextInFlow();
michael@0 9702 if (!nextLineFrame) {
michael@0 9703 break;
michael@0 9704 }
michael@0 9705 nsIFrame* kids = nextLineFrame->GetFirstPrincipalChild();
michael@0 9706 }
michael@0 9707 }
michael@0 9708 else {
michael@0 9709 // We got lucky: aPrevSibling was the last inline frame in
michael@0 9710 // the line-frame.
michael@0 9711 ReparentFrame(this, aBlockFrame, newFrame);
michael@0 9712 InsertFrames(aBlockFrame, kPrincipalList,
michael@0 9713 prevSiblingParent, newFrame);
michael@0 9714 aFrameItems.childList = nullptr;
michael@0 9715 aFrameItems.lastChild = nullptr;
michael@0 9716 }
michael@0 9717 }
michael@0 9718 }
michael@0 9719 }
michael@0 9720
michael@0 9721 #endif
michael@0 9722 return rv;
michael@0 9723 }
michael@0 9724
michael@0 9725 //----------------------------------------------------------------------
michael@0 9726
michael@0 9727 // First-letter support
michael@0 9728
michael@0 9729 // Determine how many characters in the text fragment apply to the
michael@0 9730 // first letter
michael@0 9731 static int32_t
michael@0 9732 FirstLetterCount(const nsTextFragment* aFragment)
michael@0 9733 {
michael@0 9734 int32_t count = 0;
michael@0 9735 int32_t firstLetterLength = 0;
michael@0 9736
michael@0 9737 int32_t i, n = aFragment->GetLength();
michael@0 9738 for (i = 0; i < n; i++) {
michael@0 9739 char16_t ch = aFragment->CharAt(i);
michael@0 9740 // FIXME: take content language into account when deciding whitespace.
michael@0 9741 if (dom::IsSpaceCharacter(ch)) {
michael@0 9742 if (firstLetterLength) {
michael@0 9743 break;
michael@0 9744 }
michael@0 9745 count++;
michael@0 9746 continue;
michael@0 9747 }
michael@0 9748 // XXX I18n
michael@0 9749 if ((ch == '\'') || (ch == '\"')) {
michael@0 9750 if (firstLetterLength) {
michael@0 9751 break;
michael@0 9752 }
michael@0 9753 // keep looping
michael@0 9754 firstLetterLength = 1;
michael@0 9755 }
michael@0 9756 else {
michael@0 9757 count++;
michael@0 9758 break;
michael@0 9759 }
michael@0 9760 }
michael@0 9761
michael@0 9762 return count;
michael@0 9763 }
michael@0 9764
michael@0 9765 static bool
michael@0 9766 NeedFirstLetterContinuation(nsIContent* aContent)
michael@0 9767 {
michael@0 9768 NS_PRECONDITION(aContent, "null ptr");
michael@0 9769
michael@0 9770 bool result = false;
michael@0 9771 if (aContent) {
michael@0 9772 const nsTextFragment* frag = aContent->GetText();
michael@0 9773 if (frag) {
michael@0 9774 int32_t flc = FirstLetterCount(frag);
michael@0 9775 int32_t tl = frag->GetLength();
michael@0 9776 if (flc < tl) {
michael@0 9777 result = true;
michael@0 9778 }
michael@0 9779 }
michael@0 9780 }
michael@0 9781 return result;
michael@0 9782 }
michael@0 9783
michael@0 9784 static bool IsFirstLetterContent(nsIContent* aContent)
michael@0 9785 {
michael@0 9786 return aContent->TextLength() &&
michael@0 9787 !aContent->TextIsOnlyWhitespace();
michael@0 9788 }
michael@0 9789
michael@0 9790 /**
michael@0 9791 * Create a letter frame, only make it a floating frame.
michael@0 9792 */
michael@0 9793 void
michael@0 9794 nsCSSFrameConstructor::CreateFloatingLetterFrame(
michael@0 9795 nsFrameConstructorState& aState,
michael@0 9796 nsIFrame* aBlockFrame,
michael@0 9797 nsIContent* aTextContent,
michael@0 9798 nsIFrame* aTextFrame,
michael@0 9799 nsIContent* aBlockContent,
michael@0 9800 nsIFrame* aParentFrame,
michael@0 9801 nsStyleContext* aStyleContext,
michael@0 9802 nsFrameItems& aResult)
michael@0 9803 {
michael@0 9804 // Create the first-letter-frame
michael@0 9805 nsIFrame* letterFrame;
michael@0 9806 nsStyleSet *styleSet = mPresShell->StyleSet();
michael@0 9807
michael@0 9808 letterFrame = NS_NewFirstLetterFrame(mPresShell, aStyleContext);
michael@0 9809 // We don't want to use a text content for a non-text frame (because we want
michael@0 9810 // its primary frame to be a text frame). So use its parent for the
michael@0 9811 // first-letter.
michael@0 9812 nsIContent* letterContent = aTextContent->GetParent();
michael@0 9813 nsIFrame* containingBlock = aState.GetGeometricParent(
michael@0 9814 aStyleContext->StyleDisplay(), aParentFrame);
michael@0 9815 InitAndRestoreFrame(aState, letterContent, containingBlock, letterFrame);
michael@0 9816
michael@0 9817 // Init the text frame to refer to the letter frame. Make sure we
michael@0 9818 // get a proper style context for it (the one passed in is for the
michael@0 9819 // letter frame and will have the float property set on it; the text
michael@0 9820 // frame shouldn't have that set).
michael@0 9821 nsRefPtr<nsStyleContext> textSC;
michael@0 9822 textSC = styleSet->ResolveStyleForNonElement(aStyleContext);
michael@0 9823 aTextFrame->SetStyleContextWithoutNotification(textSC);
michael@0 9824 InitAndRestoreFrame(aState, aTextContent, letterFrame, aTextFrame);
michael@0 9825
michael@0 9826 // And then give the text frame to the letter frame
michael@0 9827 SetInitialSingleChild(letterFrame, aTextFrame);
michael@0 9828
michael@0 9829 // See if we will need to continue the text frame (does it contain
michael@0 9830 // more than just the first-letter text or not?) If it does, then we
michael@0 9831 // create (in advance) a continuation frame for it.
michael@0 9832 nsIFrame* nextTextFrame = nullptr;
michael@0 9833 if (NeedFirstLetterContinuation(aTextContent)) {
michael@0 9834 // Create continuation
michael@0 9835 nextTextFrame =
michael@0 9836 CreateContinuingFrame(aState.mPresContext, aTextFrame, aParentFrame);
michael@0 9837 // Repair the continuations style context
michael@0 9838 nsStyleContext* parentStyleContext = aStyleContext->GetParent();
michael@0 9839 if (parentStyleContext) {
michael@0 9840 nsRefPtr<nsStyleContext> newSC;
michael@0 9841 newSC = styleSet->ResolveStyleForNonElement(parentStyleContext);
michael@0 9842 nextTextFrame->SetStyleContext(newSC);
michael@0 9843 }
michael@0 9844 }
michael@0 9845
michael@0 9846 NS_ASSERTION(aResult.IsEmpty(), "aResult should be an empty nsFrameItems!");
michael@0 9847 // Put the new float before any of the floats in the block we're doing
michael@0 9848 // first-letter for, that is, before any floats whose parent is
michael@0 9849 // containingBlock.
michael@0 9850 nsFrameList::FrameLinkEnumerator link(aState.mFloatedItems);
michael@0 9851 while (!link.AtEnd() && link.NextFrame()->GetParent() != containingBlock) {
michael@0 9852 link.Next();
michael@0 9853 }
michael@0 9854
michael@0 9855 aState.AddChild(letterFrame, aResult, letterContent, aStyleContext,
michael@0 9856 aParentFrame, false, true, false, true,
michael@0 9857 link.PrevFrame());
michael@0 9858
michael@0 9859 if (nextTextFrame) {
michael@0 9860 aResult.AddChild(nextTextFrame);
michael@0 9861 }
michael@0 9862 }
michael@0 9863
michael@0 9864 /**
michael@0 9865 * Create a new letter frame for aTextFrame. The letter frame will be
michael@0 9866 * a child of aParentFrame.
michael@0 9867 */
michael@0 9868 void
michael@0 9869 nsCSSFrameConstructor::CreateLetterFrame(nsIFrame* aBlockFrame,
michael@0 9870 nsIFrame* aBlockContinuation,
michael@0 9871 nsIContent* aTextContent,
michael@0 9872 nsIFrame* aParentFrame,
michael@0 9873 nsFrameItems& aResult)
michael@0 9874 {
michael@0 9875 NS_PRECONDITION(aTextContent->IsNodeOfType(nsINode::eTEXT),
michael@0 9876 "aTextContent isn't text");
michael@0 9877 NS_ASSERTION(nsLayoutUtils::GetAsBlock(aBlockFrame),
michael@0 9878 "Not a block frame?");
michael@0 9879
michael@0 9880 // Get style context for the first-letter-frame
michael@0 9881 nsStyleContext* parentStyleContext =
michael@0 9882 nsFrame::CorrectStyleParentFrame(aParentFrame,
michael@0 9883 nsCSSPseudoElements::firstLetter)->
michael@0 9884 StyleContext();
michael@0 9885
michael@0 9886 // Use content from containing block so that we can actually
michael@0 9887 // find a matching style rule.
michael@0 9888 nsIContent* blockContent = aBlockFrame->GetContent();
michael@0 9889
michael@0 9890 // Create first-letter style rule
michael@0 9891 nsRefPtr<nsStyleContext> sc = GetFirstLetterStyle(blockContent,
michael@0 9892 parentStyleContext);
michael@0 9893 if (sc) {
michael@0 9894 nsRefPtr<nsStyleContext> textSC;
michael@0 9895 textSC = mPresShell->StyleSet()->ResolveStyleForNonElement(sc);
michael@0 9896
michael@0 9897 // Create a new text frame (the original one will be discarded)
michael@0 9898 // pass a temporary stylecontext, the correct one will be set
michael@0 9899 // later. Start off by unsetting the primary frame for
michael@0 9900 // aTextContent, so it's no longer pointing to the to-be-destroyed
michael@0 9901 // frame.
michael@0 9902 // XXXbz it would be really nice to destroy the old frame _first_,
michael@0 9903 // then create the new one, so we could avoid this hack.
michael@0 9904 aTextContent->SetPrimaryFrame(nullptr);
michael@0 9905 nsIFrame* textFrame = NS_NewTextFrame(mPresShell, textSC);
michael@0 9906
michael@0 9907 NS_ASSERTION(aBlockContinuation == GetFloatContainingBlock(aParentFrame),
michael@0 9908 "Containing block is confused");
michael@0 9909 nsFrameConstructorState state(mPresShell,
michael@0 9910 GetAbsoluteContainingBlock(aParentFrame, FIXED_POS),
michael@0 9911 GetAbsoluteContainingBlock(aParentFrame, ABS_POS),
michael@0 9912 aBlockContinuation);
michael@0 9913
michael@0 9914 // Create the right type of first-letter frame
michael@0 9915 const nsStyleDisplay* display = sc->StyleDisplay();
michael@0 9916 if (display->IsFloatingStyle() && !aParentFrame->IsSVGText()) {
michael@0 9917 // Make a floating first-letter frame
michael@0 9918 CreateFloatingLetterFrame(state, aBlockFrame, aTextContent, textFrame,
michael@0 9919 blockContent, aParentFrame, sc, aResult);
michael@0 9920 }
michael@0 9921 else {
michael@0 9922 // Make an inflow first-letter frame
michael@0 9923 nsIFrame* letterFrame = NS_NewFirstLetterFrame(mPresShell, sc);
michael@0 9924
michael@0 9925 // Initialize the first-letter-frame. We don't want to use a text
michael@0 9926 // content for a non-text frame (because we want its primary frame to
michael@0 9927 // be a text frame). So use its parent for the first-letter.
michael@0 9928 nsIContent* letterContent = aTextContent->GetParent();
michael@0 9929 letterFrame->Init(letterContent, aParentFrame, nullptr);
michael@0 9930
michael@0 9931 InitAndRestoreFrame(state, aTextContent, letterFrame, textFrame);
michael@0 9932
michael@0 9933 SetInitialSingleChild(letterFrame, textFrame);
michael@0 9934 aResult.Clear();
michael@0 9935 aResult.AddChild(letterFrame);
michael@0 9936 NS_ASSERTION(!aBlockFrame->GetPrevContinuation(),
michael@0 9937 "should have the first continuation here");
michael@0 9938 aBlockFrame->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD);
michael@0 9939 }
michael@0 9940 aTextContent->SetPrimaryFrame(textFrame);
michael@0 9941 }
michael@0 9942 }
michael@0 9943
michael@0 9944 void
michael@0 9945 nsCSSFrameConstructor::WrapFramesInFirstLetterFrame(
michael@0 9946 nsIContent* aBlockContent,
michael@0 9947 nsIFrame* aBlockFrame,
michael@0 9948 nsFrameItems& aBlockFrames)
michael@0 9949 {
michael@0 9950 aBlockFrame->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE);
michael@0 9951
michael@0 9952 nsIFrame* parentFrame = nullptr;
michael@0 9953 nsIFrame* textFrame = nullptr;
michael@0 9954 nsIFrame* prevFrame = nullptr;
michael@0 9955 nsFrameItems letterFrames;
michael@0 9956 bool stopLooking = false;
michael@0 9957 WrapFramesInFirstLetterFrame(aBlockFrame, aBlockFrame, aBlockFrame,
michael@0 9958 aBlockFrames.FirstChild(),
michael@0 9959 &parentFrame, &textFrame, &prevFrame,
michael@0 9960 letterFrames, &stopLooking);
michael@0 9961 if (parentFrame) {
michael@0 9962 if (parentFrame == aBlockFrame) {
michael@0 9963 // Take textFrame out of the block's frame list and substitute the
michael@0 9964 // letter frame(s) instead.
michael@0 9965 aBlockFrames.DestroyFrame(textFrame);
michael@0 9966 aBlockFrames.InsertFrames(nullptr, prevFrame, letterFrames);
michael@0 9967 }
michael@0 9968 else {
michael@0 9969 // Take the old textFrame out of the inline parent's child list
michael@0 9970 RemoveFrame(kPrincipalList, textFrame);
michael@0 9971
michael@0 9972 // Insert in the letter frame(s)
michael@0 9973 parentFrame->InsertFrames(kPrincipalList, prevFrame, letterFrames);
michael@0 9974 }
michael@0 9975 }
michael@0 9976 }
michael@0 9977
michael@0 9978 void
michael@0 9979 nsCSSFrameConstructor::WrapFramesInFirstLetterFrame(
michael@0 9980 nsIFrame* aBlockFrame,
michael@0 9981 nsIFrame* aBlockContinuation,
michael@0 9982 nsIFrame* aParentFrame,
michael@0 9983 nsIFrame* aParentFrameList,
michael@0 9984 nsIFrame** aModifiedParent,
michael@0 9985 nsIFrame** aTextFrame,
michael@0 9986 nsIFrame** aPrevFrame,
michael@0 9987 nsFrameItems& aLetterFrames,
michael@0 9988 bool* aStopLooking)
michael@0 9989 {
michael@0 9990 nsIFrame* prevFrame = nullptr;
michael@0 9991 nsIFrame* frame = aParentFrameList;
michael@0 9992
michael@0 9993 while (frame) {
michael@0 9994 nsIFrame* nextFrame = frame->GetNextSibling();
michael@0 9995
michael@0 9996 nsIAtom* frameType = frame->GetType();
michael@0 9997 if (nsGkAtoms::textFrame == frameType) {
michael@0 9998 // Wrap up first-letter content in a letter frame
michael@0 9999 nsIContent* textContent = frame->GetContent();
michael@0 10000 if (IsFirstLetterContent(textContent)) {
michael@0 10001 // Create letter frame to wrap up the text
michael@0 10002 CreateLetterFrame(aBlockFrame, aBlockContinuation, textContent,
michael@0 10003 aParentFrame, aLetterFrames);
michael@0 10004
michael@0 10005 // Provide adjustment information for parent
michael@0 10006 *aModifiedParent = aParentFrame;
michael@0 10007 *aTextFrame = frame;
michael@0 10008 *aPrevFrame = prevFrame;
michael@0 10009 *aStopLooking = true;
michael@0 10010 return;
michael@0 10011 }
michael@0 10012 }
michael@0 10013 else if (IsInlineFrame(frame) && frameType != nsGkAtoms::brFrame) {
michael@0 10014 nsIFrame* kids = frame->GetFirstPrincipalChild();
michael@0 10015 WrapFramesInFirstLetterFrame(aBlockFrame, aBlockContinuation, frame,
michael@0 10016 kids, aModifiedParent, aTextFrame,
michael@0 10017 aPrevFrame, aLetterFrames, aStopLooking);
michael@0 10018 if (*aStopLooking) {
michael@0 10019 return;
michael@0 10020 }
michael@0 10021 }
michael@0 10022 else {
michael@0 10023 // This will stop us looking to create more letter frames. For
michael@0 10024 // example, maybe the frame-type is "letterFrame" or
michael@0 10025 // "placeholderFrame". This keeps us from creating extra letter
michael@0 10026 // frames, and also prevents us from creating letter frames when
michael@0 10027 // the first real content child of a block is not text (e.g. an
michael@0 10028 // image, hr, etc.)
michael@0 10029 *aStopLooking = true;
michael@0 10030 break;
michael@0 10031 }
michael@0 10032
michael@0 10033 prevFrame = frame;
michael@0 10034 frame = nextFrame;
michael@0 10035 }
michael@0 10036 }
michael@0 10037
michael@0 10038 static nsIFrame*
michael@0 10039 FindFirstLetterFrame(nsIFrame* aFrame, nsIFrame::ChildListID aListID)
michael@0 10040 {
michael@0 10041 nsFrameList list = aFrame->GetChildList(aListID);
michael@0 10042 for (nsFrameList::Enumerator e(list); !e.AtEnd(); e.Next()) {
michael@0 10043 if (nsGkAtoms::letterFrame == e.get()->GetType()) {
michael@0 10044 return e.get();
michael@0 10045 }
michael@0 10046 }
michael@0 10047 return nullptr;
michael@0 10048 }
michael@0 10049
michael@0 10050 nsresult
michael@0 10051 nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames(
michael@0 10052 nsPresContext* aPresContext,
michael@0 10053 nsIPresShell* aPresShell,
michael@0 10054 nsIFrame* aBlockFrame,
michael@0 10055 bool* aStopLooking)
michael@0 10056 {
michael@0 10057 // Look for the first letter frame on the kFloatList, then kPushedFloatsList.
michael@0 10058 nsIFrame* floatFrame =
michael@0 10059 ::FindFirstLetterFrame(aBlockFrame, nsIFrame::kFloatList);
michael@0 10060 if (!floatFrame) {
michael@0 10061 floatFrame =
michael@0 10062 ::FindFirstLetterFrame(aBlockFrame, nsIFrame::kPushedFloatsList);
michael@0 10063 if (!floatFrame) {
michael@0 10064 return NS_OK;
michael@0 10065 }
michael@0 10066 }
michael@0 10067
michael@0 10068 // Take the text frame away from the letter frame (so it isn't
michael@0 10069 // destroyed when we destroy the letter frame).
michael@0 10070 nsIFrame* textFrame = floatFrame->GetFirstPrincipalChild();
michael@0 10071 if (!textFrame) {
michael@0 10072 return NS_OK;
michael@0 10073 }
michael@0 10074
michael@0 10075 // Discover the placeholder frame for the letter frame
michael@0 10076 nsIFrame* parentFrame;
michael@0 10077 nsPlaceholderFrame* placeholderFrame = GetPlaceholderFrameFor(floatFrame);
michael@0 10078
michael@0 10079 if (!placeholderFrame) {
michael@0 10080 // Somethings really wrong
michael@0 10081 return NS_OK;
michael@0 10082 }
michael@0 10083 parentFrame = placeholderFrame->GetParent();
michael@0 10084 if (!parentFrame) {
michael@0 10085 // Somethings really wrong
michael@0 10086 return NS_OK;
michael@0 10087 }
michael@0 10088
michael@0 10089 // Create a new text frame with the right style context that maps
michael@0 10090 // all of the content that was previously part of the letter frame
michael@0 10091 // (and probably continued elsewhere).
michael@0 10092 nsStyleContext* parentSC = parentFrame->StyleContext();
michael@0 10093 nsIContent* textContent = textFrame->GetContent();
michael@0 10094 if (!textContent) {
michael@0 10095 return NS_OK;
michael@0 10096 }
michael@0 10097 nsRefPtr<nsStyleContext> newSC;
michael@0 10098 newSC = aPresShell->StyleSet()->ResolveStyleForNonElement(parentSC);
michael@0 10099 nsIFrame* newTextFrame = NS_NewTextFrame(aPresShell, newSC);
michael@0 10100 newTextFrame->Init(textContent, parentFrame, nullptr);
michael@0 10101
michael@0 10102 // Destroy the old text frame's continuations (the old text frame
michael@0 10103 // will be destroyed when its letter frame is destroyed).
michael@0 10104 nsIFrame* frameToDelete = textFrame->LastContinuation();
michael@0 10105 while (frameToDelete != textFrame) {
michael@0 10106 nsIFrame* nextFrameToDelete = frameToDelete->GetPrevContinuation();
michael@0 10107 RemoveFrame(kPrincipalList, frameToDelete);
michael@0 10108 frameToDelete = nextFrameToDelete;
michael@0 10109 }
michael@0 10110
michael@0 10111 nsIFrame* prevSibling = placeholderFrame->GetPrevSibling();
michael@0 10112
michael@0 10113 // Now that everything is set...
michael@0 10114 #ifdef NOISY_FIRST_LETTER
michael@0 10115 printf("RemoveFloatingFirstLetterFrames: textContent=%p oldTextFrame=%p newTextFrame=%p\n",
michael@0 10116 textContent.get(), textFrame, newTextFrame);
michael@0 10117 #endif
michael@0 10118
michael@0 10119 // Remove placeholder frame and the float
michael@0 10120 RemoveFrame(kPrincipalList, placeholderFrame);
michael@0 10121
michael@0 10122 // Now that the old frames are gone, we can start pointing to our
michael@0 10123 // new primary frame.
michael@0 10124 textContent->SetPrimaryFrame(newTextFrame);
michael@0 10125
michael@0 10126 // Wallpaper bug 822910.
michael@0 10127 bool offsetsNeedFixing =
michael@0 10128 prevSibling && prevSibling->GetType() == nsGkAtoms::textFrame;
michael@0 10129 if (offsetsNeedFixing) {
michael@0 10130 prevSibling->AddStateBits(TEXT_OFFSETS_NEED_FIXING);
michael@0 10131 }
michael@0 10132
michael@0 10133 // Insert text frame in its place
michael@0 10134 nsFrameList textList(newTextFrame, newTextFrame);
michael@0 10135 InsertFrames(parentFrame, kPrincipalList, prevSibling, textList);
michael@0 10136
michael@0 10137 if (offsetsNeedFixing) {
michael@0 10138 prevSibling->RemoveStateBits(TEXT_OFFSETS_NEED_FIXING);
michael@0 10139 }
michael@0 10140
michael@0 10141 return NS_OK;
michael@0 10142 }
michael@0 10143
michael@0 10144 nsresult
michael@0 10145 nsCSSFrameConstructor::RemoveFirstLetterFrames(nsPresContext* aPresContext,
michael@0 10146 nsIPresShell* aPresShell,
michael@0 10147 nsIFrame* aFrame,
michael@0 10148 nsIFrame* aBlockFrame,
michael@0 10149 bool* aStopLooking)
michael@0 10150 {
michael@0 10151 nsIFrame* prevSibling = nullptr;
michael@0 10152 nsIFrame* kid = aFrame->GetFirstPrincipalChild();
michael@0 10153
michael@0 10154 while (kid) {
michael@0 10155 if (nsGkAtoms::letterFrame == kid->GetType()) {
michael@0 10156 // Bingo. Found it. First steal away the text frame.
michael@0 10157 nsIFrame* textFrame = kid->GetFirstPrincipalChild();
michael@0 10158 if (!textFrame) {
michael@0 10159 break;
michael@0 10160 }
michael@0 10161
michael@0 10162 // Create a new textframe
michael@0 10163 nsStyleContext* parentSC = aFrame->StyleContext();
michael@0 10164 if (!parentSC) {
michael@0 10165 break;
michael@0 10166 }
michael@0 10167 nsIContent* textContent = textFrame->GetContent();
michael@0 10168 if (!textContent) {
michael@0 10169 break;
michael@0 10170 }
michael@0 10171 nsRefPtr<nsStyleContext> newSC;
michael@0 10172 newSC = aPresShell->StyleSet()->ResolveStyleForNonElement(parentSC);
michael@0 10173 textFrame = NS_NewTextFrame(aPresShell, newSC);
michael@0 10174 textFrame->Init(textContent, aFrame, nullptr);
michael@0 10175
michael@0 10176 // Next rip out the kid and replace it with the text frame
michael@0 10177 RemoveFrame(kPrincipalList, kid);
michael@0 10178
michael@0 10179 // Now that the old frames are gone, we can start pointing to our
michael@0 10180 // new primary frame.
michael@0 10181 textContent->SetPrimaryFrame(textFrame);
michael@0 10182
michael@0 10183 // Wallpaper bug 822910.
michael@0 10184 bool offsetsNeedFixing =
michael@0 10185 prevSibling && prevSibling->GetType() == nsGkAtoms::textFrame;
michael@0 10186 if (offsetsNeedFixing) {
michael@0 10187 prevSibling->AddStateBits(TEXT_OFFSETS_NEED_FIXING);
michael@0 10188 }
michael@0 10189
michael@0 10190 // Insert text frame in its place
michael@0 10191 nsFrameList textList(textFrame, textFrame);
michael@0 10192 InsertFrames(aFrame, kPrincipalList, prevSibling, textList);
michael@0 10193
michael@0 10194 if (offsetsNeedFixing) {
michael@0 10195 prevSibling->RemoveStateBits(TEXT_OFFSETS_NEED_FIXING);
michael@0 10196 }
michael@0 10197
michael@0 10198 *aStopLooking = true;
michael@0 10199 NS_ASSERTION(!aBlockFrame->GetPrevContinuation(),
michael@0 10200 "should have the first continuation here");
michael@0 10201 aBlockFrame->RemoveStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD);
michael@0 10202 break;
michael@0 10203 }
michael@0 10204 else if (IsInlineFrame(kid)) {
michael@0 10205 // Look inside child inline frame for the letter frame
michael@0 10206 RemoveFirstLetterFrames(aPresContext, aPresShell,
michael@0 10207 kid, aBlockFrame, aStopLooking);
michael@0 10208 if (*aStopLooking) {
michael@0 10209 break;
michael@0 10210 }
michael@0 10211 }
michael@0 10212 prevSibling = kid;
michael@0 10213 kid = kid->GetNextSibling();
michael@0 10214 }
michael@0 10215
michael@0 10216 return NS_OK;
michael@0 10217 }
michael@0 10218
michael@0 10219 nsresult
michael@0 10220 nsCSSFrameConstructor::RemoveLetterFrames(nsPresContext* aPresContext,
michael@0 10221 nsIPresShell* aPresShell,
michael@0 10222 nsIFrame* aBlockFrame)
michael@0 10223 {
michael@0 10224 aBlockFrame = aBlockFrame->FirstContinuation();
michael@0 10225 nsIFrame* continuation = aBlockFrame;
michael@0 10226
michael@0 10227 bool stopLooking = false;
michael@0 10228 nsresult rv;
michael@0 10229 do {
michael@0 10230 rv = RemoveFloatingFirstLetterFrames(aPresContext, aPresShell,
michael@0 10231 continuation, &stopLooking);
michael@0 10232 if (NS_SUCCEEDED(rv) && !stopLooking) {
michael@0 10233 rv = RemoveFirstLetterFrames(aPresContext, aPresShell,
michael@0 10234 continuation, aBlockFrame, &stopLooking);
michael@0 10235 }
michael@0 10236 if (stopLooking) {
michael@0 10237 break;
michael@0 10238 }
michael@0 10239 continuation = continuation->GetNextContinuation();
michael@0 10240 } while (continuation);
michael@0 10241 return rv;
michael@0 10242 }
michael@0 10243
michael@0 10244 // Fixup the letter frame situation for the given block
michael@0 10245 void
michael@0 10246 nsCSSFrameConstructor::RecoverLetterFrames(nsIFrame* aBlockFrame)
michael@0 10247 {
michael@0 10248 aBlockFrame = aBlockFrame->FirstContinuation();
michael@0 10249 nsIFrame* continuation = aBlockFrame;
michael@0 10250
michael@0 10251 nsIFrame* parentFrame = nullptr;
michael@0 10252 nsIFrame* textFrame = nullptr;
michael@0 10253 nsIFrame* prevFrame = nullptr;
michael@0 10254 nsFrameItems letterFrames;
michael@0 10255 bool stopLooking = false;
michael@0 10256 do {
michael@0 10257 // XXX shouldn't this bit be set already (bug 408493), assert instead?
michael@0 10258 continuation->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE);
michael@0 10259 WrapFramesInFirstLetterFrame(aBlockFrame, continuation, continuation,
michael@0 10260 continuation->GetFirstPrincipalChild(),
michael@0 10261 &parentFrame, &textFrame, &prevFrame,
michael@0 10262 letterFrames, &stopLooking);
michael@0 10263 if (stopLooking) {
michael@0 10264 break;
michael@0 10265 }
michael@0 10266 continuation = continuation->GetNextContinuation();
michael@0 10267 } while (continuation);
michael@0 10268
michael@0 10269 if (parentFrame) {
michael@0 10270 // Take the old textFrame out of the parents child list
michael@0 10271 RemoveFrame(kPrincipalList, textFrame);
michael@0 10272
michael@0 10273 // Insert in the letter frame(s)
michael@0 10274 parentFrame->InsertFrames(kPrincipalList, prevFrame, letterFrames);
michael@0 10275 }
michael@0 10276 }
michael@0 10277
michael@0 10278 //----------------------------------------------------------------------
michael@0 10279
michael@0 10280 // listbox Widget Routines
michael@0 10281
michael@0 10282 nsresult
michael@0 10283 nsCSSFrameConstructor::CreateListBoxContent(nsPresContext* aPresContext,
michael@0 10284 nsIFrame* aParentFrame,
michael@0 10285 nsIFrame* aPrevFrame,
michael@0 10286 nsIContent* aChild,
michael@0 10287 nsIFrame** aNewFrame,
michael@0 10288 bool aIsAppend,
michael@0 10289 bool aIsScrollbar,
michael@0 10290 nsILayoutHistoryState* aFrameState)
michael@0 10291 {
michael@0 10292 #ifdef MOZ_XUL
michael@0 10293 nsresult rv = NS_OK;
michael@0 10294
michael@0 10295 // Construct a new frame
michael@0 10296 if (nullptr != aParentFrame) {
michael@0 10297 nsFrameItems frameItems;
michael@0 10298 nsFrameConstructorState state(mPresShell, GetAbsoluteContainingBlock(aParentFrame, FIXED_POS),
michael@0 10299 GetAbsoluteContainingBlock(aParentFrame, ABS_POS),
michael@0 10300 GetFloatContainingBlock(aParentFrame),
michael@0 10301 mTempFrameTreeState);
michael@0 10302
michael@0 10303 // If we ever initialize the ancestor filter on |state|, make sure
michael@0 10304 // to push the right parent!
michael@0 10305
michael@0 10306 nsRefPtr<nsStyleContext> styleContext;
michael@0 10307 styleContext = ResolveStyleContext(aParentFrame, aChild, &state);
michael@0 10308
michael@0 10309 // Pre-check for display "none" - only if we find that, do we create
michael@0 10310 // any frame at all
michael@0 10311 const nsStyleDisplay* display = styleContext->StyleDisplay();
michael@0 10312
michael@0 10313 if (NS_STYLE_DISPLAY_NONE == display->mDisplay) {
michael@0 10314 *aNewFrame = nullptr;
michael@0 10315 return NS_OK;
michael@0 10316 }
michael@0 10317
michael@0 10318 BeginUpdate();
michael@0 10319
michael@0 10320 FrameConstructionItemList items;
michael@0 10321 AddFrameConstructionItemsInternal(state, aChild, aParentFrame,
michael@0 10322 aChild->Tag(), aChild->GetNameSpaceID(),
michael@0 10323 true, styleContext,
michael@0 10324 ITEM_ALLOW_XBL_BASE, nullptr, items);
michael@0 10325 ConstructFramesFromItemList(state, items, aParentFrame, frameItems);
michael@0 10326
michael@0 10327 nsIFrame* newFrame = frameItems.FirstChild();
michael@0 10328 *aNewFrame = newFrame;
michael@0 10329
michael@0 10330 if (newFrame) {
michael@0 10331 // Notify the parent frame
michael@0 10332 if (aIsAppend)
michael@0 10333 rv = ((nsListBoxBodyFrame*)aParentFrame)->ListBoxAppendFrames(frameItems);
michael@0 10334 else
michael@0 10335 rv = ((nsListBoxBodyFrame*)aParentFrame)->ListBoxInsertFrames(aPrevFrame, frameItems);
michael@0 10336 }
michael@0 10337
michael@0 10338 EndUpdate();
michael@0 10339
michael@0 10340 #ifdef ACCESSIBILITY
michael@0 10341 if (newFrame) {
michael@0 10342 nsAccessibilityService* accService = nsIPresShell::AccService();
michael@0 10343 if (accService) {
michael@0 10344 accService->ContentRangeInserted(mPresShell, aChild->GetParent(),
michael@0 10345 aChild, aChild->GetNextSibling());
michael@0 10346 }
michael@0 10347 }
michael@0 10348 #endif
michael@0 10349 }
michael@0 10350
michael@0 10351 return rv;
michael@0 10352 #else
michael@0 10353 return NS_ERROR_FAILURE;
michael@0 10354 #endif
michael@0 10355 }
michael@0 10356
michael@0 10357 //----------------------------------------
michael@0 10358
michael@0 10359 void
michael@0 10360 nsCSSFrameConstructor::ConstructBlock(nsFrameConstructorState& aState,
michael@0 10361 const nsStyleDisplay* aDisplay,
michael@0 10362 nsIContent* aContent,
michael@0 10363 nsIFrame* aParentFrame,
michael@0 10364 nsIFrame* aContentParentFrame,
michael@0 10365 nsStyleContext* aStyleContext,
michael@0 10366 nsIFrame** aNewFrame,
michael@0 10367 nsFrameItems& aFrameItems,
michael@0 10368 nsIFrame* aPositionedFrameForAbsPosContainer,
michael@0 10369 PendingBinding* aPendingBinding)
michael@0 10370 {
michael@0 10371 // Create column wrapper if necessary
michael@0 10372 nsIFrame* blockFrame = *aNewFrame;
michael@0 10373 NS_ASSERTION(blockFrame->GetType() == nsGkAtoms::blockFrame, "not a block frame?");
michael@0 10374 nsIFrame* parent = aParentFrame;
michael@0 10375 nsRefPtr<nsStyleContext> blockStyle = aStyleContext;
michael@0 10376 const nsStyleColumn* columns = aStyleContext->StyleColumn();
michael@0 10377
michael@0 10378 if (columns->mColumnCount != NS_STYLE_COLUMN_COUNT_AUTO
michael@0 10379 || columns->mColumnWidth.GetUnit() != eStyleUnit_Auto) {
michael@0 10380 nsIFrame* columnSetFrame = nullptr;
michael@0 10381 columnSetFrame =
michael@0 10382 NS_NewColumnSetFrame(mPresShell, aStyleContext, nsFrameState(0));
michael@0 10383
michael@0 10384 InitAndRestoreFrame(aState, aContent, aParentFrame, columnSetFrame);
michael@0 10385 blockStyle = mPresShell->StyleSet()->
michael@0 10386 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::columnContent, aStyleContext);
michael@0 10387 parent = columnSetFrame;
michael@0 10388 *aNewFrame = columnSetFrame;
michael@0 10389
michael@0 10390 SetInitialSingleChild(columnSetFrame, blockFrame);
michael@0 10391 }
michael@0 10392
michael@0 10393 blockFrame->SetStyleContextWithoutNotification(blockStyle);
michael@0 10394 InitAndRestoreFrame(aState, aContent, parent, blockFrame);
michael@0 10395
michael@0 10396 aState.AddChild(*aNewFrame, aFrameItems, aContent, aStyleContext,
michael@0 10397 aContentParentFrame ? aContentParentFrame :
michael@0 10398 aParentFrame);
michael@0 10399 if (!mRootElementFrame) {
michael@0 10400 // The frame we're constructing will be the root element frame.
michael@0 10401 // Set mRootElementFrame before processing children.
michael@0 10402 mRootElementFrame = *aNewFrame;
michael@0 10403 }
michael@0 10404
michael@0 10405 // We should make the outer frame be the absolute containing block,
michael@0 10406 // if one is required. We have to do this because absolute
michael@0 10407 // positioning must be computed with respect to the CSS dimensions
michael@0 10408 // of the element, which are the dimensions of the outer block. But
michael@0 10409 // we can't really do that because only blocks can have absolute
michael@0 10410 // children. So use the block and try to compensate with hacks
michael@0 10411 // in nsBlockFrame::CalculateContainingBlockSizeForAbsolutes.
michael@0 10412 nsFrameConstructorSaveState absoluteSaveState;
michael@0 10413 (*aNewFrame)->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
michael@0 10414 if (aPositionedFrameForAbsPosContainer) {
michael@0 10415 // NS_ASSERTION(aRelPos, "should have made area frame for this");
michael@0 10416 aState.PushAbsoluteContainingBlock(*aNewFrame, aPositionedFrameForAbsPosContainer, absoluteSaveState);
michael@0 10417 }
michael@0 10418
michael@0 10419 // Process the child content
michael@0 10420 nsFrameItems childItems;
michael@0 10421 ProcessChildren(aState, aContent, aStyleContext, blockFrame, true,
michael@0 10422 childItems, true, aPendingBinding);
michael@0 10423
michael@0 10424 // Set the frame's initial child list
michael@0 10425 blockFrame->SetInitialChildList(kPrincipalList, childItems);
michael@0 10426 }
michael@0 10427
michael@0 10428 nsIFrame*
michael@0 10429 nsCSSFrameConstructor::ConstructInline(nsFrameConstructorState& aState,
michael@0 10430 FrameConstructionItem& aItem,
michael@0 10431 nsIFrame* aParentFrame,
michael@0 10432 const nsStyleDisplay* aDisplay,
michael@0 10433 nsFrameItems& aFrameItems)
michael@0 10434 {
michael@0 10435 // If an inline frame has non-inline kids, then we chop up the child list
michael@0 10436 // into runs of blocks and runs of inlines, create anonymous block frames to
michael@0 10437 // contain the runs of blocks, inline frames with our style context for the
michael@0 10438 // runs of inlines, and put all these frames, in order, into aFrameItems. We
michael@0 10439 // return the the first one. The whole setup is called an {ib}
michael@0 10440 // split; in what follows "frames in the split" refers to the anonymous blocks
michael@0 10441 // and inlines that contain our children.
michael@0 10442 //
michael@0 10443 // {ib} splits maintain the following invariants:
michael@0 10444 // 1) All frames in the split have the NS_FRAME_PART_OF_IBSPLIT bit
michael@0 10445 // set.
michael@0 10446 // 2) Each frame in the split has the nsIFrame::IBSplitSibling
michael@0 10447 // property pointing to the next frame in the split, except for the last
michael@0 10448 // one, which does not have it set.
michael@0 10449 // 3) Each frame in the split has the nsIFrame::IBSplitPrevSibling
michael@0 10450 // property pointing to the previous frame in the split, except for the
michael@0 10451 // first one, which does not have it set.
michael@0 10452 // 4) The first and last frame in the split are always inlines.
michael@0 10453 //
michael@0 10454 // An invariant that is NOT maintained is that the wrappers are actually
michael@0 10455 // linked via GetNextSibling linkage. A simple example is an inline
michael@0 10456 // containing an inline that contains a block. The three parts of the inner
michael@0 10457 // inline end up with three different parents.
michael@0 10458 //
michael@0 10459 // For example, this HTML:
michael@0 10460 // <span>
michael@0 10461 // <div>a</div>
michael@0 10462 // <span>
michael@0 10463 // b
michael@0 10464 // <div>c</div>
michael@0 10465 // </span>
michael@0 10466 // d
michael@0 10467 // <div>e</div>
michael@0 10468 // f
michael@0 10469 // </span>
michael@0 10470 // Gives the following frame tree:
michael@0 10471 //
michael@0 10472 // Inline (outer span)
michael@0 10473 // Block (anonymous, outer span)
michael@0 10474 // Block (div)
michael@0 10475 // Text("a")
michael@0 10476 // Inline (outer span)
michael@0 10477 // Inline (inner span)
michael@0 10478 // Text("b")
michael@0 10479 // Block (anonymous, outer span)
michael@0 10480 // Block (anonymous, inner span)
michael@0 10481 // Block (div)
michael@0 10482 // Text("c")
michael@0 10483 // Inline (outer span)
michael@0 10484 // Inline (inner span)
michael@0 10485 // Text("d")
michael@0 10486 // Block (anonymous, outer span)
michael@0 10487 // Block (div)
michael@0 10488 // Text("e")
michael@0 10489 // Inline (outer span)
michael@0 10490 // Text("f")
michael@0 10491
michael@0 10492 nsIContent* const content = aItem.mContent;
michael@0 10493 nsStyleContext* const styleContext = aItem.mStyleContext;
michael@0 10494
michael@0 10495 bool positioned =
michael@0 10496 NS_STYLE_DISPLAY_INLINE == aDisplay->mDisplay &&
michael@0 10497 aDisplay->IsRelativelyPositionedStyle() &&
michael@0 10498 !aParentFrame->IsSVGText();
michael@0 10499
michael@0 10500 nsIFrame* newFrame = NS_NewInlineFrame(mPresShell, styleContext);
michael@0 10501
michael@0 10502 // Initialize the frame
michael@0 10503 InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
michael@0 10504
michael@0 10505 // Inline frames can always have generated content
michael@0 10506 newFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT);
michael@0 10507
michael@0 10508 nsFrameConstructorSaveState absoluteSaveState; // definition cannot be inside next block
michael@0 10509 // because the object's destructor is significant
michael@0 10510 // this is part of the fix for bug 42372
michael@0 10511
michael@0 10512 newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
michael@0 10513 if (positioned) {
michael@0 10514 // Relatively positioned frames becomes a container for child
michael@0 10515 // frames that are positioned
michael@0 10516 aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState);
michael@0 10517 }
michael@0 10518
michael@0 10519 // Process the child content
michael@0 10520 nsFrameItems childItems;
michael@0 10521 ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame, childItems);
michael@0 10522
michael@0 10523 nsFrameList::FrameLinkEnumerator firstBlockEnumerator(childItems);
michael@0 10524 if (!aItem.mIsAllInline) {
michael@0 10525 FindFirstBlock(firstBlockEnumerator);
michael@0 10526 }
michael@0 10527
michael@0 10528 if (aItem.mIsAllInline || firstBlockEnumerator.AtEnd()) {
michael@0 10529 // This part is easy. We either already know we have no non-inline kids,
michael@0 10530 // or haven't found any when constructing actual frames (the latter can
michael@0 10531 // happen only if out-of-flows that we thought had no containing block
michael@0 10532 // acquired one when ancestor inline frames and {ib} splits got
michael@0 10533 // constructed). Just put all the kids into the single inline frame and
michael@0 10534 // bail.
michael@0 10535 newFrame->SetInitialChildList(kPrincipalList, childItems);
michael@0 10536 aState.AddChild(newFrame, aFrameItems, content, styleContext, aParentFrame);
michael@0 10537 return newFrame;
michael@0 10538 }
michael@0 10539
michael@0 10540 // This inline frame contains several types of children. Therefore this frame
michael@0 10541 // has to be chopped into several pieces, as described above.
michael@0 10542
michael@0 10543 // Grab the first inline's kids
michael@0 10544 nsFrameList firstInlineKids = childItems.ExtractHead(firstBlockEnumerator);
michael@0 10545 newFrame->SetInitialChildList(kPrincipalList, firstInlineKids);
michael@0 10546
michael@0 10547 aFrameItems.AddChild(newFrame);
michael@0 10548
michael@0 10549 CreateIBSiblings(aState, newFrame, positioned, childItems, aFrameItems);
michael@0 10550
michael@0 10551 return newFrame;
michael@0 10552 }
michael@0 10553
michael@0 10554 void
michael@0 10555 nsCSSFrameConstructor::CreateIBSiblings(nsFrameConstructorState& aState,
michael@0 10556 nsIFrame* aInitialInline,
michael@0 10557 bool aIsPositioned,
michael@0 10558 nsFrameItems& aChildItems,
michael@0 10559 nsFrameItems& aSiblings)
michael@0 10560 {
michael@0 10561 nsIContent* content = aInitialInline->GetContent();
michael@0 10562 nsStyleContext* styleContext = aInitialInline->StyleContext();
michael@0 10563 nsIFrame* parentFrame = aInitialInline->GetParent();
michael@0 10564
michael@0 10565 // Resolve the right style context for our anonymous blocks.
michael@0 10566 // The distinction in styles is needed because of CSS 2.1, section
michael@0 10567 // 9.2.1.1, which says:
michael@0 10568 // When such an inline box is affected by relative positioning, any
michael@0 10569 // resulting translation also affects the block-level box contained
michael@0 10570 // in the inline box.
michael@0 10571 nsRefPtr<nsStyleContext> blockSC =
michael@0 10572 mPresShell->StyleSet()->
michael@0 10573 ResolveAnonymousBoxStyle(aIsPositioned ?
michael@0 10574 nsCSSAnonBoxes::mozAnonymousPositionedBlock :
michael@0 10575 nsCSSAnonBoxes::mozAnonymousBlock,
michael@0 10576 styleContext);
michael@0 10577
michael@0 10578 nsIFrame* lastNewInline = aInitialInline->FirstContinuation();
michael@0 10579 do {
michael@0 10580 // On entry to this loop aChildItems is not empty and the first frame in it
michael@0 10581 // is block-level.
michael@0 10582 NS_PRECONDITION(aChildItems.NotEmpty(), "Should have child items");
michael@0 10583 NS_PRECONDITION(!aChildItems.FirstChild()->IsInlineOutside(),
michael@0 10584 "Must have list starting with block");
michael@0 10585
michael@0 10586 // The initial run of blocks belongs to an anonymous block that we create
michael@0 10587 // right now. The anonymous block will be the parent of these block
michael@0 10588 // children of the inline.
michael@0 10589 nsIFrame* blockFrame;
michael@0 10590 blockFrame = NS_NewBlockFrame(mPresShell, blockSC);
michael@0 10591
michael@0 10592 InitAndRestoreFrame(aState, content, parentFrame, blockFrame, false);
michael@0 10593
michael@0 10594 // Find the first non-block child which defines the end of our block kids
michael@0 10595 // and the start of our next inline's kids
michael@0 10596 nsFrameList::FrameLinkEnumerator firstNonBlock =
michael@0 10597 FindFirstNonBlock(aChildItems);
michael@0 10598 nsFrameList blockKids = aChildItems.ExtractHead(firstNonBlock);
michael@0 10599
michael@0 10600 MoveChildrenTo(aState.mPresContext, aInitialInline, blockFrame, blockKids);
michael@0 10601
michael@0 10602 SetFrameIsIBSplit(lastNewInline, blockFrame);
michael@0 10603 aSiblings.AddChild(blockFrame);
michael@0 10604
michael@0 10605 // Now grab the initial inlines in aChildItems and put them into an inline
michael@0 10606 // frame
michael@0 10607 nsIFrame* inlineFrame = NS_NewInlineFrame(mPresShell, styleContext);
michael@0 10608
michael@0 10609 InitAndRestoreFrame(aState, content, parentFrame, inlineFrame, false);
michael@0 10610
michael@0 10611 inlineFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT |
michael@0 10612 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
michael@0 10613 if (aIsPositioned) {
michael@0 10614 inlineFrame->MarkAsAbsoluteContainingBlock();
michael@0 10615 }
michael@0 10616
michael@0 10617 if (aChildItems.NotEmpty()) {
michael@0 10618 nsFrameList::FrameLinkEnumerator firstBlock(aChildItems);
michael@0 10619 FindFirstBlock(firstBlock);
michael@0 10620 nsFrameList inlineKids = aChildItems.ExtractHead(firstBlock);
michael@0 10621
michael@0 10622 MoveChildrenTo(aState.mPresContext, aInitialInline, inlineFrame,
michael@0 10623 inlineKids);
michael@0 10624 }
michael@0 10625
michael@0 10626 SetFrameIsIBSplit(blockFrame, inlineFrame);
michael@0 10627 aSiblings.AddChild(inlineFrame);
michael@0 10628 lastNewInline = inlineFrame;
michael@0 10629 } while (aChildItems.NotEmpty());
michael@0 10630
michael@0 10631 SetFrameIsIBSplit(lastNewInline, nullptr);
michael@0 10632 }
michael@0 10633
michael@0 10634 void
michael@0 10635 nsCSSFrameConstructor::BuildInlineChildItems(nsFrameConstructorState& aState,
michael@0 10636 FrameConstructionItem& aParentItem,
michael@0 10637 bool aItemIsWithinSVGText,
michael@0 10638 bool aItemAllowsTextPathChild)
michael@0 10639 {
michael@0 10640 // XXXbz should we preallocate aParentItem.mChildItems to some sane
michael@0 10641 // length? Maybe even to parentContent->GetChildCount()?
michael@0 10642 nsFrameConstructorState::PendingBindingAutoPusher
michael@0 10643 pusher(aState, aParentItem.mPendingBinding);
michael@0 10644
michael@0 10645 nsStyleContext* const parentStyleContext = aParentItem.mStyleContext;
michael@0 10646 nsIContent* const parentContent = aParentItem.mContent;
michael@0 10647
michael@0 10648 TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext);
michael@0 10649 if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
michael@0 10650 ancestorPusher.PushAncestorAndStyleScope(parentContent->AsElement());
michael@0 10651 } else {
michael@0 10652 ancestorPusher.PushStyleScope(parentContent->AsElement());
michael@0 10653 }
michael@0 10654
michael@0 10655 if (!aItemIsWithinSVGText) {
michael@0 10656 // Probe for generated content before
michael@0 10657 CreateGeneratedContentItem(aState, nullptr, parentContent, parentStyleContext,
michael@0 10658 nsCSSPseudoElements::ePseudo_before,
michael@0 10659 aParentItem.mChildItems);
michael@0 10660 }
michael@0 10661
michael@0 10662 uint32_t flags = ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK;
michael@0 10663 if (aItemIsWithinSVGText) {
michael@0 10664 flags |= ITEM_IS_WITHIN_SVG_TEXT;
michael@0 10665 }
michael@0 10666 if (aItemAllowsTextPathChild && aParentItem.mIsForSVGAElement) {
michael@0 10667 flags |= ITEM_ALLOWS_TEXT_PATH_CHILD;
michael@0 10668 }
michael@0 10669
michael@0 10670 if (!aParentItem.mAnonChildren.IsEmpty()) {
michael@0 10671 // Use the anon-children list instead of the content tree child list so
michael@0 10672 // that we use any special style context that should be associated with
michael@0 10673 // the children, and so that we won't try to construct grandchildren frame
michael@0 10674 // constructor items before the frame is available for their parent.
michael@0 10675 AddFCItemsForAnonymousContent(aState, nullptr, aParentItem.mAnonChildren,
michael@0 10676 aParentItem.mChildItems, flags);
michael@0 10677 } else {
michael@0 10678 // Use the content tree child list:
michael@0 10679 FlattenedChildIterator iter(parentContent);
michael@0 10680 for (nsIContent* content = iter.GetNextChild(); content; content = iter.GetNextChild()) {
michael@0 10681 // Get the parent of the content and check if it is a XBL children element
michael@0 10682 // (if the content is a children element then contentParent != parentContent because the
michael@0 10683 // FlattenedChildIterator will transitively iterate through <xbl:children>
michael@0 10684 // for default content). Push the children element as an ancestor here because
michael@0 10685 // it does not have a frame and would not otherwise be pushed as an ancestor.
michael@0 10686 nsIContent* contentParent = content->GetParent();
michael@0 10687 MOZ_ASSERT(contentParent, "Parent must be non-null because we are iterating children.");
michael@0 10688 TreeMatchContext::AutoAncestorPusher insertionPointPusher(aState.mTreeMatchContext);
michael@0 10689 if (contentParent != parentContent && contentParent->IsElement()) {
michael@0 10690 if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
michael@0 10691 insertionPointPusher.PushAncestorAndStyleScope(contentParent->AsElement());
michael@0 10692 } else {
michael@0 10693 insertionPointPusher.PushStyleScope(contentParent->AsElement());
michael@0 10694 }
michael@0 10695 }
michael@0 10696
michael@0 10697 // Manually check for comments/PIs, since we don't have a frame to pass to
michael@0 10698 // AddFrameConstructionItems. We know our parent is a non-replaced inline,
michael@0 10699 // so there is no need to do the NeedFrameFor check.
michael@0 10700 content->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
michael@0 10701 if (content->IsNodeOfType(nsINode::eCOMMENT) ||
michael@0 10702 content->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
michael@0 10703 continue;
michael@0 10704 }
michael@0 10705 if (content->IsElement()) {
michael@0 10706 // See comment explaining why we need to remove the "is possible
michael@0 10707 // restyle root" flags in AddFrameConstructionItems. But note
michael@0 10708 // that we can remove all restyle flags, just like in
michael@0 10709 // ProcessChildren and for the same reason.
michael@0 10710 content->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
michael@0 10711 }
michael@0 10712
michael@0 10713 nsRefPtr<nsStyleContext> childContext =
michael@0 10714 ResolveStyleContext(parentStyleContext, content, &aState);
michael@0 10715
michael@0 10716 AddFrameConstructionItemsInternal(aState, content, nullptr, content->Tag(),
michael@0 10717 content->GetNameSpaceID(),
michael@0 10718 iter.XBLInvolved(), childContext,
michael@0 10719 flags, nullptr,
michael@0 10720 aParentItem.mChildItems);
michael@0 10721 }
michael@0 10722 }
michael@0 10723
michael@0 10724 if (!aItemIsWithinSVGText) {
michael@0 10725 // Probe for generated content after
michael@0 10726 CreateGeneratedContentItem(aState, nullptr, parentContent, parentStyleContext,
michael@0 10727 nsCSSPseudoElements::ePseudo_after,
michael@0 10728 aParentItem.mChildItems);
michael@0 10729 }
michael@0 10730
michael@0 10731 aParentItem.mIsAllInline = aParentItem.mChildItems.AreAllItemsInline();
michael@0 10732 }
michael@0 10733
michael@0 10734 // return whether it's ok to append (in the AppendFrames sense) to
michael@0 10735 // aParentFrame if our nextSibling is aNextSibling. aParentFrame must
michael@0 10736 // be an ib-split inline.
michael@0 10737 static bool
michael@0 10738 IsSafeToAppendToIBSplitInline(nsIFrame* aParentFrame, nsIFrame* aNextSibling)
michael@0 10739 {
michael@0 10740 NS_PRECONDITION(IsInlineFrame(aParentFrame),
michael@0 10741 "Must have an inline parent here");
michael@0 10742 do {
michael@0 10743 NS_ASSERTION(IsFramePartOfIBSplit(aParentFrame),
michael@0 10744 "How is this not part of an ib-split?");
michael@0 10745 if (aNextSibling || aParentFrame->GetNextContinuation() ||
michael@0 10746 GetIBSplitSibling(aParentFrame)) {
michael@0 10747 return false;
michael@0 10748 }
michael@0 10749
michael@0 10750 aNextSibling = aParentFrame->GetNextSibling();
michael@0 10751 aParentFrame = aParentFrame->GetParent();
michael@0 10752 } while (IsInlineFrame(aParentFrame));
michael@0 10753
michael@0 10754 return true;
michael@0 10755 }
michael@0 10756
michael@0 10757 bool
michael@0 10758 nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState,
michael@0 10759 nsIFrame* aContainingBlock,
michael@0 10760 nsIFrame* aFrame,
michael@0 10761 FrameConstructionItemList& aItems,
michael@0 10762 bool aIsAppend,
michael@0 10763 nsIFrame* aPrevSibling)
michael@0 10764 {
michael@0 10765 if (aItems.IsEmpty()) {
michael@0 10766 return false;
michael@0 10767 }
michael@0 10768
michael@0 10769 // Before we go and append the frames, we must check for several
michael@0 10770 // special situations.
michael@0 10771
michael@0 10772 // Situation #1 is a XUL frame that contains frames that are required
michael@0 10773 // to be wrapped in blocks.
michael@0 10774 if (aFrame->IsBoxFrame() &&
michael@0 10775 !(aFrame->GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK) &&
michael@0 10776 aItems.AnyItemsNeedBlockParent()) {
michael@0 10777 RecreateFramesForContent(aFrame->GetContent(), true);
michael@0 10778 return true;
michael@0 10779 }
michael@0 10780
michael@0 10781 nsIFrame* nextSibling = ::GetInsertNextSibling(aFrame, aPrevSibling);
michael@0 10782
michael@0 10783 // Situation #2 is a flex container frame into which we're inserting new
michael@0 10784 // inline non-replaced children, adjacent to an existing anonymous flex item.
michael@0 10785 if (aFrame->GetType() == nsGkAtoms::flexContainerFrame) {
michael@0 10786 FCItemIterator iter(aItems);
michael@0 10787
michael@0 10788 // Check if we're adding to-be-wrapped content right *after* an existing
michael@0 10789 // anonymous flex item (which would need to absorb this content).
michael@0 10790 if (aPrevSibling && IsAnonymousFlexItem(aPrevSibling) &&
michael@0 10791 iter.item().NeedsAnonFlexItem(aState)) {
michael@0 10792 RecreateFramesForContent(aFrame->GetContent(), true);
michael@0 10793 return true;
michael@0 10794 }
michael@0 10795
michael@0 10796 // Check if we're adding to-be-wrapped content right *before* an existing
michael@0 10797 // anonymous flex item (which would need to absorb this content).
michael@0 10798 if (nextSibling && IsAnonymousFlexItem(nextSibling)) {
michael@0 10799 // Jump to the last entry in the list
michael@0 10800 iter.SetToEnd();
michael@0 10801 iter.Prev();
michael@0 10802 if (iter.item().NeedsAnonFlexItem(aState)) {
michael@0 10803 RecreateFramesForContent(aFrame->GetContent(), true);
michael@0 10804 return true;
michael@0 10805 }
michael@0 10806 }
michael@0 10807 }
michael@0 10808
michael@0 10809 // Situation #3 is an anonymous flex item that's getting new children who
michael@0 10810 // don't want to be wrapped.
michael@0 10811 if (IsAnonymousFlexItem(aFrame)) {
michael@0 10812 nsIFrame* flexContainerFrame = aFrame->GetParent();
michael@0 10813 NS_ABORT_IF_FALSE(flexContainerFrame &&
michael@0 10814 flexContainerFrame->GetType() == nsGkAtoms::flexContainerFrame,
michael@0 10815 "anonymous flex items should only exist as children "
michael@0 10816 "of flex container frames");
michael@0 10817
michael@0 10818 // We need to push a null float containing block to be sure that
michael@0 10819 // "NeedsAnonFlexItem" will know we're not honoring floats for this
michael@0 10820 // inserted content. (In particular, this is necessary in order for
michael@0 10821 // NeedsAnonFlexItem's "GetGeometricParent" call to return the correct
michael@0 10822 // result.) We're not honoring floats on this content because it has the
michael@0 10823 // _flex container_ as its parent in the content tree.
michael@0 10824 nsFrameConstructorSaveState floatSaveState;
michael@0 10825 aState.PushFloatContainingBlock(nullptr, floatSaveState);
michael@0 10826
michael@0 10827 FCItemIterator iter(aItems);
michael@0 10828 // Skip over things that _do_ need an anonymous flex item, because
michael@0 10829 // they're perfectly happy to go here -- they won't cause a reframe.
michael@0 10830 if (!iter.SkipItemsThatNeedAnonFlexItem(aState)) {
michael@0 10831 // We hit something that _doesn't_ need an anonymous flex item!
michael@0 10832 // Rebuild the flex container to bust it out.
michael@0 10833 RecreateFramesForContent(flexContainerFrame->GetContent(), true);
michael@0 10834 return true;
michael@0 10835 }
michael@0 10836
michael@0 10837 // If we get here, then everything in |aItems| needs to be wrapped in
michael@0 10838 // an anonymous flex item. That's where it's already going - good!
michael@0 10839 }
michael@0 10840
michael@0 10841 // Situation #4 is a case when table pseudo-frames don't work out right
michael@0 10842 ParentType parentType = GetParentType(aFrame);
michael@0 10843 // If all the kids want a parent of the type that aFrame is, then we're all
michael@0 10844 // set to go. Indeed, there won't be any table pseudo-frames created between
michael@0 10845 // aFrame and the kids, so those won't need to be merged with any table
michael@0 10846 // pseudo-frames that might already be kids of aFrame. If aFrame itself is a
michael@0 10847 // table pseudo-frame, then all the kids in this list would have wanted a
michael@0 10848 // frame of that type wrapping them anyway, so putting them inside it is ok.
michael@0 10849 if (!aItems.AllWantParentType(parentType)) {
michael@0 10850 // Don't give up yet. If parentType is not eTypeBlock and the parent is
michael@0 10851 // not a generated content frame, then try filtering whitespace out of the
michael@0 10852 // list.
michael@0 10853 if (parentType != eTypeBlock && !aFrame->IsGeneratedContentFrame()) {
michael@0 10854 // For leading whitespace followed by a kid that wants our parent type,
michael@0 10855 // there are four cases:
michael@0 10856 // 1) We have a previous sibling which is not a table pseudo. That means
michael@0 10857 // that previous sibling wanted a (non-block) parent of the type we're
michael@0 10858 // looking at. Then the whitespace comes between two table-internal
michael@0 10859 // elements, so should be collapsed out.
michael@0 10860 // 2) We have a previous sibling which is a table pseudo. It might have
michael@0 10861 // kids who want this whitespace, so we need to reframe.
michael@0 10862 // 3) We have no previous sibling and our parent frame is not a table
michael@0 10863 // pseudo. That means that we'll be at the beginning of our actual
michael@0 10864 // non-block-type parent, and the whitespace is OK to collapse out.
michael@0 10865 // If something is ever inserted before us, it'll find our own parent
michael@0 10866 // as its parent and if it's something that would care about the
michael@0 10867 // whitespace it'll want a block parent, so it'll trigger a reframe at
michael@0 10868 // that point.
michael@0 10869 // 4) We have no previous sibling and our parent frame is a table pseudo.
michael@0 10870 // Need to reframe.
michael@0 10871 // All that is predicated on finding the correct previous sibling. We
michael@0 10872 // might have to walk backwards along continuations from aFrame to do so.
michael@0 10873 //
michael@0 10874 // It's always OK to drop whitespace between any two items that want a
michael@0 10875 // parent of type parentType.
michael@0 10876 //
michael@0 10877 // For trailing whitespace preceded by a kid that wants our parent type,
michael@0 10878 // there are four cases:
michael@0 10879 // 1) We have a next sibling which is not a table pseudo. That means
michael@0 10880 // that next sibling wanted a (non-block) parent of the type we're
michael@0 10881 // looking at. Then the whitespace comes between two table-internal
michael@0 10882 // elements, so should be collapsed out.
michael@0 10883 // 2) We have a next sibling which is a table pseudo. It might have
michael@0 10884 // kids who want this whitespace, so we need to reframe.
michael@0 10885 // 3) We have no next sibling and our parent frame is not a table
michael@0 10886 // pseudo. That means that we'll be at the end of our actual
michael@0 10887 // non-block-type parent, and the whitespace is OK to collapse out.
michael@0 10888 // If something is ever inserted after us, it'll find our own parent
michael@0 10889 // as its parent and if it's something that would care about the
michael@0 10890 // whitespace it'll want a block parent, so it'll trigger a reframe at
michael@0 10891 // that point.
michael@0 10892 // 4) We have no next sibling and our parent frame is a table pseudo.
michael@0 10893 // Need to reframe.
michael@0 10894 // All that is predicated on finding the correct next sibling. We might
michael@0 10895 // have to walk forward along continuations from aFrame to do so. That
michael@0 10896 // said, in the case when nextSibling is null at this point and aIsAppend
michael@0 10897 // is true, we know we're in case 3. Furthermore, in that case we don't
michael@0 10898 // even have to worry about the table pseudo situation; we know our
michael@0 10899 // parent is not a table pseudo there.
michael@0 10900 FCItemIterator iter(aItems);
michael@0 10901 FCItemIterator start(iter);
michael@0 10902 do {
michael@0 10903 if (iter.SkipItemsWantingParentType(parentType)) {
michael@0 10904 break;
michael@0 10905 }
michael@0 10906
michael@0 10907 // iter points to an item that wants a different parent. If it's not
michael@0 10908 // whitespace, we're done; no more point scanning the list.
michael@0 10909 if (!iter.item().IsWhitespace(aState)) {
michael@0 10910 break;
michael@0 10911 }
michael@0 10912
michael@0 10913 if (iter == start) {
michael@0 10914 // Leading whitespace. How to handle this depends on our
michael@0 10915 // previous sibling and aFrame. See the long comment above.
michael@0 10916 nsIFrame* prevSibling = aPrevSibling;
michael@0 10917 if (!prevSibling) {
michael@0 10918 // Try to find one after all
michael@0 10919 nsIFrame* parentPrevCont = aFrame->GetPrevContinuation();
michael@0 10920 while (parentPrevCont) {
michael@0 10921 prevSibling = parentPrevCont->GetLastChild(kPrincipalList);
michael@0 10922 if (prevSibling) {
michael@0 10923 break;
michael@0 10924 }
michael@0 10925 parentPrevCont = parentPrevCont->GetPrevContinuation();
michael@0 10926 }
michael@0 10927 };
michael@0 10928 if (prevSibling) {
michael@0 10929 if (IsTablePseudo(prevSibling)) {
michael@0 10930 // need to reframe
michael@0 10931 break;
michael@0 10932 }
michael@0 10933 } else if (IsTablePseudo(aFrame)) {
michael@0 10934 // need to reframe
michael@0 10935 break;
michael@0 10936 }
michael@0 10937 }
michael@0 10938
michael@0 10939 FCItemIterator spaceEndIter(iter);
michael@0 10940 // Advance spaceEndIter past any whitespace
michael@0 10941 bool trailingSpaces = spaceEndIter.SkipWhitespace(aState);
michael@0 10942
michael@0 10943 bool okToDrop;
michael@0 10944 if (trailingSpaces) {
michael@0 10945 // Trailing whitespace. How to handle this depeds on aIsAppend, our
michael@0 10946 // next sibling and aFrame. See the long comment above.
michael@0 10947 okToDrop = aIsAppend && !nextSibling;
michael@0 10948 if (!okToDrop) {
michael@0 10949 if (!nextSibling) {
michael@0 10950 // Try to find one after all
michael@0 10951 nsIFrame* parentNextCont = aFrame->GetNextContinuation();
michael@0 10952 while (parentNextCont) {
michael@0 10953 nextSibling = parentNextCont->GetFirstPrincipalChild();
michael@0 10954 if (nextSibling) {
michael@0 10955 break;
michael@0 10956 }
michael@0 10957 parentNextCont = parentNextCont->GetNextContinuation();
michael@0 10958 }
michael@0 10959 }
michael@0 10960
michael@0 10961 okToDrop = (nextSibling && !IsTablePseudo(nextSibling)) ||
michael@0 10962 (!nextSibling && !IsTablePseudo(aFrame));
michael@0 10963 }
michael@0 10964 #ifdef DEBUG
michael@0 10965 else {
michael@0 10966 NS_ASSERTION(!IsTablePseudo(aFrame), "How did that happen?");
michael@0 10967 }
michael@0 10968 #endif
michael@0 10969 } else {
michael@0 10970 okToDrop = (spaceEndIter.item().DesiredParentType() == parentType);
michael@0 10971 }
michael@0 10972
michael@0 10973 if (okToDrop) {
michael@0 10974 iter.DeleteItemsTo(spaceEndIter);
michael@0 10975 } else {
michael@0 10976 // We're done: we don't want to drop the whitespace, and it has the
michael@0 10977 // wrong parent type.
michael@0 10978 break;
michael@0 10979 }
michael@0 10980
michael@0 10981 // Now loop, since |iter| points to item right after the whitespace we
michael@0 10982 // removed.
michael@0 10983 } while (!iter.IsDone());
michael@0 10984 }
michael@0 10985
michael@0 10986 // We might be able to figure out some sort of optimizations here, but they
michael@0 10987 // would have to depend on having a correct aPrevSibling and a correct next
michael@0 10988 // sibling. For example, we can probably avoid reframing if none of
michael@0 10989 // aFrame, aPrevSibling, and next sibling are table pseudo-frames. But it
michael@0 10990 // doesn't seem worth it to worry about that for now, especially since we
michael@0 10991 // in fact do not have a reliable aPrevSibling, nor any next sibling, in
michael@0 10992 // this method.
michael@0 10993
michael@0 10994 // aItems might have changed, so recheck the parent type thing. In fact,
michael@0 10995 // it might be empty, so recheck that too.
michael@0 10996 if (aItems.IsEmpty()) {
michael@0 10997 return false;
michael@0 10998 }
michael@0 10999
michael@0 11000 if (!aItems.AllWantParentType(parentType)) {
michael@0 11001 // Reframing aFrame->GetContent() is good enough, since the content of
michael@0 11002 // table pseudo-frames is the ancestor content.
michael@0 11003 RecreateFramesForContent(aFrame->GetContent(), true);
michael@0 11004 return true;
michael@0 11005 }
michael@0 11006 }
michael@0 11007
michael@0 11008 // Now we have several cases involving {ib} splits. Put them all in a
michael@0 11009 // do/while with breaks to take us to the "go and reconstruct" code.
michael@0 11010 do {
michael@0 11011 if (IsInlineFrame(aFrame)) {
michael@0 11012 if (aItems.AreAllItemsInline()) {
michael@0 11013 // We can just put the kids in.
michael@0 11014 return false;
michael@0 11015 }
michael@0 11016
michael@0 11017 if (!IsFramePartOfIBSplit(aFrame)) {
michael@0 11018 // Need to go ahead and reconstruct.
michael@0 11019 break;
michael@0 11020 }
michael@0 11021
michael@0 11022 // Now we're adding kids including some blocks to an inline part of an
michael@0 11023 // {ib} split. If we plan to call AppendFrames, and don't have a next
michael@0 11024 // sibling for the new frames, and our parent is the last continuation of
michael@0 11025 // the last part of the {ib} split, and the same is true of all our
michael@0 11026 // ancestor inlines (they have no following continuations and they're the
michael@0 11027 // last part of their {ib} splits and we'd be adding to the end for all
michael@0 11028 // of them), then AppendFrames will handle things for us. Bail out in
michael@0 11029 // that case.
michael@0 11030 if (aIsAppend && IsSafeToAppendToIBSplitInline(aFrame, nextSibling)) {
michael@0 11031 return false;
michael@0 11032 }
michael@0 11033
michael@0 11034 // Need to reconstruct.
michael@0 11035 break;
michael@0 11036 }
michael@0 11037
michael@0 11038 // Now we know we have a block parent. If it's not part of an
michael@0 11039 // ib-split, we're all set.
michael@0 11040 if (!IsFramePartOfIBSplit(aFrame)) {
michael@0 11041 return false;
michael@0 11042 }
michael@0 11043
michael@0 11044 // We're adding some kids to a block part of an {ib} split. If all the
michael@0 11045 // kids are blocks, we don't need to reconstruct.
michael@0 11046 if (aItems.AreAllItemsBlock()) {
michael@0 11047 return false;
michael@0 11048 }
michael@0 11049
michael@0 11050 // We might have some inline kids for this block. Just reconstruct.
michael@0 11051 break;
michael@0 11052 } while (0);
michael@0 11053
michael@0 11054 // If we don't have a containing block, start with aFrame and look for one.
michael@0 11055 if (!aContainingBlock) {
michael@0 11056 aContainingBlock = aFrame;
michael@0 11057 }
michael@0 11058
michael@0 11059 // To find the right block to reframe, just walk up the tree until we find a
michael@0 11060 // frame that is:
michael@0 11061 // 1) Not part of an IB split
michael@0 11062 // 2) Not a pseudo-frame
michael@0 11063 // 3) Not an inline frame
michael@0 11064 // We're guaranteed to find one, since nsStyleContext::ApplyStyleFixups
michael@0 11065 // enforces that the root is display:none, display:table, or display:block.
michael@0 11066 // Note that walking up "too far" is OK in terms of correctness, even if it
michael@0 11067 // might be a little inefficient. This is why we walk out of all
michael@0 11068 // pseudo-frames -- telling which ones are or are not OK to walk out of is
michael@0 11069 // too hard (and I suspect that we do in fact need to walk out of all of
michael@0 11070 // them).
michael@0 11071 while (IsFramePartOfIBSplit(aContainingBlock) ||
michael@0 11072 aContainingBlock->IsInlineOutside() ||
michael@0 11073 aContainingBlock->StyleContext()->GetPseudo()) {
michael@0 11074 aContainingBlock = aContainingBlock->GetParent();
michael@0 11075 NS_ASSERTION(aContainingBlock,
michael@0 11076 "Must have non-inline, non-ib-split, non-pseudo frame as "
michael@0 11077 "root (or child of root, for a table root)!");
michael@0 11078 }
michael@0 11079
michael@0 11080 // Tell parent of the containing block to reformulate the
michael@0 11081 // entire block. This is painful and definitely not optimal
michael@0 11082 // but it will *always* get the right answer.
michael@0 11083
michael@0 11084 nsIContent *blockContent = aContainingBlock->GetContent();
michael@0 11085 #ifdef DEBUG
michael@0 11086 if (gNoisyContentUpdates) {
michael@0 11087 printf("nsCSSFrameConstructor::WipeContainingBlock: blockContent=%p\n",
michael@0 11088 static_cast<void*>(blockContent));
michael@0 11089 }
michael@0 11090 #endif
michael@0 11091 RecreateFramesForContent(blockContent, true);
michael@0 11092 return true;
michael@0 11093 }
michael@0 11094
michael@0 11095 nsresult
michael@0 11096 nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame)
michael@0 11097 {
michael@0 11098
michael@0 11099 #ifdef DEBUG
michael@0 11100 // ReframeContainingBlock is a NASTY routine, it causes terrible performance problems
michael@0 11101 // so I want to see when it is happening! Unfortunately, it is happening way to often because
michael@0 11102 // so much content on the web causes block-in-inline frame situations and we handle them
michael@0 11103 // very poorly
michael@0 11104 if (gNoisyContentUpdates) {
michael@0 11105 printf("nsCSSFrameConstructor::ReframeContainingBlock frame=%p\n",
michael@0 11106 static_cast<void*>(aFrame));
michael@0 11107 }
michael@0 11108 #endif
michael@0 11109
michael@0 11110 // XXXbz how exactly would we get here while isReflowing anyway? Should this
michael@0 11111 // whole test be ifdef DEBUG?
michael@0 11112 if (mPresShell->IsReflowLocked()) {
michael@0 11113 // don't ReframeContainingBlock, this will result in a crash
michael@0 11114 // if we remove a tree that's in reflow - see bug 121368 for testcase
michael@0 11115 NS_ERROR("Atemptted to nsCSSFrameConstructor::ReframeContainingBlock during a Reflow!!!");
michael@0 11116 return NS_OK;
michael@0 11117 }
michael@0 11118
michael@0 11119 // Get the first "normal" ancestor of the target frame.
michael@0 11120 nsIFrame* containingBlock = GetIBContainingBlockFor(aFrame);
michael@0 11121 if (containingBlock) {
michael@0 11122 // From here we look for the containing block in case the target
michael@0 11123 // frame is already a block (which can happen when an inline frame
michael@0 11124 // wraps some of its content in an anonymous block; see
michael@0 11125 // ConstructInline)
michael@0 11126
michael@0 11127 // NOTE: We used to get the FloatContainingBlock here, but it was often wrong.
michael@0 11128 // GetIBContainingBlock works much better and provides the correct container in all cases
michael@0 11129 // so GetFloatContainingBlock(aFrame) has been removed
michael@0 11130
michael@0 11131 // And get the containingBlock's content
michael@0 11132 nsCOMPtr<nsIContent> blockContent = containingBlock->GetContent();
michael@0 11133 if (blockContent) {
michael@0 11134 #ifdef DEBUG
michael@0 11135 if (gNoisyContentUpdates) {
michael@0 11136 printf(" ==> blockContent=%p\n", static_cast<void*>(blockContent));
michael@0 11137 }
michael@0 11138 #endif
michael@0 11139 return RecreateFramesForContent(blockContent, true);
michael@0 11140 }
michael@0 11141 }
michael@0 11142
michael@0 11143 // If we get here, we're screwed!
michael@0 11144 return RecreateFramesForContent(mPresShell->GetDocument()->GetRootElement(),
michael@0 11145 true);
michael@0 11146 }
michael@0 11147
michael@0 11148 nsresult
michael@0 11149 nsCSSFrameConstructor::GenerateChildFrames(nsIFrame* aFrame)
michael@0 11150 {
michael@0 11151 {
michael@0 11152 nsAutoScriptBlocker scriptBlocker;
michael@0 11153 BeginUpdate();
michael@0 11154
michael@0 11155 nsFrameItems childItems;
michael@0 11156 nsFrameConstructorState state(mPresShell, nullptr, nullptr, nullptr);
michael@0 11157 // We don't have a parent frame with a pending binding constructor here,
michael@0 11158 // so no need to worry about ordering of the kids' constructors with it.
michael@0 11159 // Pass null for the PendingBinding.
michael@0 11160 ProcessChildren(state, aFrame->GetContent(), aFrame->StyleContext(),
michael@0 11161 aFrame, false, childItems, false,
michael@0 11162 nullptr);
michael@0 11163
michael@0 11164 aFrame->SetInitialChildList(kPrincipalList, childItems);
michael@0 11165
michael@0 11166 EndUpdate();
michael@0 11167 }
michael@0 11168
michael@0 11169 #ifdef ACCESSIBILITY
michael@0 11170 nsAccessibilityService* accService = nsIPresShell::AccService();
michael@0 11171 if (accService) {
michael@0 11172 nsIContent* container = aFrame->GetContent();
michael@0 11173 nsIContent* child = container->GetFirstChild();
michael@0 11174 if (child) {
michael@0 11175 accService->ContentRangeInserted(mPresShell, container, child, nullptr);
michael@0 11176 }
michael@0 11177 }
michael@0 11178 #endif
michael@0 11179
michael@0 11180 // call XBL constructors after the frames are created
michael@0 11181 mPresShell->GetDocument()->BindingManager()->ProcessAttachedQueue();
michael@0 11182
michael@0 11183 return NS_OK;
michael@0 11184 }
michael@0 11185
michael@0 11186 //////////////////////////////////////////////////////////
michael@0 11187 // nsCSSFrameConstructor::FrameConstructionItem methods //
michael@0 11188 //////////////////////////////////////////////////////////
michael@0 11189 bool
michael@0 11190 nsCSSFrameConstructor::
michael@0 11191 FrameConstructionItem::IsWhitespace(nsFrameConstructorState& aState) const
michael@0 11192 {
michael@0 11193 NS_PRECONDITION(aState.mCreatingExtraFrames ||
michael@0 11194 !mContent->GetPrimaryFrame(), "How did that happen?");
michael@0 11195 if (!mIsText) {
michael@0 11196 return false;
michael@0 11197 }
michael@0 11198 mContent->SetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
michael@0 11199 NS_REFRAME_IF_WHITESPACE);
michael@0 11200 return mContent->TextIsOnlyWhitespace();
michael@0 11201 }
michael@0 11202
michael@0 11203 //////////////////////////////////////////////////////////////
michael@0 11204 // nsCSSFrameConstructor::FrameConstructionItemList methods //
michael@0 11205 //////////////////////////////////////////////////////////////
michael@0 11206 void
michael@0 11207 nsCSSFrameConstructor::FrameConstructionItemList::
michael@0 11208 AdjustCountsForItem(FrameConstructionItem* aItem, int32_t aDelta)
michael@0 11209 {
michael@0 11210 NS_PRECONDITION(aDelta == 1 || aDelta == -1, "Unexpected delta");
michael@0 11211 mItemCount += aDelta;
michael@0 11212 if (aItem->mIsAllInline) {
michael@0 11213 mInlineCount += aDelta;
michael@0 11214 }
michael@0 11215 if (aItem->mIsBlock) {
michael@0 11216 mBlockCount += aDelta;
michael@0 11217 }
michael@0 11218 if (aItem->mIsLineParticipant) {
michael@0 11219 mLineParticipantCount += aDelta;
michael@0 11220 }
michael@0 11221 mDesiredParentCounts[aItem->DesiredParentType()] += aDelta;
michael@0 11222 }
michael@0 11223
michael@0 11224 ////////////////////////////////////////////////////////////////////////
michael@0 11225 // nsCSSFrameConstructor::FrameConstructionItemList::Iterator methods //
michael@0 11226 ////////////////////////////////////////////////////////////////////////
michael@0 11227 inline bool
michael@0 11228 nsCSSFrameConstructor::FrameConstructionItemList::
michael@0 11229 Iterator::SkipItemsWantingParentType(ParentType aParentType)
michael@0 11230 {
michael@0 11231 NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
michael@0 11232 while (item().DesiredParentType() == aParentType) {
michael@0 11233 Next();
michael@0 11234 if (IsDone()) {
michael@0 11235 return true;
michael@0 11236 }
michael@0 11237 }
michael@0 11238 return false;
michael@0 11239 }
michael@0 11240
michael@0 11241 bool
michael@0 11242 nsCSSFrameConstructor::FrameConstructionItem::
michael@0 11243 NeedsAnonFlexItem(const nsFrameConstructorState& aState)
michael@0 11244 {
michael@0 11245 if (mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) {
michael@0 11246 // This will be an inline non-replaced box.
michael@0 11247 return true;
michael@0 11248 }
michael@0 11249
michael@0 11250 if (!(mFCData->mBits & FCDATA_DISALLOW_OUT_OF_FLOW) &&
michael@0 11251 aState.GetGeometricParent(mStyleContext->StyleDisplay(), nullptr)) {
michael@0 11252 // We're abspos or fixedpos, which means we'll spawn a placeholder which
michael@0 11253 // we'll need to wrap in an anonymous flex item. So, we just treat
michael@0 11254 // _this_ frame as if _it_ needs to be wrapped in an anonymous flex item,
michael@0 11255 // and then when we spawn the placeholder, it'll end up in the right spot.
michael@0 11256 return true;
michael@0 11257 }
michael@0 11258
michael@0 11259 return false;
michael@0 11260 }
michael@0 11261
michael@0 11262 inline bool
michael@0 11263 nsCSSFrameConstructor::FrameConstructionItemList::
michael@0 11264 Iterator::SkipItemsThatNeedAnonFlexItem(
michael@0 11265 const nsFrameConstructorState& aState)
michael@0 11266 {
michael@0 11267 NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
michael@0 11268 while (item().NeedsAnonFlexItem(aState)) {
michael@0 11269 Next();
michael@0 11270 if (IsDone()) {
michael@0 11271 return true;
michael@0 11272 }
michael@0 11273 }
michael@0 11274 return false;
michael@0 11275 }
michael@0 11276
michael@0 11277 inline bool
michael@0 11278 nsCSSFrameConstructor::FrameConstructionItemList::
michael@0 11279 Iterator::SkipItemsThatDontNeedAnonFlexItem(
michael@0 11280 const nsFrameConstructorState& aState)
michael@0 11281 {
michael@0 11282 NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
michael@0 11283 while (!(item().NeedsAnonFlexItem(aState))) {
michael@0 11284 Next();
michael@0 11285 if (IsDone()) {
michael@0 11286 return true;
michael@0 11287 }
michael@0 11288 }
michael@0 11289 return false;
michael@0 11290 }
michael@0 11291
michael@0 11292 inline bool
michael@0 11293 nsCSSFrameConstructor::FrameConstructionItemList::
michael@0 11294 Iterator::SkipWhitespace(nsFrameConstructorState& aState)
michael@0 11295 {
michael@0 11296 NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
michael@0 11297 NS_PRECONDITION(item().IsWhitespace(aState), "Not pointing to whitespace?");
michael@0 11298 do {
michael@0 11299 Next();
michael@0 11300 if (IsDone()) {
michael@0 11301 return true;
michael@0 11302 }
michael@0 11303 } while (item().IsWhitespace(aState));
michael@0 11304
michael@0 11305 return false;
michael@0 11306 }
michael@0 11307
michael@0 11308 void
michael@0 11309 nsCSSFrameConstructor::FrameConstructionItemList::
michael@0 11310 Iterator::AppendItemToList(FrameConstructionItemList& aTargetList)
michael@0 11311 {
michael@0 11312 NS_ASSERTION(&aTargetList != &mList, "Unexpected call");
michael@0 11313 NS_PRECONDITION(!IsDone(), "should not be done");
michael@0 11314
michael@0 11315 FrameConstructionItem* item = ToItem(mCurrent);
michael@0 11316 Next();
michael@0 11317 PR_REMOVE_LINK(item);
michael@0 11318 PR_APPEND_LINK(item, &aTargetList.mItems);
michael@0 11319
michael@0 11320 mList.AdjustCountsForItem(item, -1);
michael@0 11321 aTargetList.AdjustCountsForItem(item, 1);
michael@0 11322 }
michael@0 11323
michael@0 11324 void
michael@0 11325 nsCSSFrameConstructor::FrameConstructionItemList::
michael@0 11326 Iterator::AppendItemsToList(const Iterator& aEnd,
michael@0 11327 FrameConstructionItemList& aTargetList)
michael@0 11328 {
michael@0 11329 NS_ASSERTION(&aTargetList != &mList, "Unexpected call");
michael@0 11330 NS_PRECONDITION(mEnd == aEnd.mEnd, "end iterator for some other list?");
michael@0 11331
michael@0 11332 // We can't just move our guts to the other list if it already has
michael@0 11333 // some information or if we're not moving our entire list.
michael@0 11334 if (!AtStart() || !aEnd.IsDone() || !aTargetList.IsEmpty() ||
michael@0 11335 !aTargetList.mUndisplayedItems.IsEmpty()) {
michael@0 11336 do {
michael@0 11337 AppendItemToList(aTargetList);
michael@0 11338 } while (*this != aEnd);
michael@0 11339 return;
michael@0 11340 }
michael@0 11341
michael@0 11342 // move over the list of items
michael@0 11343 PR_INSERT_AFTER(&aTargetList.mItems, &mList.mItems);
michael@0 11344 // Need to init when we remove to makd ~FrameConstructionItemList work right.
michael@0 11345 PR_REMOVE_AND_INIT_LINK(&mList.mItems);
michael@0 11346
michael@0 11347 // Copy over the various counters
michael@0 11348 aTargetList.mInlineCount = mList.mInlineCount;
michael@0 11349 aTargetList.mBlockCount = mList.mBlockCount;
michael@0 11350 aTargetList.mLineParticipantCount = mList.mLineParticipantCount;
michael@0 11351 aTargetList.mItemCount = mList.mItemCount;
michael@0 11352 memcpy(aTargetList.mDesiredParentCounts, mList.mDesiredParentCounts,
michael@0 11353 sizeof(aTargetList.mDesiredParentCounts));
michael@0 11354
michael@0 11355 // Swap out undisplayed item arrays, before we nuke the array on our end
michael@0 11356 aTargetList.mUndisplayedItems.SwapElements(mList.mUndisplayedItems);
michael@0 11357
michael@0 11358 // reset mList
michael@0 11359 mList.~FrameConstructionItemList();
michael@0 11360 new (&mList) FrameConstructionItemList();
michael@0 11361
michael@0 11362 // Point ourselves to aEnd, as advertised
michael@0 11363 mCurrent = mEnd = &mList.mItems;
michael@0 11364 NS_POSTCONDITION(*this == aEnd, "How did that happen?");
michael@0 11365 }
michael@0 11366
michael@0 11367 void
michael@0 11368 nsCSSFrameConstructor::FrameConstructionItemList::
michael@0 11369 Iterator::InsertItem(FrameConstructionItem* aItem)
michael@0 11370 {
michael@0 11371 // Just insert the item before us. There's no magic here.
michael@0 11372 PR_INSERT_BEFORE(aItem, mCurrent);
michael@0 11373 mList.AdjustCountsForItem(aItem, 1);
michael@0 11374
michael@0 11375 NS_POSTCONDITION(PR_NEXT_LINK(aItem) == mCurrent, "How did that happen?");
michael@0 11376 }
michael@0 11377
michael@0 11378 void
michael@0 11379 nsCSSFrameConstructor::FrameConstructionItemList::
michael@0 11380 Iterator::DeleteItemsTo(const Iterator& aEnd)
michael@0 11381 {
michael@0 11382 NS_PRECONDITION(mEnd == aEnd.mEnd, "end iterator for some other list?");
michael@0 11383 NS_PRECONDITION(*this != aEnd, "Shouldn't be at aEnd yet");
michael@0 11384
michael@0 11385 do {
michael@0 11386 NS_ASSERTION(!IsDone(), "Ran off end of list?");
michael@0 11387 FrameConstructionItem* item = ToItem(mCurrent);
michael@0 11388 Next();
michael@0 11389 PR_REMOVE_LINK(item);
michael@0 11390 mList.AdjustCountsForItem(item, -1);
michael@0 11391 delete item;
michael@0 11392 } while (*this != aEnd);
michael@0 11393 }

mercurial