/src/mozilla-central/layout/tables/nsTableCellFrame.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
3 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
5 | | |
6 | | #include "nsTableCellFrame.h" |
7 | | |
8 | | #include "gfxContext.h" |
9 | | #include "gfxUtils.h" |
10 | | #include "mozilla/gfx/2D.h" |
11 | | #include "mozilla/gfx/Helpers.h" |
12 | | #include "nsTableFrame.h" |
13 | | #include "nsTableColFrame.h" |
14 | | #include "nsTableRowFrame.h" |
15 | | #include "nsTableRowGroupFrame.h" |
16 | | #include "mozilla/ComputedStyle.h" |
17 | | #include "nsStyleConsts.h" |
18 | | #include "nsPresContext.h" |
19 | | #include "nsCSSRendering.h" |
20 | | #include "nsIContent.h" |
21 | | #include "nsGenericHTMLElement.h" |
22 | | #include "nsAttrValueInlines.h" |
23 | | #include "nsHTMLParts.h" |
24 | | #include "nsGkAtoms.h" |
25 | | #include "nsIPresShell.h" |
26 | | #include "nsIServiceManager.h" |
27 | | #include "nsDisplayList.h" |
28 | | #include "nsLayoutUtils.h" |
29 | | #include "nsTextFrame.h" |
30 | | #include "FrameLayerBuilder.h" |
31 | | #include <algorithm> |
32 | | |
33 | | //TABLECELL SELECTION |
34 | | #include "nsFrameSelection.h" |
35 | | #include "mozilla/LookAndFeel.h" |
36 | | |
37 | | using namespace mozilla; |
38 | | using namespace mozilla::gfx; |
39 | | using namespace mozilla::image; |
40 | | |
41 | | class nsDisplayTableCellSelection final : public nsDisplayItem { |
42 | | public: |
43 | | nsDisplayTableCellSelection(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) |
44 | | : nsDisplayItem(aBuilder, aFrame) |
45 | 0 | { |
46 | 0 | MOZ_COUNT_CTOR(nsDisplayTableCellSelection); |
47 | 0 | } |
48 | | #ifdef NS_BUILD_REFCNT_LOGGING |
49 | | virtual ~nsDisplayTableCellSelection() { |
50 | | MOZ_COUNT_DTOR(nsDisplayTableCellSelection); |
51 | | } |
52 | | #endif |
53 | | |
54 | | void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override |
55 | 0 | { |
56 | 0 | static_cast<nsTableCellFrame*>(mFrame)->DecorateForSelection(aCtx->GetDrawTarget(), ToReferenceFrame()); |
57 | 0 | } |
58 | | NS_DISPLAY_DECL_NAME("TableCellSelection", TYPE_TABLE_CELL_SELECTION) |
59 | | |
60 | | bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder, |
61 | | mozilla::wr::IpcResourceUpdateQueue& aResources, |
62 | | const StackingContextHelper& aSc, |
63 | | mozilla::layers::WebRenderLayerManager* aManager, |
64 | | nsDisplayListBuilder* aDisplayListBuilder) override |
65 | 0 | { |
66 | 0 | RefPtr<nsFrameSelection> frameSelection = mFrame->PresShell()->FrameSelection(); |
67 | 0 | if (frameSelection->GetTableCellSelection()) { |
68 | 0 | return false; |
69 | 0 | } |
70 | 0 | |
71 | 0 | return true; |
72 | 0 | } |
73 | | }; |
74 | | |
75 | | nsTableCellFrame::nsTableCellFrame(ComputedStyle* aStyle, |
76 | | nsTableFrame* aTableFrame, |
77 | | ClassID aID) |
78 | | : nsContainerFrame(aStyle, aID) |
79 | | , mDesiredSize(aTableFrame->GetWritingMode()) |
80 | 0 | { |
81 | 0 | mColIndex = 0; |
82 | 0 | mPriorAvailISize = 0; |
83 | 0 |
|
84 | 0 | SetContentEmpty(false); |
85 | 0 | SetHasPctOverBSize(false); |
86 | 0 | } |
87 | | |
88 | | nsTableCellFrame::~nsTableCellFrame() |
89 | 0 | { |
90 | 0 | } |
91 | | |
92 | | NS_IMPL_FRAMEARENA_HELPERS(nsTableCellFrame) |
93 | | |
94 | | void |
95 | | nsTableCellFrame::Init(nsIContent* aContent, |
96 | | nsContainerFrame* aParent, |
97 | | nsIFrame* aPrevInFlow) |
98 | 0 | { |
99 | 0 | // Let the base class do its initialization |
100 | 0 | nsContainerFrame::Init(aContent, aParent, aPrevInFlow); |
101 | 0 |
|
102 | 0 | if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER)) { |
103 | 0 | AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT); |
104 | 0 | } |
105 | 0 |
|
106 | 0 | if (aPrevInFlow) { |
107 | 0 | // Set the column index |
108 | 0 | nsTableCellFrame* cellFrame = (nsTableCellFrame*)aPrevInFlow; |
109 | 0 | uint32_t colIndex = cellFrame->ColIndex(); |
110 | 0 | SetColIndex(colIndex); |
111 | 0 | } else { |
112 | 0 | // Although the spec doesn't say that writing-mode is not applied to |
113 | 0 | // table-cells, we still override style value here because we want to |
114 | 0 | // make effective writing mode of table structure frames consistent |
115 | 0 | // within a table. The content inside table cells is reflowed by an |
116 | 0 | // anonymous block, hence their writing mode is not affected. |
117 | 0 | mWritingMode = GetTableFrame()->GetWritingMode(); |
118 | 0 | } |
119 | 0 | } |
120 | | |
121 | | void |
122 | | nsTableCellFrame::DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData) |
123 | 0 | { |
124 | 0 | if (HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) { |
125 | 0 | nsTableFrame::UnregisterPositionedTablePart(this, aDestructRoot); |
126 | 0 | } |
127 | 0 |
|
128 | 0 | nsContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData); |
129 | 0 | } |
130 | | |
131 | | // nsIPercentBSizeObserver methods |
132 | | |
133 | | void |
134 | | nsTableCellFrame::NotifyPercentBSize(const ReflowInput& aReflowInput) |
135 | 0 | { |
136 | 0 | // ReflowInput ensures the mCBReflowInput of blocks inside a |
137 | 0 | // cell is the cell frame, not the inner-cell block, and that the |
138 | 0 | // containing block of an inner table is the containing block of its |
139 | 0 | // table wrapper. |
140 | 0 | // XXXldb Given the now-stricter |NeedsToObserve|, many if not all of |
141 | 0 | // these tests are probably unnecessary. |
142 | 0 |
|
143 | 0 | // Maybe the cell reflow state; we sure if we're inside the |if|. |
144 | 0 | const ReflowInput *cellRI = aReflowInput.mCBReflowInput; |
145 | 0 |
|
146 | 0 | if (cellRI && cellRI->mFrame == this && |
147 | 0 | (cellRI->ComputedBSize() == NS_UNCONSTRAINEDSIZE || |
148 | 0 | cellRI->ComputedBSize() == 0)) { // XXXldb Why 0? |
149 | 0 | // This is a percentage bsize on a frame whose percentage bsizes |
150 | 0 | // are based on the bsize of the cell, since its containing block |
151 | 0 | // is the inner cell frame. |
152 | 0 |
|
153 | 0 | // We'll only honor the percent bsize if sibling-cells/ancestors |
154 | 0 | // have specified/pct bsize. (Also, siblings only count for this if |
155 | 0 | // both this cell and the sibling cell span exactly 1 row.) |
156 | 0 |
|
157 | 0 | if (nsTableFrame::AncestorsHaveStyleBSize(*cellRI) || |
158 | 0 | (GetTableFrame()->GetEffectiveRowSpan(*this) == 1 && |
159 | 0 | cellRI->mParentReflowInput->mFrame-> |
160 | 0 | HasAnyStateBits(NS_ROW_HAS_CELL_WITH_STYLE_BSIZE))) { |
161 | 0 |
|
162 | 0 | for (const ReflowInput *rs = aReflowInput.mParentReflowInput; |
163 | 0 | rs != cellRI; |
164 | 0 | rs = rs->mParentReflowInput) { |
165 | 0 | rs->mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); |
166 | 0 | } |
167 | 0 |
|
168 | 0 | nsTableFrame::RequestSpecialBSizeReflow(*cellRI); |
169 | 0 | } |
170 | 0 | } |
171 | 0 | } |
172 | | |
173 | | // The cell needs to observe its block and things inside its block but nothing below that |
174 | | bool |
175 | | nsTableCellFrame::NeedsToObserve(const ReflowInput& aReflowInput) |
176 | 0 | { |
177 | 0 | const ReflowInput *rs = aReflowInput.mParentReflowInput; |
178 | 0 | if (!rs) |
179 | 0 | return false; |
180 | 0 | if (rs->mFrame == this) { |
181 | 0 | // We always observe the child block. It will never send any |
182 | 0 | // notifications, but we need this so that the observer gets |
183 | 0 | // propagated to its kids. |
184 | 0 | return true; |
185 | 0 | } |
186 | 0 | rs = rs->mParentReflowInput; |
187 | 0 | if (!rs) { |
188 | 0 | return false; |
189 | 0 | } |
190 | 0 | |
191 | 0 | // We always need to let the percent bsize observer be propagated |
192 | 0 | // from a table wrapper frame to an inner table frame. |
193 | 0 | LayoutFrameType fType = aReflowInput.mFrame->Type(); |
194 | 0 | if (fType == LayoutFrameType::Table) { |
195 | 0 | return true; |
196 | 0 | } |
197 | 0 | |
198 | 0 | // We need the observer to be propagated to all children of the cell |
199 | 0 | // (i.e., children of the child block) in quirks mode, but only to |
200 | 0 | // tables in standards mode. |
201 | 0 | // XXX This may not be true in the case of orthogonal flows within |
202 | 0 | // the cell (bug 1174711 comment 8); we may need to observe isizes |
203 | 0 | // instead of bsizes for orthogonal children. |
204 | 0 | return rs->mFrame == this && |
205 | 0 | (PresContext()->CompatibilityMode() == eCompatibility_NavQuirks || |
206 | 0 | fType == LayoutFrameType::TableWrapper); |
207 | 0 | } |
208 | | |
209 | | nsresult |
210 | | nsTableCellFrame::AttributeChanged(int32_t aNameSpaceID, |
211 | | nsAtom* aAttribute, |
212 | | int32_t aModType) |
213 | 0 | { |
214 | 0 | // We need to recalculate in this case because of the nowrap quirk in |
215 | 0 | // BasicTableLayoutStrategy |
216 | 0 | if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::nowrap && |
217 | 0 | PresContext()->CompatibilityMode() == eCompatibility_NavQuirks) { |
218 | 0 | PresShell()-> |
219 | 0 | FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY); |
220 | 0 | } |
221 | 0 |
|
222 | 0 | if (aAttribute == nsGkAtoms::rowspan || aAttribute == nsGkAtoms::colspan) { |
223 | 0 | nsLayoutUtils::PostRestyleEvent(mContent->AsElement(), |
224 | 0 | nsRestyleHint(0), |
225 | 0 | nsChangeHint_UpdateTableCellSpans); |
226 | 0 | } |
227 | 0 | return NS_OK; |
228 | 0 | } |
229 | | |
230 | | /* virtual */ void |
231 | | nsTableCellFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) |
232 | 0 | { |
233 | 0 | nsContainerFrame::DidSetComputedStyle(aOldComputedStyle); |
234 | 0 |
|
235 | 0 | if (!aOldComputedStyle) //avoid this on init |
236 | 0 | return; |
237 | 0 | |
238 | 0 | nsTableFrame* tableFrame = GetTableFrame(); |
239 | 0 | if (tableFrame->IsBorderCollapse() && |
240 | 0 | tableFrame->BCRecalcNeeded(aOldComputedStyle, Style())) { |
241 | 0 | uint32_t colIndex = ColIndex(); |
242 | 0 | uint32_t rowIndex = RowIndex(); |
243 | 0 | // row span needs to be clamped as we do not create rows in the cellmap |
244 | 0 | // which do not have cells originating in them |
245 | 0 | TableArea damageArea(colIndex, rowIndex, GetColSpan(), |
246 | 0 | std::min(static_cast<uint32_t>(GetRowSpan()), |
247 | 0 | tableFrame->GetRowCount() - rowIndex)); |
248 | 0 | tableFrame->AddBCDamageArea(damageArea); |
249 | 0 | } |
250 | 0 | } |
251 | | |
252 | | #ifdef DEBUG |
253 | | void |
254 | | nsTableCellFrame::AppendFrames(ChildListID aListID, |
255 | | nsFrameList& aFrameList) |
256 | | { |
257 | | MOZ_CRASH("unsupported operation"); |
258 | | } |
259 | | |
260 | | void |
261 | | nsTableCellFrame::InsertFrames(ChildListID aListID, |
262 | | nsIFrame* aPrevFrame, |
263 | | nsFrameList& aFrameList) |
264 | | { |
265 | | MOZ_CRASH("unsupported operation"); |
266 | | } |
267 | | |
268 | | void |
269 | | nsTableCellFrame::RemoveFrame(ChildListID aListID, |
270 | | nsIFrame* aOldFrame) |
271 | | { |
272 | | MOZ_CRASH("unsupported operation"); |
273 | | } |
274 | | #endif |
275 | | |
276 | | void nsTableCellFrame::SetColIndex(int32_t aColIndex) |
277 | 0 | { |
278 | 0 | mColIndex = aColIndex; |
279 | 0 | } |
280 | | |
281 | | /* virtual */ nsMargin |
282 | | nsTableCellFrame::GetUsedMargin() const |
283 | 0 | { |
284 | 0 | return nsMargin(0,0,0,0); |
285 | 0 | } |
286 | | |
287 | | //ASSURE DIFFERENT COLORS for selection |
288 | | inline nscolor EnsureDifferentColors(nscolor colorA, nscolor colorB) |
289 | 0 | { |
290 | 0 | if (colorA == colorB) |
291 | 0 | { |
292 | 0 | nscolor res; |
293 | 0 | res = NS_RGB(NS_GET_R(colorA) ^ 0xff, |
294 | 0 | NS_GET_G(colorA) ^ 0xff, |
295 | 0 | NS_GET_B(colorA) ^ 0xff); |
296 | 0 | return res; |
297 | 0 | } |
298 | 0 | return colorA; |
299 | 0 | } |
300 | | |
301 | | void |
302 | | nsTableCellFrame::DecorateForSelection(DrawTarget* aDrawTarget, nsPoint aPt) |
303 | 0 | { |
304 | 0 | NS_ASSERTION(IsSelected(), "Should only be called for selected cells"); |
305 | 0 | int16_t displaySelection; |
306 | 0 | nsPresContext* presContext = PresContext(); |
307 | 0 | displaySelection = DisplaySelection(presContext); |
308 | 0 | if (displaySelection) { |
309 | 0 | RefPtr<nsFrameSelection> frameSelection = |
310 | 0 | presContext->PresShell()->FrameSelection(); |
311 | 0 |
|
312 | 0 | if (frameSelection->GetTableCellSelection()) { |
313 | 0 | nscolor bordercolor; |
314 | 0 | if (displaySelection == nsISelectionController::SELECTION_DISABLED) { |
315 | 0 | bordercolor = NS_RGB(176,176,176);// disabled color |
316 | 0 | } |
317 | 0 | else { |
318 | 0 | bordercolor = |
319 | 0 | LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground); |
320 | 0 | } |
321 | 0 | nscoord threePx = nsPresContext::CSSPixelsToAppUnits(3); |
322 | 0 | if ((mRect.width > threePx) && (mRect.height > threePx)) { |
323 | 0 | //compare bordercolor to background-color |
324 | 0 | bordercolor = EnsureDifferentColors( |
325 | 0 | bordercolor, StyleBackground()->BackgroundColor(this)); |
326 | 0 |
|
327 | 0 | int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel(); |
328 | 0 | Point devPixelOffset = NSPointToPoint(aPt, appUnitsPerDevPixel); |
329 | 0 |
|
330 | 0 | AutoRestoreTransform autoRestoreTransform(aDrawTarget); |
331 | 0 | aDrawTarget->SetTransform( |
332 | 0 | aDrawTarget->GetTransform().PreTranslate(devPixelOffset)); |
333 | 0 |
|
334 | 0 | ColorPattern color(ToDeviceColor(bordercolor)); |
335 | 0 |
|
336 | 0 | nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); |
337 | 0 |
|
338 | 0 | StrokeLineWithSnapping(nsPoint(onePixel, 0), nsPoint(mRect.width, 0), |
339 | 0 | appUnitsPerDevPixel, *aDrawTarget, color); |
340 | 0 | StrokeLineWithSnapping(nsPoint(0, onePixel), nsPoint(0, mRect.height), |
341 | 0 | appUnitsPerDevPixel, *aDrawTarget, color); |
342 | 0 | StrokeLineWithSnapping(nsPoint(onePixel, mRect.height), |
343 | 0 | nsPoint(mRect.width, mRect.height), |
344 | 0 | appUnitsPerDevPixel, *aDrawTarget, color); |
345 | 0 | StrokeLineWithSnapping(nsPoint(mRect.width, onePixel), |
346 | 0 | nsPoint(mRect.width, mRect.height), |
347 | 0 | appUnitsPerDevPixel, *aDrawTarget, color); |
348 | 0 | //middle |
349 | 0 | nsRect r(onePixel, onePixel, |
350 | 0 | mRect.width - onePixel, mRect.height - onePixel); |
351 | 0 | Rect devPixelRect = |
352 | 0 | NSRectToSnappedRect(r, appUnitsPerDevPixel, *aDrawTarget); |
353 | 0 | aDrawTarget->StrokeRect(devPixelRect, color); |
354 | 0 | //shading |
355 | 0 | StrokeLineWithSnapping(nsPoint(2*onePixel, mRect.height-2*onePixel), |
356 | 0 | nsPoint(mRect.width-onePixel, mRect.height- (2*onePixel)), |
357 | 0 | appUnitsPerDevPixel, *aDrawTarget, color); |
358 | 0 | StrokeLineWithSnapping(nsPoint(mRect.width - (2*onePixel), 2*onePixel), |
359 | 0 | nsPoint(mRect.width - (2*onePixel), mRect.height-onePixel), |
360 | 0 | appUnitsPerDevPixel, *aDrawTarget, color); |
361 | 0 | } |
362 | 0 | } |
363 | 0 | } |
364 | 0 | } |
365 | | |
366 | | ImgDrawResult |
367 | | nsTableCellFrame::PaintBackground(gfxContext& aRenderingContext, |
368 | | const nsRect& aDirtyRect, |
369 | | nsPoint aPt, |
370 | | uint32_t aFlags) |
371 | 0 | { |
372 | 0 | nsRect rect(aPt, GetSize()); |
373 | 0 | nsCSSRendering::PaintBGParams params = |
374 | 0 | nsCSSRendering::PaintBGParams::ForAllLayers(*PresContext(), |
375 | 0 | aDirtyRect, rect, |
376 | 0 | this, aFlags); |
377 | 0 | return nsCSSRendering::PaintStyleImageLayer(params, aRenderingContext); |
378 | 0 | } |
379 | | |
380 | | nsresult |
381 | | nsTableCellFrame::ProcessBorders(nsTableFrame* aFrame, |
382 | | nsDisplayListBuilder* aBuilder, |
383 | | const nsDisplayListSet& aLists) |
384 | 0 | { |
385 | 0 | const nsStyleBorder* borderStyle = StyleBorder(); |
386 | 0 | if (aFrame->IsBorderCollapse() || !borderStyle->HasBorder()) |
387 | 0 | return NS_OK; |
388 | 0 | |
389 | 0 | if (!GetContentEmpty() || |
390 | 0 | StyleTableBorder()->mEmptyCells == NS_STYLE_TABLE_EMPTY_CELLS_SHOW) { |
391 | 0 | aLists.BorderBackground()->AppendToTop(MakeDisplayItem<nsDisplayBorder>(aBuilder, this)); |
392 | 0 | } |
393 | 0 |
|
394 | 0 | return NS_OK; |
395 | 0 | } |
396 | | |
397 | | class nsDisplayTableCellBackground : public nsDisplayTableItem { |
398 | | public: |
399 | | nsDisplayTableCellBackground(nsDisplayListBuilder* aBuilder, |
400 | | nsTableCellFrame* aFrame) : |
401 | 0 | nsDisplayTableItem(aBuilder, aFrame) { |
402 | 0 | MOZ_COUNT_CTOR(nsDisplayTableCellBackground); |
403 | 0 | } |
404 | | #ifdef NS_BUILD_REFCNT_LOGGING |
405 | | virtual ~nsDisplayTableCellBackground() { |
406 | | MOZ_COUNT_DTOR(nsDisplayTableCellBackground); |
407 | | } |
408 | | #endif |
409 | | |
410 | | virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, |
411 | | HitTestState* aState, |
412 | 0 | nsTArray<nsIFrame*> *aOutFrames) override { |
413 | 0 | aOutFrames->AppendElement(mFrame); |
414 | 0 | } |
415 | | virtual void Paint(nsDisplayListBuilder* aBuilder, |
416 | | gfxContext* aCtx) override; |
417 | | virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, |
418 | | bool* aSnap) const override; |
419 | | NS_DISPLAY_DECL_NAME("TableCellBackground", TYPE_TABLE_CELL_BACKGROUND) |
420 | | }; |
421 | | |
422 | | void nsDisplayTableCellBackground::Paint(nsDisplayListBuilder* aBuilder, |
423 | | gfxContext* aCtx) |
424 | 0 | { |
425 | 0 | ImgDrawResult result = static_cast<nsTableCellFrame*>(mFrame)-> |
426 | 0 | PaintBackground(*aCtx, GetPaintRect(), ToReferenceFrame(), |
427 | 0 | aBuilder->GetBackgroundPaintFlags()); |
428 | 0 |
|
429 | 0 | nsDisplayTableItemGeometry::UpdateDrawResult(this, result); |
430 | 0 | } |
431 | | |
432 | | nsRect |
433 | | nsDisplayTableCellBackground::GetBounds(nsDisplayListBuilder* aBuilder, |
434 | | bool* aSnap) const |
435 | 0 | { |
436 | 0 | // revert from nsDisplayTableItem's implementation ... cell backgrounds |
437 | 0 | // don't overflow the cell |
438 | 0 | return nsDisplayItem::GetBounds(aBuilder, aSnap); |
439 | 0 | } |
440 | | |
441 | | void nsTableCellFrame::InvalidateFrame(uint32_t aDisplayItemKey, bool aRebuildDisplayItems) |
442 | 0 | { |
443 | 0 | nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems); |
444 | 0 | if (GetTableFrame()->IsBorderCollapse()) { |
445 | 0 | GetParent()->InvalidateFrameWithRect(GetVisualOverflowRect() + GetPosition(), aDisplayItemKey, false); |
446 | 0 | } |
447 | 0 | } |
448 | | |
449 | | void nsTableCellFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey, bool aRebuildDisplayItems) |
450 | 0 | { |
451 | 0 | nsIFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey, aRebuildDisplayItems); |
452 | 0 | // If we have filters applied that would affects our bounds, then |
453 | 0 | // we get an inactive layer created and this is computed |
454 | 0 | // within FrameLayerBuilder |
455 | 0 | GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey, false); |
456 | 0 | } |
457 | | |
458 | | bool |
459 | | nsTableCellFrame::ShouldPaintBordersAndBackgrounds() const |
460 | 0 | { |
461 | 0 | // If we're not visible, we don't paint. |
462 | 0 | if (!StyleVisibility()->IsVisible()) { |
463 | 0 | return false; |
464 | 0 | } |
465 | 0 | |
466 | 0 | // Consider 'empty-cells', but only in separated borders mode. |
467 | 0 | if (!GetContentEmpty()) { |
468 | 0 | return true; |
469 | 0 | } |
470 | 0 | |
471 | 0 | nsTableFrame* tableFrame = GetTableFrame(); |
472 | 0 | if (tableFrame->IsBorderCollapse()) { |
473 | 0 | return true; |
474 | 0 | } |
475 | 0 | |
476 | 0 | return StyleTableBorder()->mEmptyCells == NS_STYLE_TABLE_EMPTY_CELLS_SHOW; |
477 | 0 | } |
478 | | |
479 | | bool |
480 | | nsTableCellFrame::ShouldPaintBackground(nsDisplayListBuilder* aBuilder) |
481 | 0 | { |
482 | 0 | return ShouldPaintBordersAndBackgrounds() && IsVisibleInSelection(aBuilder); |
483 | 0 | } |
484 | | |
485 | | void |
486 | | nsTableCellFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
487 | | const nsDisplayListSet& aLists) |
488 | 0 | { |
489 | 0 | DO_GLOBAL_REFLOW_COUNT_DSP("nsTableCellFrame"); |
490 | 0 | if (ShouldPaintBordersAndBackgrounds()) { |
491 | 0 | // display outset box-shadows if we need to. |
492 | 0 | bool hasBoxShadow = !!StyleEffects()->mBoxShadow; |
493 | 0 | if (hasBoxShadow) { |
494 | 0 | aLists.BorderBackground()->AppendToTop( |
495 | 0 | MakeDisplayItem<nsDisplayBoxShadowOuter>(aBuilder, this)); |
496 | 0 | } |
497 | 0 |
|
498 | 0 | // display background if we need to. |
499 | 0 | if (aBuilder->IsForEventDelivery() || |
500 | 0 | !StyleBackground()->IsTransparent(this) || |
501 | 0 | StyleDisplay()->HasAppearance()) { |
502 | 0 | nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, |
503 | 0 | this, |
504 | 0 | GetRectRelativeToSelf(), |
505 | 0 | aLists.BorderBackground()); |
506 | 0 | } |
507 | 0 |
|
508 | 0 | // display inset box-shadows if we need to. |
509 | 0 | if (hasBoxShadow) { |
510 | 0 | aLists.BorderBackground()->AppendToTop( |
511 | 0 | MakeDisplayItem<nsDisplayBoxShadowInner>(aBuilder, this)); |
512 | 0 | } |
513 | 0 |
|
514 | 0 | // display borders if we need to |
515 | 0 | ProcessBorders(GetTableFrame(), aBuilder, aLists); |
516 | 0 |
|
517 | 0 | // and display the selection border if we need to |
518 | 0 | if (IsSelected()) { |
519 | 0 | aLists.BorderBackground()->AppendToTop( |
520 | 0 | MakeDisplayItem<nsDisplayTableCellSelection>(aBuilder, this)); |
521 | 0 | } |
522 | 0 | } |
523 | 0 |
|
524 | 0 | // the 'empty-cells' property has no effect on 'outline' |
525 | 0 | DisplayOutline(aBuilder, aLists); |
526 | 0 |
|
527 | 0 | // Push a null 'current table item' so that descendant tables can't |
528 | 0 | // accidentally mess with our table |
529 | 0 | nsAutoPushCurrentTableItem pushTableItem; |
530 | 0 | pushTableItem.Push(aBuilder, nullptr); |
531 | 0 |
|
532 | 0 | nsIFrame* kid = mFrames.FirstChild(); |
533 | 0 | NS_ASSERTION(kid && !kid->GetNextSibling(), "Table cells should have just one child"); |
534 | 0 | // The child's background will go in our BorderBackground() list. |
535 | 0 | // This isn't a problem since it won't have a real background except for |
536 | 0 | // event handling. We do not call BuildDisplayListForNonBlockChildren |
537 | 0 | // because that/ would put the child's background in the Content() list |
538 | 0 | // which isn't right (e.g., would end up on top of our child floats for |
539 | 0 | // event handling). |
540 | 0 | BuildDisplayListForChild(aBuilder, kid, aLists); |
541 | 0 | } |
542 | | |
543 | | nsIFrame::LogicalSides |
544 | | nsTableCellFrame::GetLogicalSkipSides(const ReflowInput* aReflowInput) const |
545 | 0 | { |
546 | 0 | if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak == |
547 | 0 | StyleBoxDecorationBreak::Clone)) { |
548 | 0 | return LogicalSides(); |
549 | 0 | } |
550 | 0 | |
551 | 0 | LogicalSides skip; |
552 | 0 | if (nullptr != GetPrevInFlow()) { |
553 | 0 | skip |= eLogicalSideBitsBStart; |
554 | 0 | } |
555 | 0 | if (nullptr != GetNextInFlow()) { |
556 | 0 | skip |= eLogicalSideBitsBEnd; |
557 | 0 | } |
558 | 0 | return skip; |
559 | 0 | } |
560 | | |
561 | | /* virtual */ nsMargin |
562 | | nsTableCellFrame::GetBorderOverflow() |
563 | 0 | { |
564 | 0 | return nsMargin(0, 0, 0, 0); |
565 | 0 | } |
566 | | |
567 | | // Align the cell's child frame within the cell |
568 | | |
569 | | void nsTableCellFrame::BlockDirAlignChild(WritingMode aWM, nscoord aMaxAscent) |
570 | 0 | { |
571 | 0 | /* It's the 'border-collapse' on the table that matters */ |
572 | 0 | LogicalMargin borderPadding = GetLogicalUsedBorderAndPadding(aWM); |
573 | 0 |
|
574 | 0 | nscoord bStartInset = borderPadding.BStart(aWM); |
575 | 0 | nscoord bEndInset = borderPadding.BEnd(aWM); |
576 | 0 |
|
577 | 0 | uint8_t verticalAlignFlags = GetVerticalAlign(); |
578 | 0 |
|
579 | 0 | nscoord bSize = BSize(aWM); |
580 | 0 | nsIFrame* firstKid = mFrames.FirstChild(); |
581 | 0 | nsSize containerSize = mRect.Size(); |
582 | 0 | NS_ASSERTION(firstKid, "Frame construction error, a table cell always has " |
583 | 0 | "an inner cell frame"); |
584 | 0 | LogicalRect kidRect = firstKid->GetLogicalRect(aWM, containerSize); |
585 | 0 | nscoord childBSize = kidRect.BSize(aWM); |
586 | 0 |
|
587 | 0 | // Vertically align the child |
588 | 0 | nscoord kidBStart = 0; |
589 | 0 | switch (verticalAlignFlags) |
590 | 0 | { |
591 | 0 | case NS_STYLE_VERTICAL_ALIGN_BASELINE: |
592 | 0 | // Align the baselines of the child frame with the baselines of |
593 | 0 | // other children in the same row which have 'vertical-align: baseline' |
594 | 0 | kidBStart = bStartInset + aMaxAscent - GetCellBaseline(); |
595 | 0 | break; |
596 | 0 |
|
597 | 0 | case NS_STYLE_VERTICAL_ALIGN_TOP: |
598 | 0 | // Align the top of the child frame with the top of the content area, |
599 | 0 | kidBStart = bStartInset; |
600 | 0 | break; |
601 | 0 |
|
602 | 0 | case NS_STYLE_VERTICAL_ALIGN_BOTTOM: |
603 | 0 | // Align the bottom of the child frame with the bottom of the content area, |
604 | 0 | kidBStart = bSize - childBSize - bEndInset; |
605 | 0 | break; |
606 | 0 |
|
607 | 0 | default: |
608 | 0 | case NS_STYLE_VERTICAL_ALIGN_MIDDLE: |
609 | 0 | // Align the middle of the child frame with the middle of the content area, |
610 | 0 | kidBStart = (bSize - childBSize - bEndInset + bStartInset) / 2; |
611 | 0 | } |
612 | 0 | // If the content is larger than the cell bsize, align from bStartInset |
613 | 0 | // (cell's content-box bstart edge). |
614 | 0 | kidBStart = std::max(bStartInset, kidBStart); |
615 | 0 |
|
616 | 0 | if (kidBStart != kidRect.BStart(aWM)) { |
617 | 0 | // Invalidate at the old position first |
618 | 0 | firstKid->InvalidateFrameSubtree(); |
619 | 0 | } |
620 | 0 |
|
621 | 0 | firstKid->SetPosition(aWM, LogicalPoint(aWM, kidRect.IStart(aWM), |
622 | 0 | kidBStart), containerSize); |
623 | 0 | ReflowOutput desiredSize(aWM); |
624 | 0 | desiredSize.SetSize(aWM, GetLogicalSize(aWM)); |
625 | 0 |
|
626 | 0 | nsRect overflow(nsPoint(0,0), GetSize()); |
627 | 0 | overflow.Inflate(GetBorderOverflow()); |
628 | 0 | desiredSize.mOverflowAreas.SetAllTo(overflow); |
629 | 0 | ConsiderChildOverflow(desiredSize.mOverflowAreas, firstKid); |
630 | 0 | FinishAndStoreOverflow(&desiredSize); |
631 | 0 | if (kidBStart != kidRect.BStart(aWM)) { |
632 | 0 | // Make sure any child views are correctly positioned. We know the inner table |
633 | 0 | // cell won't have a view |
634 | 0 | nsContainerFrame::PositionChildViews(firstKid); |
635 | 0 |
|
636 | 0 | // Invalidate new overflow rect |
637 | 0 | firstKid->InvalidateFrameSubtree(); |
638 | 0 | } |
639 | 0 | if (HasView()) { |
640 | 0 | nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this, |
641 | 0 | GetView(), |
642 | 0 | desiredSize.VisualOverflow(), 0); |
643 | 0 | } |
644 | 0 | } |
645 | | |
646 | | bool |
647 | | nsTableCellFrame::ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) |
648 | 0 | { |
649 | 0 | nsRect bounds(nsPoint(0,0), GetSize()); |
650 | 0 | bounds.Inflate(GetBorderOverflow()); |
651 | 0 |
|
652 | 0 | aOverflowAreas.UnionAllWith(bounds); |
653 | 0 | return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas); |
654 | 0 | } |
655 | | |
656 | | // Per CSS 2.1, we map 'sub', 'super', 'text-top', 'text-bottom', |
657 | | // length, percentage, and calc() values to 'baseline'. |
658 | | uint8_t |
659 | | nsTableCellFrame::GetVerticalAlign() const |
660 | 0 | { |
661 | 0 | const nsStyleCoord& verticalAlign = StyleDisplay()->mVerticalAlign; |
662 | 0 | if (verticalAlign.GetUnit() == eStyleUnit_Enumerated) { |
663 | 0 | uint8_t value = verticalAlign.GetIntValue(); |
664 | 0 | if (value == NS_STYLE_VERTICAL_ALIGN_TOP || |
665 | 0 | value == NS_STYLE_VERTICAL_ALIGN_MIDDLE || |
666 | 0 | value == NS_STYLE_VERTICAL_ALIGN_BOTTOM) { |
667 | 0 | return value; |
668 | 0 | } |
669 | 0 | } |
670 | 0 | return NS_STYLE_VERTICAL_ALIGN_BASELINE; |
671 | 0 | } |
672 | | |
673 | | bool |
674 | | nsTableCellFrame::CellHasVisibleContent(nscoord height, |
675 | | nsTableFrame* tableFrame, |
676 | | nsIFrame* kidFrame) |
677 | 0 | { |
678 | 0 | // see http://www.w3.org/TR/CSS21/tables.html#empty-cells |
679 | 0 | if (height > 0) |
680 | 0 | return true; |
681 | 0 | if (tableFrame->IsBorderCollapse()) |
682 | 0 | return true; |
683 | 0 | for (nsIFrame* innerFrame : kidFrame->PrincipalChildList()) { |
684 | 0 | LayoutFrameType frameType = innerFrame->Type(); |
685 | 0 | if (LayoutFrameType::Text == frameType) { |
686 | 0 | nsTextFrame* textFrame = static_cast<nsTextFrame*>(innerFrame); |
687 | 0 | if (textFrame->HasNoncollapsedCharacters()) |
688 | 0 | return true; |
689 | 0 | } else if (LayoutFrameType::Placeholder != frameType) { |
690 | 0 | return true; |
691 | 0 | } |
692 | 0 | else { |
693 | 0 | nsIFrame *floatFrame = nsLayoutUtils::GetFloatFromPlaceholder(innerFrame); |
694 | 0 | if (floatFrame) |
695 | 0 | return true; |
696 | 0 | } |
697 | 0 | } |
698 | 0 | return false; |
699 | 0 | } |
700 | | |
701 | | nscoord |
702 | | nsTableCellFrame::GetCellBaseline() const |
703 | 0 | { |
704 | 0 | // Ignore the position of the inner frame relative to the cell frame |
705 | 0 | // since we want the position as though the inner were top-aligned. |
706 | 0 | nsIFrame *inner = mFrames.FirstChild(); |
707 | 0 | nscoord borderPadding = GetUsedBorderAndPadding().top; |
708 | 0 | nscoord result; |
709 | 0 | if (nsLayoutUtils::GetFirstLineBaseline(GetWritingMode(), inner, &result)) |
710 | 0 | return result + borderPadding; |
711 | 0 | return inner->GetContentRectRelativeToSelf().YMost() + |
712 | 0 | borderPadding; |
713 | 0 | } |
714 | | |
715 | | int32_t |
716 | | nsTableCellFrame::GetRowSpan() |
717 | 0 | { |
718 | 0 | int32_t rowSpan=1; |
719 | 0 |
|
720 | 0 | // Don't look at the content's rowspan if we're a pseudo cell |
721 | 0 | if (!Style()->GetPseudo()) { |
722 | 0 | dom::Element* elem = mContent->AsElement(); |
723 | 0 | const nsAttrValue* attr = elem->GetParsedAttr(nsGkAtoms::rowspan); |
724 | 0 | // Note that we don't need to check the tag name, because only table cells |
725 | 0 | // (including MathML <mtd>) and table headers parse the "rowspan" attribute |
726 | 0 | // into an integer. |
727 | 0 | if (attr && attr->Type() == nsAttrValue::eInteger) { |
728 | 0 | rowSpan = attr->GetIntegerValue(); |
729 | 0 | } |
730 | 0 | } |
731 | 0 | return rowSpan; |
732 | 0 | } |
733 | | |
734 | | int32_t |
735 | | nsTableCellFrame::GetColSpan() |
736 | 0 | { |
737 | 0 | int32_t colSpan=1; |
738 | 0 |
|
739 | 0 | // Don't look at the content's colspan if we're a pseudo cell |
740 | 0 | if (!Style()->GetPseudo()) { |
741 | 0 | dom::Element* elem = mContent->AsElement(); |
742 | 0 | const nsAttrValue* attr = elem->GetParsedAttr( |
743 | 0 | MOZ_UNLIKELY(elem->IsMathMLElement()) ? nsGkAtoms::columnspan_ |
744 | 0 | : nsGkAtoms::colspan); |
745 | 0 | // Note that we don't need to check the tag name, because only table cells |
746 | 0 | // (including MathML <mtd>) and table headers parse the "colspan" attribute |
747 | 0 | // into an integer. |
748 | 0 | if (attr && attr->Type() == nsAttrValue::eInteger) { |
749 | 0 | colSpan = attr->GetIntegerValue(); |
750 | 0 | } |
751 | 0 | } |
752 | 0 | return colSpan; |
753 | 0 | } |
754 | | |
755 | | /* virtual */ nscoord |
756 | | nsTableCellFrame::GetMinISize(gfxContext *aRenderingContext) |
757 | 0 | { |
758 | 0 | nscoord result = 0; |
759 | 0 | DISPLAY_MIN_INLINE_SIZE(this, result); |
760 | 0 |
|
761 | 0 | nsIFrame *inner = mFrames.FirstChild(); |
762 | 0 | result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner, |
763 | 0 | nsLayoutUtils::MIN_ISIZE); |
764 | 0 | return result; |
765 | 0 | } |
766 | | |
767 | | /* virtual */ nscoord |
768 | | nsTableCellFrame::GetPrefISize(gfxContext *aRenderingContext) |
769 | 0 | { |
770 | 0 | nscoord result = 0; |
771 | 0 | DISPLAY_PREF_INLINE_SIZE(this, result); |
772 | 0 |
|
773 | 0 | nsIFrame *inner = mFrames.FirstChild(); |
774 | 0 | result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner, |
775 | 0 | nsLayoutUtils::PREF_ISIZE); |
776 | 0 | return result; |
777 | 0 | } |
778 | | |
779 | | /* virtual */ nsIFrame::IntrinsicISizeOffsetData |
780 | | nsTableCellFrame::IntrinsicISizeOffsets(nscoord aPercentageBasis) |
781 | 0 | { |
782 | 0 | IntrinsicISizeOffsetData result = |
783 | 0 | nsContainerFrame::IntrinsicISizeOffsets(aPercentageBasis); |
784 | 0 |
|
785 | 0 | result.hMargin = 0; |
786 | 0 |
|
787 | 0 | WritingMode wm = GetWritingMode(); |
788 | 0 | result.hBorder = GetBorderWidth(wm).IStartEnd(wm); |
789 | 0 |
|
790 | 0 | return result; |
791 | 0 | } |
792 | | |
793 | | #ifdef DEBUG |
794 | | #define PROBABLY_TOO_LARGE 1000000 |
795 | | static |
796 | | void DebugCheckChildSize(nsIFrame* aChild, |
797 | | ReflowOutput& aMet) |
798 | | { |
799 | | WritingMode wm = aMet.GetWritingMode(); |
800 | | if ((aMet.ISize(wm) < 0) || (aMet.ISize(wm) > PROBABLY_TOO_LARGE)) { |
801 | | printf("WARNING: cell content %p has large inline size %d \n", |
802 | | static_cast<void*>(aChild), int32_t(aMet.ISize(wm))); |
803 | | } |
804 | | } |
805 | | #endif |
806 | | |
807 | | // the computed bsize for the cell, which descendants use for percent bsize calculations |
808 | | // it is the bsize (minus border, padding) of the cell's first in flow during its final |
809 | | // reflow without an unconstrained bsize. |
810 | | static nscoord |
811 | | CalcUnpaginatedBSize(nsTableCellFrame& aCellFrame, |
812 | | nsTableFrame& aTableFrame, |
813 | | nscoord aBlockDirBorderPadding) |
814 | 0 | { |
815 | 0 | const nsTableCellFrame* firstCellInFlow = |
816 | 0 | static_cast<nsTableCellFrame*>(aCellFrame.FirstInFlow()); |
817 | 0 | nsTableFrame* firstTableInFlow = |
818 | 0 | static_cast<nsTableFrame*>(aTableFrame.FirstInFlow()); |
819 | 0 | nsTableRowFrame* row = |
820 | 0 | static_cast<nsTableRowFrame*>(firstCellInFlow->GetParent()); |
821 | 0 | nsTableRowGroupFrame* firstRGInFlow = |
822 | 0 | static_cast<nsTableRowGroupFrame*>(row->GetParent()); |
823 | 0 |
|
824 | 0 | uint32_t rowIndex = firstCellInFlow->RowIndex(); |
825 | 0 | int32_t rowSpan = aTableFrame.GetEffectiveRowSpan(*firstCellInFlow); |
826 | 0 |
|
827 | 0 | nscoord computedBSize = firstTableInFlow->GetRowSpacing(rowIndex, |
828 | 0 | rowIndex + rowSpan - 1); |
829 | 0 | computedBSize -= aBlockDirBorderPadding; |
830 | 0 | uint32_t rowX; |
831 | 0 | for (row = firstRGInFlow->GetFirstRow(), rowX = 0; row; row = row->GetNextRow(), rowX++) { |
832 | 0 | if (rowX > rowIndex + rowSpan - 1) { |
833 | 0 | break; |
834 | 0 | } |
835 | 0 | else if (rowX >= rowIndex) { |
836 | 0 | computedBSize += row->GetUnpaginatedBSize(); |
837 | 0 | } |
838 | 0 | } |
839 | 0 | return computedBSize; |
840 | 0 | } |
841 | | |
842 | | void |
843 | | nsTableCellFrame::Reflow(nsPresContext* aPresContext, |
844 | | ReflowOutput& aDesiredSize, |
845 | | const ReflowInput& aReflowInput, |
846 | | nsReflowStatus& aStatus) |
847 | 0 | { |
848 | 0 | MarkInReflow(); |
849 | 0 | DO_GLOBAL_REFLOW_COUNT("nsTableCellFrame"); |
850 | 0 | DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); |
851 | 0 | MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); |
852 | 0 |
|
853 | 0 | if (aReflowInput.mFlags.mSpecialBSizeReflow) { |
854 | 0 | FirstInFlow()->AddStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW); |
855 | 0 | } |
856 | 0 |
|
857 | 0 | // see if a special bsize reflow needs to occur due to having a pct height |
858 | 0 | nsTableFrame::CheckRequestSpecialBSizeReflow(aReflowInput); |
859 | 0 |
|
860 | 0 | WritingMode wm = aReflowInput.GetWritingMode(); |
861 | 0 | LogicalSize availSize(wm, aReflowInput.AvailableISize(), |
862 | 0 | aReflowInput.AvailableBSize()); |
863 | 0 |
|
864 | 0 | LogicalMargin borderPadding = aReflowInput.ComputedLogicalPadding(); |
865 | 0 | LogicalMargin border = GetBorderWidth(wm); |
866 | 0 | borderPadding += border; |
867 | 0 |
|
868 | 0 | // reduce available space by insets, if we're in a constrained situation |
869 | 0 | availSize.ISize(wm) -= borderPadding.IStartEnd(wm); |
870 | 0 | if (NS_UNCONSTRAINEDSIZE != availSize.BSize(wm)) { |
871 | 0 | availSize.BSize(wm) -= borderPadding.BStartEnd(wm); |
872 | 0 | } |
873 | 0 |
|
874 | 0 | // Try to reflow the child into the available space. It might not |
875 | 0 | // fit or might need continuing. |
876 | 0 | if (availSize.BSize(wm) < 0) { |
877 | 0 | availSize.BSize(wm) = 1; |
878 | 0 | } |
879 | 0 |
|
880 | 0 | ReflowOutput kidSize(wm); |
881 | 0 | kidSize.ClearSize(); |
882 | 0 | SetPriorAvailISize(aReflowInput.AvailableISize()); |
883 | 0 | nsIFrame* firstKid = mFrames.FirstChild(); |
884 | 0 | NS_ASSERTION(firstKid, "Frame construction error, a table cell always has an inner cell frame"); |
885 | 0 | nsTableFrame* tableFrame = GetTableFrame(); |
886 | 0 |
|
887 | 0 | if (aReflowInput.mFlags.mSpecialBSizeReflow) { |
888 | 0 | const_cast<ReflowInput&>(aReflowInput). |
889 | 0 | SetComputedBSize(BSize(wm) - borderPadding.BStartEnd(wm)); |
890 | 0 | DISPLAY_REFLOW_CHANGE(); |
891 | 0 | } |
892 | 0 | else if (aPresContext->IsPaginated()) { |
893 | 0 | nscoord computedUnpaginatedBSize = |
894 | 0 | CalcUnpaginatedBSize((nsTableCellFrame&)*this, |
895 | 0 | *tableFrame, borderPadding.BStartEnd(wm)); |
896 | 0 | if (computedUnpaginatedBSize > 0) { |
897 | 0 | const_cast<ReflowInput&>(aReflowInput).SetComputedBSize(computedUnpaginatedBSize); |
898 | 0 | DISPLAY_REFLOW_CHANGE(); |
899 | 0 | } |
900 | 0 | } |
901 | 0 | else { |
902 | 0 | SetHasPctOverBSize(false); |
903 | 0 | } |
904 | 0 |
|
905 | 0 | WritingMode kidWM = firstKid->GetWritingMode(); |
906 | 0 | ReflowInput kidReflowInput(aPresContext, aReflowInput, firstKid, |
907 | 0 | availSize.ConvertTo(kidWM, wm)); |
908 | 0 |
|
909 | 0 | // Don't be a percent height observer if we're in the middle of |
910 | 0 | // special-bsize reflow, in case we get an accidental NotifyPercentBSize() |
911 | 0 | // call (which we shouldn't honor during special-bsize reflow) |
912 | 0 | if (!aReflowInput.mFlags.mSpecialBSizeReflow) { |
913 | 0 | // mPercentBSizeObserver is for children of cells in quirks mode, |
914 | 0 | // but only those than are tables in standards mode. NeedsToObserve |
915 | 0 | // will determine how far this is propagated to descendants. |
916 | 0 | kidReflowInput.mPercentBSizeObserver = this; |
917 | 0 | } |
918 | 0 | // Don't propagate special bsize reflow state to our kids |
919 | 0 | kidReflowInput.mFlags.mSpecialBSizeReflow = false; |
920 | 0 |
|
921 | 0 | if (aReflowInput.mFlags.mSpecialBSizeReflow || |
922 | 0 | FirstInFlow()->HasAnyStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) { |
923 | 0 | // We need to force the kid to have mBResize set if we've had a |
924 | 0 | // special reflow in the past, since the non-special reflow needs to |
925 | 0 | // resize back to what it was without the special bsize reflow. |
926 | 0 | kidReflowInput.SetBResize(true); |
927 | 0 | } |
928 | 0 |
|
929 | 0 | nsSize containerSize = |
930 | 0 | aReflowInput.ComputedSizeAsContainerIfConstrained(); |
931 | 0 |
|
932 | 0 | LogicalPoint kidOrigin(wm, borderPadding.IStart(wm), |
933 | 0 | borderPadding.BStart(wm)); |
934 | 0 | nsRect origRect = firstKid->GetRect(); |
935 | 0 | nsRect origVisualOverflow = firstKid->GetVisualOverflowRect(); |
936 | 0 | bool firstReflow = firstKid->HasAnyStateBits(NS_FRAME_FIRST_REFLOW); |
937 | 0 |
|
938 | 0 | ReflowChild(firstKid, aPresContext, kidSize, kidReflowInput, |
939 | 0 | wm, kidOrigin, containerSize, 0, aStatus); |
940 | 0 | if (aStatus.IsOverflowIncomplete()) { |
941 | 0 | // Don't pass OVERFLOW_INCOMPLETE through tables until they can actually handle it |
942 | 0 | //XXX should paginate overflow as overflow, but not in this patch (bug 379349) |
943 | 0 | aStatus.SetIncomplete(); |
944 | 0 | printf("Set table cell incomplete %p\n", static_cast<void*>(this)); |
945 | 0 | } |
946 | 0 |
|
947 | 0 | // XXXbz is this invalidate actually needed, really? |
948 | 0 | if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) { |
949 | 0 | InvalidateFrameSubtree(); |
950 | 0 | } |
951 | 0 |
|
952 | | #ifdef DEBUG |
953 | | DebugCheckChildSize(firstKid, kidSize); |
954 | | #endif |
955 | |
|
956 | 0 | // 0 dimensioned cells need to be treated specially in Standard/NavQuirks mode |
957 | 0 | // see testcase "emptyCells.html" |
958 | 0 | nsIFrame* prevInFlow = GetPrevInFlow(); |
959 | 0 | bool isEmpty; |
960 | 0 | if (prevInFlow) { |
961 | 0 | isEmpty = static_cast<nsTableCellFrame*>(prevInFlow)->GetContentEmpty(); |
962 | 0 | } else { |
963 | 0 | isEmpty = !CellHasVisibleContent(kidSize.Height(), tableFrame, firstKid); |
964 | 0 | } |
965 | 0 | SetContentEmpty(isEmpty); |
966 | 0 |
|
967 | 0 | // Place the child |
968 | 0 | FinishReflowChild(firstKid, aPresContext, kidSize, &kidReflowInput, |
969 | 0 | wm, kidOrigin, containerSize, 0); |
970 | 0 |
|
971 | 0 | if (tableFrame->IsBorderCollapse()) { |
972 | 0 | nsTableFrame::InvalidateTableFrame(firstKid, origRect, origVisualOverflow, |
973 | 0 | firstReflow); |
974 | 0 | } |
975 | 0 | // first, compute the bsize which can be set w/o being restricted by |
976 | 0 | // available bsize |
977 | 0 | LogicalSize cellSize(wm); |
978 | 0 | cellSize.BSize(wm) = kidSize.BSize(wm); |
979 | 0 |
|
980 | 0 | if (NS_UNCONSTRAINEDSIZE != cellSize.BSize(wm)) { |
981 | 0 | cellSize.BSize(wm) += borderPadding.BStartEnd(wm); |
982 | 0 | } |
983 | 0 |
|
984 | 0 | // next determine the cell's isize |
985 | 0 | cellSize.ISize(wm) = kidSize.ISize(wm); // at this point, we've factored in the cell's style attributes |
986 | 0 |
|
987 | 0 | // factor in border and padding |
988 | 0 | if (NS_UNCONSTRAINEDSIZE != cellSize.ISize(wm)) { |
989 | 0 | cellSize.ISize(wm) += borderPadding.IStartEnd(wm); |
990 | 0 | } |
991 | 0 |
|
992 | 0 | // set the cell's desired size and max element size |
993 | 0 | aDesiredSize.SetSize(wm, cellSize); |
994 | 0 |
|
995 | 0 | // the overflow area will be computed when BlockDirAlignChild() gets called |
996 | 0 |
|
997 | 0 | if (aReflowInput.mFlags.mSpecialBSizeReflow) { |
998 | 0 | if (aDesiredSize.BSize(wm) > BSize(wm)) { |
999 | 0 | // set a bit indicating that the pct bsize contents exceeded |
1000 | 0 | // the height that they could honor in the pass 2 reflow |
1001 | 0 | SetHasPctOverBSize(true); |
1002 | 0 | } |
1003 | 0 | if (NS_UNCONSTRAINEDSIZE == aReflowInput.AvailableBSize()) { |
1004 | 0 | aDesiredSize.BSize(wm) = BSize(wm); |
1005 | 0 | } |
1006 | 0 | } |
1007 | 0 |
|
1008 | 0 | // If our parent is in initial reflow, it'll handle invalidating our |
1009 | 0 | // entire overflow rect. |
1010 | 0 | if (!GetParent()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW) && |
1011 | 0 | nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) { |
1012 | 0 | InvalidateFrame(); |
1013 | 0 | } |
1014 | 0 |
|
1015 | 0 | // remember the desired size for this reflow |
1016 | 0 | SetDesiredSize(aDesiredSize); |
1017 | 0 |
|
1018 | 0 | // Any absolutely-positioned children will get reflowed in |
1019 | 0 | // nsFrame::FixupPositionedTableParts in another pass, so propagate our |
1020 | 0 | // dirtiness to them before our parent clears our dirty bits. |
1021 | 0 | PushDirtyBitToAbsoluteFrames(); |
1022 | 0 |
|
1023 | 0 | NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize); |
1024 | 0 | } |
1025 | | |
1026 | | /* ----- global methods ----- */ |
1027 | | |
1028 | 0 | NS_QUERYFRAME_HEAD(nsTableCellFrame) |
1029 | 0 | NS_QUERYFRAME_ENTRY(nsTableCellFrame) |
1030 | 0 | NS_QUERYFRAME_ENTRY(nsITableCellLayout) |
1031 | 0 | NS_QUERYFRAME_ENTRY(nsIPercentBSizeObserver) |
1032 | 0 | NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) |
1033 | | |
1034 | | #ifdef ACCESSIBILITY |
1035 | | a11y::AccType |
1036 | | nsTableCellFrame::AccessibleType() |
1037 | 0 | { |
1038 | 0 | return a11y::eHTMLTableCellType; |
1039 | 0 | } |
1040 | | #endif |
1041 | | |
1042 | | /* This is primarily for editor access via nsITableLayout */ |
1043 | | NS_IMETHODIMP |
1044 | | nsTableCellFrame::GetCellIndexes(int32_t &aRowIndex, int32_t &aColIndex) |
1045 | 0 | { |
1046 | 0 | aRowIndex = RowIndex(); |
1047 | 0 | aColIndex = mColIndex; |
1048 | 0 | return NS_OK; |
1049 | 0 | } |
1050 | | |
1051 | | nsTableCellFrame* |
1052 | | NS_NewTableCellFrame(nsIPresShell* aPresShell, |
1053 | | ComputedStyle* aStyle, |
1054 | | nsTableFrame* aTableFrame) |
1055 | 0 | { |
1056 | 0 | if (aTableFrame->IsBorderCollapse()) |
1057 | 0 | return new (aPresShell) nsBCTableCellFrame(aStyle, aTableFrame); |
1058 | 0 | else |
1059 | 0 | return new (aPresShell) nsTableCellFrame(aStyle, aTableFrame); |
1060 | 0 | } |
1061 | | |
1062 | | NS_IMPL_FRAMEARENA_HELPERS(nsBCTableCellFrame) |
1063 | | |
1064 | | LogicalMargin |
1065 | | nsTableCellFrame::GetBorderWidth(WritingMode aWM) const |
1066 | 0 | { |
1067 | 0 | return LogicalMargin(aWM, StyleBorder()->GetComputedBorder()); |
1068 | 0 | } |
1069 | | |
1070 | | void |
1071 | | nsTableCellFrame::AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) |
1072 | 0 | { |
1073 | 0 | nsIFrame* kid = mFrames.FirstChild(); |
1074 | 0 | MOZ_ASSERT(kid && !kid->GetNextSibling(), |
1075 | 0 | "Table cells should have just one child"); |
1076 | 0 | aResult.AppendElement(OwnedAnonBox(kid)); |
1077 | 0 | } |
1078 | | |
1079 | | #ifdef DEBUG_FRAME_DUMP |
1080 | | nsresult |
1081 | | nsTableCellFrame::GetFrameName(nsAString& aResult) const |
1082 | | { |
1083 | | return MakeFrameName(NS_LITERAL_STRING("TableCell"), aResult); |
1084 | | } |
1085 | | #endif |
1086 | | |
1087 | | // nsBCTableCellFrame |
1088 | | |
1089 | | nsBCTableCellFrame::nsBCTableCellFrame(ComputedStyle* aStyle, |
1090 | | nsTableFrame* aTableFrame) |
1091 | | : nsTableCellFrame(aStyle, aTableFrame, kClassID) |
1092 | 0 | { |
1093 | 0 | mBStartBorder = mIEndBorder = mBEndBorder = mIStartBorder = 0; |
1094 | 0 | } |
1095 | | |
1096 | | nsBCTableCellFrame::~nsBCTableCellFrame() |
1097 | | { |
1098 | | } |
1099 | | |
1100 | | /* virtual */ nsMargin |
1101 | | nsBCTableCellFrame::GetUsedBorder() const |
1102 | 0 | { |
1103 | 0 | WritingMode wm = GetWritingMode(); |
1104 | 0 | return GetBorderWidth(wm).GetPhysicalMargin(wm); |
1105 | 0 | } |
1106 | | |
1107 | | #ifdef DEBUG_FRAME_DUMP |
1108 | | nsresult |
1109 | | nsBCTableCellFrame::GetFrameName(nsAString& aResult) const |
1110 | | { |
1111 | | return MakeFrameName(NS_LITERAL_STRING("BCTableCell"), aResult); |
1112 | | } |
1113 | | #endif |
1114 | | |
1115 | | LogicalMargin |
1116 | | nsBCTableCellFrame::GetBorderWidth(WritingMode aWM) const |
1117 | 0 | { |
1118 | 0 | int32_t d2a = PresContext()->AppUnitsPerDevPixel(); |
1119 | 0 | return LogicalMargin(aWM, |
1120 | 0 | BC_BORDER_END_HALF_COORD(d2a, mBStartBorder), |
1121 | 0 | BC_BORDER_START_HALF_COORD(d2a, mIEndBorder), |
1122 | 0 | BC_BORDER_START_HALF_COORD(d2a, mBEndBorder), |
1123 | 0 | BC_BORDER_END_HALF_COORD(d2a, mIStartBorder)); |
1124 | 0 | } |
1125 | | |
1126 | | BCPixelSize |
1127 | | nsBCTableCellFrame::GetBorderWidth(LogicalSide aSide) const |
1128 | | { |
1129 | | switch(aSide) { |
1130 | | case eLogicalSideBStart: |
1131 | | return BC_BORDER_END_HALF(mBStartBorder); |
1132 | | case eLogicalSideIEnd: |
1133 | | return BC_BORDER_START_HALF(mIEndBorder); |
1134 | | case eLogicalSideBEnd: |
1135 | | return BC_BORDER_START_HALF(mBEndBorder); |
1136 | | default: |
1137 | | return BC_BORDER_END_HALF(mIStartBorder); |
1138 | | } |
1139 | | } |
1140 | | |
1141 | | void |
1142 | | nsBCTableCellFrame::SetBorderWidth(LogicalSide aSide, BCPixelSize aValue) |
1143 | | { |
1144 | | switch(aSide) { |
1145 | | case eLogicalSideBStart: |
1146 | | mBStartBorder = aValue; |
1147 | | break; |
1148 | | case eLogicalSideIEnd: |
1149 | | mIEndBorder = aValue; |
1150 | | break; |
1151 | | case eLogicalSideBEnd: |
1152 | | mBEndBorder = aValue; |
1153 | | break; |
1154 | | default: |
1155 | | mIStartBorder = aValue; |
1156 | | } |
1157 | | } |
1158 | | |
1159 | | /* virtual */ nsMargin |
1160 | | nsBCTableCellFrame::GetBorderOverflow() |
1161 | 0 | { |
1162 | 0 | WritingMode wm = GetWritingMode(); |
1163 | 0 | int32_t d2a = PresContext()->AppUnitsPerDevPixel(); |
1164 | 0 | LogicalMargin halfBorder(wm, |
1165 | 0 | BC_BORDER_START_HALF_COORD(d2a, mBStartBorder), |
1166 | 0 | BC_BORDER_END_HALF_COORD(d2a, mIEndBorder), |
1167 | 0 | BC_BORDER_END_HALF_COORD(d2a, mBEndBorder), |
1168 | 0 | BC_BORDER_START_HALF_COORD(d2a, mIStartBorder)); |
1169 | 0 | return halfBorder.GetPhysicalMargin(wm); |
1170 | 0 | } |
1171 | | |
1172 | | ImgDrawResult |
1173 | | nsBCTableCellFrame::PaintBackground(gfxContext& aRenderingContext, |
1174 | | const nsRect& aDirtyRect, |
1175 | | nsPoint aPt, |
1176 | | uint32_t aFlags) |
1177 | 0 | { |
1178 | 0 | // make border-width reflect the half of the border-collapse |
1179 | 0 | // assigned border that's inside the cell |
1180 | 0 | WritingMode wm = GetWritingMode(); |
1181 | 0 | nsMargin borderWidth = GetBorderWidth(wm).GetPhysicalMargin(wm); |
1182 | 0 |
|
1183 | 0 | nsStyleBorder myBorder(*StyleBorder()); |
1184 | 0 |
|
1185 | 0 | NS_FOR_CSS_SIDES(side) { |
1186 | 0 | myBorder.SetBorderWidth(side, borderWidth.Side(side)); |
1187 | 0 | } |
1188 | 0 |
|
1189 | 0 | // bypassing nsCSSRendering::PaintBackground is safe because this kind |
1190 | 0 | // of frame cannot be used for the root element |
1191 | 0 | nsRect rect(aPt, GetSize()); |
1192 | 0 | nsCSSRendering::PaintBGParams params = |
1193 | 0 | nsCSSRendering::PaintBGParams::ForAllLayers(*PresContext(), |
1194 | 0 | aDirtyRect, |
1195 | 0 | rect, this, |
1196 | 0 | aFlags); |
1197 | 0 | return nsCSSRendering::PaintStyleImageLayerWithSC(params, aRenderingContext, Style(), |
1198 | 0 | myBorder); |
1199 | 0 | } |