Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | /* |
michael@0 | 7 | * rendering object for the point that anchors out-of-flow rendering |
michael@0 | 8 | * objects such as floats and absolutely positioned elements |
michael@0 | 9 | */ |
michael@0 | 10 | |
michael@0 | 11 | #include "nsPlaceholderFrame.h" |
michael@0 | 12 | |
michael@0 | 13 | #include "nsDisplayList.h" |
michael@0 | 14 | #include "nsFrameManager.h" |
michael@0 | 15 | #include "nsLayoutUtils.h" |
michael@0 | 16 | #include "nsPresContext.h" |
michael@0 | 17 | #include "nsRenderingContext.h" |
michael@0 | 18 | #include "nsIFrameInlines.h" |
michael@0 | 19 | |
michael@0 | 20 | nsIFrame* |
michael@0 | 21 | NS_NewPlaceholderFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, |
michael@0 | 22 | nsFrameState aTypeBit) |
michael@0 | 23 | { |
michael@0 | 24 | return new (aPresShell) nsPlaceholderFrame(aContext, aTypeBit); |
michael@0 | 25 | } |
michael@0 | 26 | |
michael@0 | 27 | NS_IMPL_FRAMEARENA_HELPERS(nsPlaceholderFrame) |
michael@0 | 28 | |
michael@0 | 29 | #ifdef DEBUG |
michael@0 | 30 | NS_QUERYFRAME_HEAD(nsPlaceholderFrame) |
michael@0 | 31 | NS_QUERYFRAME_ENTRY(nsPlaceholderFrame) |
michael@0 | 32 | NS_QUERYFRAME_TAIL_INHERITING(nsFrame) |
michael@0 | 33 | #endif |
michael@0 | 34 | |
michael@0 | 35 | /* virtual */ nsSize |
michael@0 | 36 | nsPlaceholderFrame::GetMinSize(nsBoxLayoutState& aBoxLayoutState) |
michael@0 | 37 | { |
michael@0 | 38 | nsSize size(0, 0); |
michael@0 | 39 | DISPLAY_MIN_SIZE(this, size); |
michael@0 | 40 | return size; |
michael@0 | 41 | } |
michael@0 | 42 | |
michael@0 | 43 | /* virtual */ nsSize |
michael@0 | 44 | nsPlaceholderFrame::GetPrefSize(nsBoxLayoutState& aBoxLayoutState) |
michael@0 | 45 | { |
michael@0 | 46 | nsSize size(0, 0); |
michael@0 | 47 | DISPLAY_PREF_SIZE(this, size); |
michael@0 | 48 | return size; |
michael@0 | 49 | } |
michael@0 | 50 | |
michael@0 | 51 | /* virtual */ nsSize |
michael@0 | 52 | nsPlaceholderFrame::GetMaxSize(nsBoxLayoutState& aBoxLayoutState) |
michael@0 | 53 | { |
michael@0 | 54 | nsSize size(NS_INTRINSICSIZE, NS_INTRINSICSIZE); |
michael@0 | 55 | DISPLAY_MAX_SIZE(this, size); |
michael@0 | 56 | return size; |
michael@0 | 57 | } |
michael@0 | 58 | |
michael@0 | 59 | /* virtual */ void |
michael@0 | 60 | nsPlaceholderFrame::AddInlineMinWidth(nsRenderingContext* aRenderingContext, |
michael@0 | 61 | nsIFrame::InlineMinWidthData* aData) |
michael@0 | 62 | { |
michael@0 | 63 | // Override AddInlineMinWith so that *nothing* happens. In |
michael@0 | 64 | // particular, we don't want to zero out |aData->trailingWhitespace|, |
michael@0 | 65 | // since nsLineLayout skips placeholders when trimming trailing |
michael@0 | 66 | // whitespace, and we don't want to set aData->skipWhitespace to |
michael@0 | 67 | // false. |
michael@0 | 68 | |
michael@0 | 69 | // ...but push floats onto the list |
michael@0 | 70 | if (mOutOfFlowFrame->IsFloating()) { |
michael@0 | 71 | nscoord floatWidth = |
michael@0 | 72 | nsLayoutUtils::IntrinsicForContainer(aRenderingContext, |
michael@0 | 73 | mOutOfFlowFrame, |
michael@0 | 74 | nsLayoutUtils::MIN_WIDTH); |
michael@0 | 75 | aData->floats.AppendElement( |
michael@0 | 76 | InlineIntrinsicWidthData::FloatInfo(mOutOfFlowFrame, floatWidth)); |
michael@0 | 77 | } |
michael@0 | 78 | } |
michael@0 | 79 | |
michael@0 | 80 | /* virtual */ void |
michael@0 | 81 | nsPlaceholderFrame::AddInlinePrefWidth(nsRenderingContext* aRenderingContext, |
michael@0 | 82 | nsIFrame::InlinePrefWidthData* aData) |
michael@0 | 83 | { |
michael@0 | 84 | // Override AddInlinePrefWith so that *nothing* happens. In |
michael@0 | 85 | // particular, we don't want to zero out |aData->trailingWhitespace|, |
michael@0 | 86 | // since nsLineLayout skips placeholders when trimming trailing |
michael@0 | 87 | // whitespace, and we don't want to set aData->skipWhitespace to |
michael@0 | 88 | // false. |
michael@0 | 89 | |
michael@0 | 90 | // ...but push floats onto the list |
michael@0 | 91 | if (mOutOfFlowFrame->IsFloating()) { |
michael@0 | 92 | nscoord floatWidth = |
michael@0 | 93 | nsLayoutUtils::IntrinsicForContainer(aRenderingContext, |
michael@0 | 94 | mOutOfFlowFrame, |
michael@0 | 95 | nsLayoutUtils::PREF_WIDTH); |
michael@0 | 96 | aData->floats.AppendElement( |
michael@0 | 97 | InlineIntrinsicWidthData::FloatInfo(mOutOfFlowFrame, floatWidth)); |
michael@0 | 98 | } |
michael@0 | 99 | } |
michael@0 | 100 | |
michael@0 | 101 | nsresult |
michael@0 | 102 | nsPlaceholderFrame::Reflow(nsPresContext* aPresContext, |
michael@0 | 103 | nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 104 | const nsHTMLReflowState& aReflowState, |
michael@0 | 105 | nsReflowStatus& aStatus) |
michael@0 | 106 | { |
michael@0 | 107 | #ifdef DEBUG |
michael@0 | 108 | // We should be getting reflowed before our out-of-flow. |
michael@0 | 109 | // If this is our first reflow, and our out-of-flow has already received its |
michael@0 | 110 | // first reflow (before us), complain. |
michael@0 | 111 | // XXXdholbert This "look for a previous continuation or IB-split sibling" |
michael@0 | 112 | // code could use nsLayoutUtils::GetPrevContinuationOrIBSplitSibling(), if |
michael@0 | 113 | // we ever add a function like that. (We currently have a "Next" version.) |
michael@0 | 114 | if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) && |
michael@0 | 115 | !(mOutOfFlowFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) { |
michael@0 | 116 | |
michael@0 | 117 | // Unfortunately, this can currently happen when the placeholder is in a |
michael@0 | 118 | // later continuation or later IB-split sibling than its out-of-flow (as |
michael@0 | 119 | // is the case in some of our existing unit tests). So for now, in that |
michael@0 | 120 | // case, we'll warn instead of asserting. |
michael@0 | 121 | bool isInContinuationOrIBSplit = false; |
michael@0 | 122 | nsIFrame* ancestor = this; |
michael@0 | 123 | while ((ancestor = ancestor->GetParent())) { |
michael@0 | 124 | if (ancestor->GetPrevContinuation() || |
michael@0 | 125 | ancestor->Properties().Get(IBSplitPrevSibling())) { |
michael@0 | 126 | isInContinuationOrIBSplit = true; |
michael@0 | 127 | break; |
michael@0 | 128 | } |
michael@0 | 129 | } |
michael@0 | 130 | |
michael@0 | 131 | if (isInContinuationOrIBSplit) { |
michael@0 | 132 | NS_WARNING("Out-of-flow frame got reflowed before its placeholder"); |
michael@0 | 133 | } else { |
michael@0 | 134 | NS_ERROR("Out-of-flow frame got reflowed before its placeholder"); |
michael@0 | 135 | } |
michael@0 | 136 | } |
michael@0 | 137 | #endif |
michael@0 | 138 | |
michael@0 | 139 | DO_GLOBAL_REFLOW_COUNT("nsPlaceholderFrame"); |
michael@0 | 140 | DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); |
michael@0 | 141 | aDesiredSize.Width() = 0; |
michael@0 | 142 | aDesiredSize.Height() = 0; |
michael@0 | 143 | |
michael@0 | 144 | aStatus = NS_FRAME_COMPLETE; |
michael@0 | 145 | NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); |
michael@0 | 146 | return NS_OK; |
michael@0 | 147 | } |
michael@0 | 148 | |
michael@0 | 149 | void |
michael@0 | 150 | nsPlaceholderFrame::DestroyFrom(nsIFrame* aDestructRoot) |
michael@0 | 151 | { |
michael@0 | 152 | nsIFrame* oof = mOutOfFlowFrame; |
michael@0 | 153 | if (oof) { |
michael@0 | 154 | // Unregister out-of-flow frame |
michael@0 | 155 | nsFrameManager* fm = PresContext()->GetPresShell()->FrameManager(); |
michael@0 | 156 | fm->UnregisterPlaceholderFrame(this); |
michael@0 | 157 | mOutOfFlowFrame = nullptr; |
michael@0 | 158 | // If aDestructRoot is not an ancestor of the out-of-flow frame, |
michael@0 | 159 | // then call RemoveFrame on it here. |
michael@0 | 160 | // Also destroy it here if it's a popup frame. (Bug 96291) |
michael@0 | 161 | if ((GetStateBits() & PLACEHOLDER_FOR_POPUP) || |
michael@0 | 162 | !nsLayoutUtils::IsProperAncestorFrame(aDestructRoot, oof)) { |
michael@0 | 163 | ChildListID listId = nsLayoutUtils::GetChildListNameFor(oof); |
michael@0 | 164 | fm->RemoveFrame(listId, oof); |
michael@0 | 165 | } |
michael@0 | 166 | // else oof will be destroyed by its parent |
michael@0 | 167 | } |
michael@0 | 168 | |
michael@0 | 169 | nsFrame::DestroyFrom(aDestructRoot); |
michael@0 | 170 | } |
michael@0 | 171 | |
michael@0 | 172 | nsIAtom* |
michael@0 | 173 | nsPlaceholderFrame::GetType() const |
michael@0 | 174 | { |
michael@0 | 175 | return nsGkAtoms::placeholderFrame; |
michael@0 | 176 | } |
michael@0 | 177 | |
michael@0 | 178 | /* virtual */ bool |
michael@0 | 179 | nsPlaceholderFrame::CanContinueTextRun() const |
michael@0 | 180 | { |
michael@0 | 181 | if (!mOutOfFlowFrame) { |
michael@0 | 182 | return false; |
michael@0 | 183 | } |
michael@0 | 184 | // first-letter frames can continue text runs, and placeholders for floated |
michael@0 | 185 | // first-letter frames can too |
michael@0 | 186 | return mOutOfFlowFrame->CanContinueTextRun(); |
michael@0 | 187 | } |
michael@0 | 188 | |
michael@0 | 189 | nsIFrame* |
michael@0 | 190 | nsPlaceholderFrame::GetParentStyleContextFrame() const |
michael@0 | 191 | { |
michael@0 | 192 | NS_PRECONDITION(GetParent(), "How can we not have a parent here?"); |
michael@0 | 193 | |
michael@0 | 194 | // Lie about our pseudo so we can step out of all anon boxes and |
michael@0 | 195 | // pseudo-elements. The other option would be to reimplement the |
michael@0 | 196 | // {ib} split gunk here. |
michael@0 | 197 | return CorrectStyleParentFrame(GetParent(), nsGkAtoms::placeholderFrame); |
michael@0 | 198 | } |
michael@0 | 199 | |
michael@0 | 200 | |
michael@0 | 201 | #ifdef DEBUG |
michael@0 | 202 | static void |
michael@0 | 203 | PaintDebugPlaceholder(nsIFrame* aFrame, nsRenderingContext* aCtx, |
michael@0 | 204 | const nsRect& aDirtyRect, nsPoint aPt) |
michael@0 | 205 | { |
michael@0 | 206 | aCtx->SetColor(NS_RGB(0, 255, 255)); |
michael@0 | 207 | nscoord x = nsPresContext::CSSPixelsToAppUnits(-5); |
michael@0 | 208 | aCtx->FillRect(aPt.x + x, aPt.y, |
michael@0 | 209 | nsPresContext::CSSPixelsToAppUnits(13), |
michael@0 | 210 | nsPresContext::CSSPixelsToAppUnits(3)); |
michael@0 | 211 | nscoord y = nsPresContext::CSSPixelsToAppUnits(-10); |
michael@0 | 212 | aCtx->FillRect(aPt.x, aPt.y + y, |
michael@0 | 213 | nsPresContext::CSSPixelsToAppUnits(3), |
michael@0 | 214 | nsPresContext::CSSPixelsToAppUnits(10)); |
michael@0 | 215 | } |
michael@0 | 216 | #endif // DEBUG |
michael@0 | 217 | |
michael@0 | 218 | #if defined(DEBUG) || (defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF)) |
michael@0 | 219 | |
michael@0 | 220 | void |
michael@0 | 221 | nsPlaceholderFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
michael@0 | 222 | const nsRect& aDirtyRect, |
michael@0 | 223 | const nsDisplayListSet& aLists) |
michael@0 | 224 | { |
michael@0 | 225 | DO_GLOBAL_REFLOW_COUNT_DSP("nsPlaceholderFrame"); |
michael@0 | 226 | |
michael@0 | 227 | #ifdef DEBUG |
michael@0 | 228 | if (GetShowFrameBorders()) { |
michael@0 | 229 | aLists.Outlines()->AppendNewToTop( |
michael@0 | 230 | new (aBuilder) nsDisplayGeneric(aBuilder, this, PaintDebugPlaceholder, |
michael@0 | 231 | "DebugPlaceholder", |
michael@0 | 232 | nsDisplayItem::TYPE_DEBUG_PLACEHOLDER)); |
michael@0 | 233 | } |
michael@0 | 234 | #endif |
michael@0 | 235 | } |
michael@0 | 236 | #endif // DEBUG || (MOZ_REFLOW_PERF_DSP && MOZ_REFLOW_PERF) |
michael@0 | 237 | |
michael@0 | 238 | #ifdef DEBUG_FRAME_DUMP |
michael@0 | 239 | nsresult |
michael@0 | 240 | nsPlaceholderFrame::GetFrameName(nsAString& aResult) const |
michael@0 | 241 | { |
michael@0 | 242 | return MakeFrameName(NS_LITERAL_STRING("Placeholder"), aResult); |
michael@0 | 243 | } |
michael@0 | 244 | |
michael@0 | 245 | void |
michael@0 | 246 | nsPlaceholderFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const |
michael@0 | 247 | { |
michael@0 | 248 | nsCString str; |
michael@0 | 249 | ListGeneric(str, aPrefix, aFlags); |
michael@0 | 250 | |
michael@0 | 251 | if (mOutOfFlowFrame) { |
michael@0 | 252 | str += " outOfFlowFrame="; |
michael@0 | 253 | nsFrame::ListTag(str, mOutOfFlowFrame); |
michael@0 | 254 | } |
michael@0 | 255 | fprintf_stderr(out, "%s\n", str.get()); |
michael@0 | 256 | } |
michael@0 | 257 | #endif |