Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 | } |