1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/generic/nsPlaceholderFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,257 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* 1.10 + * rendering object for the point that anchors out-of-flow rendering 1.11 + * objects such as floats and absolutely positioned elements 1.12 + */ 1.13 + 1.14 +#include "nsPlaceholderFrame.h" 1.15 + 1.16 +#include "nsDisplayList.h" 1.17 +#include "nsFrameManager.h" 1.18 +#include "nsLayoutUtils.h" 1.19 +#include "nsPresContext.h" 1.20 +#include "nsRenderingContext.h" 1.21 +#include "nsIFrameInlines.h" 1.22 + 1.23 +nsIFrame* 1.24 +NS_NewPlaceholderFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, 1.25 + nsFrameState aTypeBit) 1.26 +{ 1.27 + return new (aPresShell) nsPlaceholderFrame(aContext, aTypeBit); 1.28 +} 1.29 + 1.30 +NS_IMPL_FRAMEARENA_HELPERS(nsPlaceholderFrame) 1.31 + 1.32 +#ifdef DEBUG 1.33 +NS_QUERYFRAME_HEAD(nsPlaceholderFrame) 1.34 + NS_QUERYFRAME_ENTRY(nsPlaceholderFrame) 1.35 +NS_QUERYFRAME_TAIL_INHERITING(nsFrame) 1.36 +#endif 1.37 + 1.38 +/* virtual */ nsSize 1.39 +nsPlaceholderFrame::GetMinSize(nsBoxLayoutState& aBoxLayoutState) 1.40 +{ 1.41 + nsSize size(0, 0); 1.42 + DISPLAY_MIN_SIZE(this, size); 1.43 + return size; 1.44 +} 1.45 + 1.46 +/* virtual */ nsSize 1.47 +nsPlaceholderFrame::GetPrefSize(nsBoxLayoutState& aBoxLayoutState) 1.48 +{ 1.49 + nsSize size(0, 0); 1.50 + DISPLAY_PREF_SIZE(this, size); 1.51 + return size; 1.52 +} 1.53 + 1.54 +/* virtual */ nsSize 1.55 +nsPlaceholderFrame::GetMaxSize(nsBoxLayoutState& aBoxLayoutState) 1.56 +{ 1.57 + nsSize size(NS_INTRINSICSIZE, NS_INTRINSICSIZE); 1.58 + DISPLAY_MAX_SIZE(this, size); 1.59 + return size; 1.60 +} 1.61 + 1.62 +/* virtual */ void 1.63 +nsPlaceholderFrame::AddInlineMinWidth(nsRenderingContext* aRenderingContext, 1.64 + nsIFrame::InlineMinWidthData* aData) 1.65 +{ 1.66 + // Override AddInlineMinWith so that *nothing* happens. In 1.67 + // particular, we don't want to zero out |aData->trailingWhitespace|, 1.68 + // since nsLineLayout skips placeholders when trimming trailing 1.69 + // whitespace, and we don't want to set aData->skipWhitespace to 1.70 + // false. 1.71 + 1.72 + // ...but push floats onto the list 1.73 + if (mOutOfFlowFrame->IsFloating()) { 1.74 + nscoord floatWidth = 1.75 + nsLayoutUtils::IntrinsicForContainer(aRenderingContext, 1.76 + mOutOfFlowFrame, 1.77 + nsLayoutUtils::MIN_WIDTH); 1.78 + aData->floats.AppendElement( 1.79 + InlineIntrinsicWidthData::FloatInfo(mOutOfFlowFrame, floatWidth)); 1.80 + } 1.81 +} 1.82 + 1.83 +/* virtual */ void 1.84 +nsPlaceholderFrame::AddInlinePrefWidth(nsRenderingContext* aRenderingContext, 1.85 + nsIFrame::InlinePrefWidthData* aData) 1.86 +{ 1.87 + // Override AddInlinePrefWith so that *nothing* happens. In 1.88 + // particular, we don't want to zero out |aData->trailingWhitespace|, 1.89 + // since nsLineLayout skips placeholders when trimming trailing 1.90 + // whitespace, and we don't want to set aData->skipWhitespace to 1.91 + // false. 1.92 + 1.93 + // ...but push floats onto the list 1.94 + if (mOutOfFlowFrame->IsFloating()) { 1.95 + nscoord floatWidth = 1.96 + nsLayoutUtils::IntrinsicForContainer(aRenderingContext, 1.97 + mOutOfFlowFrame, 1.98 + nsLayoutUtils::PREF_WIDTH); 1.99 + aData->floats.AppendElement( 1.100 + InlineIntrinsicWidthData::FloatInfo(mOutOfFlowFrame, floatWidth)); 1.101 + } 1.102 +} 1.103 + 1.104 +nsresult 1.105 +nsPlaceholderFrame::Reflow(nsPresContext* aPresContext, 1.106 + nsHTMLReflowMetrics& aDesiredSize, 1.107 + const nsHTMLReflowState& aReflowState, 1.108 + nsReflowStatus& aStatus) 1.109 +{ 1.110 +#ifdef DEBUG 1.111 + // We should be getting reflowed before our out-of-flow. 1.112 + // If this is our first reflow, and our out-of-flow has already received its 1.113 + // first reflow (before us), complain. 1.114 + // XXXdholbert This "look for a previous continuation or IB-split sibling" 1.115 + // code could use nsLayoutUtils::GetPrevContinuationOrIBSplitSibling(), if 1.116 + // we ever add a function like that. (We currently have a "Next" version.) 1.117 + if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) && 1.118 + !(mOutOfFlowFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) { 1.119 + 1.120 + // Unfortunately, this can currently happen when the placeholder is in a 1.121 + // later continuation or later IB-split sibling than its out-of-flow (as 1.122 + // is the case in some of our existing unit tests). So for now, in that 1.123 + // case, we'll warn instead of asserting. 1.124 + bool isInContinuationOrIBSplit = false; 1.125 + nsIFrame* ancestor = this; 1.126 + while ((ancestor = ancestor->GetParent())) { 1.127 + if (ancestor->GetPrevContinuation() || 1.128 + ancestor->Properties().Get(IBSplitPrevSibling())) { 1.129 + isInContinuationOrIBSplit = true; 1.130 + break; 1.131 + } 1.132 + } 1.133 + 1.134 + if (isInContinuationOrIBSplit) { 1.135 + NS_WARNING("Out-of-flow frame got reflowed before its placeholder"); 1.136 + } else { 1.137 + NS_ERROR("Out-of-flow frame got reflowed before its placeholder"); 1.138 + } 1.139 + } 1.140 +#endif 1.141 + 1.142 + DO_GLOBAL_REFLOW_COUNT("nsPlaceholderFrame"); 1.143 + DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); 1.144 + aDesiredSize.Width() = 0; 1.145 + aDesiredSize.Height() = 0; 1.146 + 1.147 + aStatus = NS_FRAME_COMPLETE; 1.148 + NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); 1.149 + return NS_OK; 1.150 +} 1.151 + 1.152 +void 1.153 +nsPlaceholderFrame::DestroyFrom(nsIFrame* aDestructRoot) 1.154 +{ 1.155 + nsIFrame* oof = mOutOfFlowFrame; 1.156 + if (oof) { 1.157 + // Unregister out-of-flow frame 1.158 + nsFrameManager* fm = PresContext()->GetPresShell()->FrameManager(); 1.159 + fm->UnregisterPlaceholderFrame(this); 1.160 + mOutOfFlowFrame = nullptr; 1.161 + // If aDestructRoot is not an ancestor of the out-of-flow frame, 1.162 + // then call RemoveFrame on it here. 1.163 + // Also destroy it here if it's a popup frame. (Bug 96291) 1.164 + if ((GetStateBits() & PLACEHOLDER_FOR_POPUP) || 1.165 + !nsLayoutUtils::IsProperAncestorFrame(aDestructRoot, oof)) { 1.166 + ChildListID listId = nsLayoutUtils::GetChildListNameFor(oof); 1.167 + fm->RemoveFrame(listId, oof); 1.168 + } 1.169 + // else oof will be destroyed by its parent 1.170 + } 1.171 + 1.172 + nsFrame::DestroyFrom(aDestructRoot); 1.173 +} 1.174 + 1.175 +nsIAtom* 1.176 +nsPlaceholderFrame::GetType() const 1.177 +{ 1.178 + return nsGkAtoms::placeholderFrame; 1.179 +} 1.180 + 1.181 +/* virtual */ bool 1.182 +nsPlaceholderFrame::CanContinueTextRun() const 1.183 +{ 1.184 + if (!mOutOfFlowFrame) { 1.185 + return false; 1.186 + } 1.187 + // first-letter frames can continue text runs, and placeholders for floated 1.188 + // first-letter frames can too 1.189 + return mOutOfFlowFrame->CanContinueTextRun(); 1.190 +} 1.191 + 1.192 +nsIFrame* 1.193 +nsPlaceholderFrame::GetParentStyleContextFrame() const 1.194 +{ 1.195 + NS_PRECONDITION(GetParent(), "How can we not have a parent here?"); 1.196 + 1.197 + // Lie about our pseudo so we can step out of all anon boxes and 1.198 + // pseudo-elements. The other option would be to reimplement the 1.199 + // {ib} split gunk here. 1.200 + return CorrectStyleParentFrame(GetParent(), nsGkAtoms::placeholderFrame); 1.201 +} 1.202 + 1.203 + 1.204 +#ifdef DEBUG 1.205 +static void 1.206 +PaintDebugPlaceholder(nsIFrame* aFrame, nsRenderingContext* aCtx, 1.207 + const nsRect& aDirtyRect, nsPoint aPt) 1.208 +{ 1.209 + aCtx->SetColor(NS_RGB(0, 255, 255)); 1.210 + nscoord x = nsPresContext::CSSPixelsToAppUnits(-5); 1.211 + aCtx->FillRect(aPt.x + x, aPt.y, 1.212 + nsPresContext::CSSPixelsToAppUnits(13), 1.213 + nsPresContext::CSSPixelsToAppUnits(3)); 1.214 + nscoord y = nsPresContext::CSSPixelsToAppUnits(-10); 1.215 + aCtx->FillRect(aPt.x, aPt.y + y, 1.216 + nsPresContext::CSSPixelsToAppUnits(3), 1.217 + nsPresContext::CSSPixelsToAppUnits(10)); 1.218 +} 1.219 +#endif // DEBUG 1.220 + 1.221 +#if defined(DEBUG) || (defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF)) 1.222 + 1.223 +void 1.224 +nsPlaceholderFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.225 + const nsRect& aDirtyRect, 1.226 + const nsDisplayListSet& aLists) 1.227 +{ 1.228 + DO_GLOBAL_REFLOW_COUNT_DSP("nsPlaceholderFrame"); 1.229 + 1.230 +#ifdef DEBUG 1.231 + if (GetShowFrameBorders()) { 1.232 + aLists.Outlines()->AppendNewToTop( 1.233 + new (aBuilder) nsDisplayGeneric(aBuilder, this, PaintDebugPlaceholder, 1.234 + "DebugPlaceholder", 1.235 + nsDisplayItem::TYPE_DEBUG_PLACEHOLDER)); 1.236 + } 1.237 +#endif 1.238 +} 1.239 +#endif // DEBUG || (MOZ_REFLOW_PERF_DSP && MOZ_REFLOW_PERF) 1.240 + 1.241 +#ifdef DEBUG_FRAME_DUMP 1.242 +nsresult 1.243 +nsPlaceholderFrame::GetFrameName(nsAString& aResult) const 1.244 +{ 1.245 + return MakeFrameName(NS_LITERAL_STRING("Placeholder"), aResult); 1.246 +} 1.247 + 1.248 +void 1.249 +nsPlaceholderFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const 1.250 +{ 1.251 + nsCString str; 1.252 + ListGeneric(str, aPrefix, aFlags); 1.253 + 1.254 + if (mOutOfFlowFrame) { 1.255 + str += " outOfFlowFrame="; 1.256 + nsFrame::ListTag(str, mOutOfFlowFrame); 1.257 + } 1.258 + fprintf_stderr(out, "%s\n", str.get()); 1.259 +} 1.260 +#endif