Bug 1692937 [wpt PR 27636] - new parameter --include-file for wptrunner, a=testonly
[gecko.git] / layout / generic / nsFrameSetFrame.cpp
blobac5cb62b900c72b25b1102c8fec88715591171a1
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* rendering object for HTML <frameset> elements */
9 #include "nsFrameSetFrame.h"
11 #include "gfxContext.h"
12 #include "gfxUtils.h"
13 #include "mozilla/ComputedStyle.h"
14 #include "mozilla/DebugOnly.h"
15 #include "mozilla/gfx/2D.h"
16 #include "mozilla/gfx/Helpers.h"
17 #include "mozilla/Likely.h"
18 #include "mozilla/PresShell.h"
19 #include "mozilla/PresShellInlines.h"
21 #include "nsGenericHTMLElement.h"
22 #include "nsAttrValueInlines.h"
23 #include "nsLeafFrame.h"
24 #include "nsContainerFrame.h"
25 #include "nsLayoutUtils.h"
26 #include "nsPresContext.h"
27 #include "nsIContentInlines.h"
28 #include "nsGkAtoms.h"
29 #include "nsStyleConsts.h"
30 #include "nsHTMLParts.h"
31 #include "nsNameSpaceManager.h"
32 #include "nsCSSAnonBoxes.h"
33 #include "mozilla/ServoStyleSet.h"
34 #include "mozilla/ServoStyleSetInlines.h"
35 #include "mozilla/dom/Element.h"
36 #include "nsDisplayList.h"
37 #include "mozAutoDocUpdate.h"
38 #include "mozilla/Preferences.h"
39 #include "mozilla/dom/ChildIterator.h"
40 #include "mozilla/dom/HTMLFrameSetElement.h"
41 #include "mozilla/LookAndFeel.h"
42 #include "mozilla/MouseEvents.h"
43 #include "nsSubDocumentFrame.h"
45 using namespace mozilla;
46 using namespace mozilla::dom;
47 using namespace mozilla::gfx;
49 // masks for mEdgeVisibility
50 #define LEFT_VIS 0x0001
51 #define RIGHT_VIS 0x0002
52 #define TOP_VIS 0x0004
53 #define BOTTOM_VIS 0x0008
54 #define ALL_VIS 0x000F
55 #define NONE_VIS 0x0000
57 /*******************************************************************************
58 * nsFramesetDrag
59 ******************************************************************************/
60 nsFramesetDrag::nsFramesetDrag() { UnSet(); }
62 void nsFramesetDrag::Reset(bool aVertical, int32_t aIndex, int32_t aChange,
63 nsHTMLFramesetFrame* aSource) {
64 mVertical = aVertical;
65 mIndex = aIndex;
66 mChange = aChange;
67 mSource = aSource;
70 void nsFramesetDrag::UnSet() {
71 mVertical = true;
72 mIndex = -1;
73 mChange = 0;
74 mSource = nullptr;
77 /*******************************************************************************
78 * nsHTMLFramesetBorderFrame
79 ******************************************************************************/
80 class nsHTMLFramesetBorderFrame final : public nsLeafFrame {
81 public:
82 NS_DECL_FRAMEARENA_HELPERS(nsHTMLFramesetBorderFrame)
84 #ifdef DEBUG_FRAME_DUMP
85 virtual nsresult GetFrameName(nsAString& aResult) const override;
86 #endif
88 virtual nsresult HandleEvent(nsPresContext* aPresContext,
89 WidgetGUIEvent* aEvent,
90 nsEventStatus* aEventStatus) override;
92 Maybe<Cursor> GetCursor(const nsPoint&) override;
94 virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
95 const nsDisplayListSet& aLists) override;
97 virtual void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
98 const ReflowInput& aReflowInput,
99 nsReflowStatus& aStatus) override;
101 bool GetVisibility() { return mVisibility; }
102 void SetVisibility(bool aVisibility);
103 void SetColor(nscolor aColor);
105 void PaintBorder(DrawTarget* aDrawTarget, nsPoint aPt);
107 protected:
108 nsHTMLFramesetBorderFrame(ComputedStyle*, nsPresContext*, int32_t aWidth,
109 bool aVertical, bool aVisible);
110 virtual ~nsHTMLFramesetBorderFrame();
111 virtual nscoord GetIntrinsicISize() override;
112 virtual nscoord GetIntrinsicBSize() override;
114 // the prev and next neighbors are indexes into the row (for a horizontal
115 // border) or col (for a vertical border) of nsHTMLFramesetFrames or
116 // nsHTMLFrames
117 int32_t mPrevNeighbor;
118 int32_t mNextNeighbor;
119 nscolor mColor;
120 int32_t mWidth;
121 bool mVertical;
122 bool mVisibility;
123 bool mCanResize;
124 friend class nsHTMLFramesetFrame;
126 /*******************************************************************************
127 * nsHTMLFramesetBlankFrame
128 ******************************************************************************/
129 class nsHTMLFramesetBlankFrame final : public nsLeafFrame {
130 public:
131 NS_DECL_QUERYFRAME
132 NS_DECL_FRAMEARENA_HELPERS(nsHTMLFramesetBlankFrame)
134 #ifdef DEBUG_FRAME_DUMP
135 virtual nsresult GetFrameName(nsAString& aResult) const override {
136 return MakeFrameName(u"FramesetBlank"_ns, aResult);
138 #endif
140 virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
141 const nsDisplayListSet& aLists) override;
143 virtual void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
144 const ReflowInput& aReflowInput,
145 nsReflowStatus& aStatus) override;
147 protected:
148 explicit nsHTMLFramesetBlankFrame(ComputedStyle* aStyle,
149 nsPresContext* aPresContext)
150 : nsLeafFrame(aStyle, aPresContext, kClassID) {}
152 virtual ~nsHTMLFramesetBlankFrame();
153 virtual nscoord GetIntrinsicISize() override;
154 virtual nscoord GetIntrinsicBSize() override;
156 friend class nsHTMLFramesetFrame;
157 friend class nsHTMLFrameset;
160 /*******************************************************************************
161 * nsHTMLFramesetFrame
162 ******************************************************************************/
163 bool nsHTMLFramesetFrame::gDragInProgress = false;
164 #define DEFAULT_BORDER_WIDTH_PX 6
166 nsHTMLFramesetFrame::nsHTMLFramesetFrame(ComputedStyle* aStyle,
167 nsPresContext* aPresContext)
168 : nsContainerFrame(aStyle, aPresContext, kClassID) {
169 mNumRows = 0;
170 mNumCols = 0;
171 mEdgeVisibility = 0;
172 mParentFrameborder = eFrameborder_Yes; // default
173 mParentBorderWidth = -1; // default not set
174 mParentBorderColor = NO_COLOR; // default not set
175 mFirstDragPoint.x = mFirstDragPoint.y = 0;
176 mMinDrag = nsPresContext::CSSPixelsToAppUnits(2);
177 mNonBorderChildCount = 0;
178 mNonBlankChildCount = 0;
179 mDragger = nullptr;
180 mChildCount = 0;
181 mTopLevelFrameset = nullptr;
182 mEdgeColors.Set(NO_COLOR);
185 nsHTMLFramesetFrame::~nsHTMLFramesetFrame() = default;
187 NS_QUERYFRAME_HEAD(nsHTMLFramesetFrame)
188 NS_QUERYFRAME_ENTRY(nsHTMLFramesetFrame)
189 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
191 void nsHTMLFramesetFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
192 nsIFrame* aPrevInFlow) {
193 nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
194 // find the highest ancestor that is a frameset
195 nsIFrame* parentFrame = GetParent();
196 mTopLevelFrameset = this;
197 while (parentFrame) {
198 nsHTMLFramesetFrame* frameset = do_QueryFrame(parentFrame);
199 if (frameset) {
200 mTopLevelFrameset = frameset;
201 parentFrame = parentFrame->GetParent();
202 } else {
203 break;
207 nsPresContext* presContext = PresContext();
208 mozilla::PresShell* presShell = presContext->PresShell();
210 nsFrameborder frameborder = GetFrameBorder();
211 int32_t borderWidth = GetBorderWidth(presContext, false);
212 nscolor borderColor = GetBorderColor();
214 // Get the rows= cols= data
215 HTMLFrameSetElement* ourContent = HTMLFrameSetElement::FromNode(mContent);
216 NS_ASSERTION(ourContent, "Someone gave us a broken frameset element!");
217 const nsFramesetSpec* rowSpecs = nullptr;
218 const nsFramesetSpec* colSpecs = nullptr;
219 // GetRowSpec and GetColSpec can fail, but when they do they set
220 // mNumRows and mNumCols respectively to 0, so we deal with it fine.
221 ourContent->GetRowSpec(&mNumRows, &rowSpecs);
222 ourContent->GetColSpec(&mNumCols, &colSpecs);
224 static_assert(
225 NS_MAX_FRAMESET_SPEC_COUNT < UINT_MAX / sizeof(nscoord),
226 "Maximum value of mNumRows and mNumCols is NS_MAX_FRAMESET_SPEC_COUNT");
227 mRowSizes = MakeUnique<nscoord[]>(mNumRows);
228 mColSizes = MakeUnique<nscoord[]>(mNumCols);
230 static_assert(
231 NS_MAX_FRAMESET_SPEC_COUNT < INT32_MAX / NS_MAX_FRAMESET_SPEC_COUNT,
232 "Should not overflow numCells");
233 int32_t numCells = mNumRows * mNumCols;
235 static_assert(NS_MAX_FRAMESET_SPEC_COUNT <
236 UINT_MAX / sizeof(nsHTMLFramesetBorderFrame*),
237 "Should not overflow nsHTMLFramesetBorderFrame");
238 mVerBorders = MakeUnique<nsHTMLFramesetBorderFrame*[]>(
239 mNumCols); // 1 more than number of ver borders
241 for (int verX = 0; verX < mNumCols; verX++) mVerBorders[verX] = nullptr;
243 mHorBorders = MakeUnique<nsHTMLFramesetBorderFrame*[]>(
244 mNumRows); // 1 more than number of hor borders
246 for (int horX = 0; horX < mNumRows; horX++) mHorBorders[horX] = nullptr;
248 static_assert(NS_MAX_FRAMESET_SPEC_COUNT <
249 UINT_MAX / sizeof(int32_t) / NS_MAX_FRAMESET_SPEC_COUNT,
250 "Should not overflow numCells");
251 static_assert(NS_MAX_FRAMESET_SPEC_COUNT < UINT_MAX / sizeof(nsFrameborder) /
252 NS_MAX_FRAMESET_SPEC_COUNT,
253 "Should not overflow numCells");
254 static_assert(NS_MAX_FRAMESET_SPEC_COUNT < UINT_MAX / sizeof(nsBorderColor) /
255 NS_MAX_FRAMESET_SPEC_COUNT,
256 "Should not overflow numCells");
257 mChildFrameborder = MakeUnique<nsFrameborder[]>(numCells);
258 mChildBorderColors = MakeUnique<nsBorderColor[]>(numCells);
260 // create the children frames; skip content which isn't <frameset> or <frame>
261 mChildCount = 0; // number of <frame> or <frameset> children
263 FlattenedChildIterator children(mContent);
264 for (nsIContent* child = children.GetNextChild(); child;
265 child = children.GetNextChild()) {
266 if (mChildCount == numCells) {
267 // we have more <frame> or <frameset> than cells
268 // Clear the lazy bits in the remaining children. Also clear
269 // the restyle flags, like nsCSSFrameConstructor::ProcessChildren does.
270 for (; child; child = child->GetNextSibling()) {
271 child->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
273 break;
275 child->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
277 // IMPORTANT: This must match the conditions in
278 // nsCSSFrameConstructor::ContentAppended/Inserted/Removed
279 if (!child->IsAnyOfHTMLElements(nsGkAtoms::frameset, nsGkAtoms::frame)) {
280 continue;
283 // FIXME(emilio): This doesn't even respect display: none, but that matches
284 // other browsers ;_;
286 // Maybe we should change that though.
287 RefPtr<ComputedStyle> kidStyle =
288 ServoStyleSet::ResolveServoStyle(*child->AsElement());
289 nsIFrame* frame;
290 if (child->IsHTMLElement(nsGkAtoms::frameset)) {
291 frame = NS_NewHTMLFramesetFrame(presShell, kidStyle);
293 nsHTMLFramesetFrame* childFrame = (nsHTMLFramesetFrame*)frame;
294 childFrame->SetParentFrameborder(frameborder);
295 childFrame->SetParentBorderWidth(borderWidth);
296 childFrame->SetParentBorderColor(borderColor);
297 frame->Init(child, this, nullptr);
299 mChildBorderColors[mChildCount].Set(childFrame->GetBorderColor());
300 } else { // frame
301 frame = NS_NewSubDocumentFrame(presShell, kidStyle);
303 frame->Init(child, this, nullptr);
305 mChildFrameborder[mChildCount] = GetFrameBorder(child);
306 mChildBorderColors[mChildCount].Set(GetBorderColor(child));
308 child->SetPrimaryFrame(frame);
310 mFrames.AppendFrame(nullptr, frame);
312 mChildCount++;
315 mNonBlankChildCount = mChildCount;
316 // add blank frames for frameset cells that had no content provided
317 for (int blankX = mChildCount; blankX < numCells; blankX++) {
318 RefPtr<ComputedStyle> pseudoComputedStyle =
319 presShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle(
320 PseudoStyleType::framesetBlank);
322 // XXX the blank frame is using the content of its parent - at some point it
323 // should just have null content, if we support that
324 nsHTMLFramesetBlankFrame* blankFrame = new (presShell)
325 nsHTMLFramesetBlankFrame(pseudoComputedStyle, PresContext());
327 blankFrame->Init(mContent, this, nullptr);
329 mFrames.AppendFrame(nullptr, blankFrame);
331 mChildBorderColors[mChildCount].Set(NO_COLOR);
332 mChildCount++;
335 mNonBorderChildCount = mChildCount;
338 void nsHTMLFramesetFrame::SetInitialChildList(ChildListID aListID,
339 nsFrameList& aChildList) {
340 // We do this weirdness where we create our child frames in Init(). On the
341 // other hand, we're going to get a SetInitialChildList() with an empty list
342 // and null list name after the frame constructor is done creating us. So
343 // just ignore that call.
344 if (aListID == kPrincipalList && aChildList.IsEmpty()) {
345 return;
348 nsContainerFrame::SetInitialChildList(aListID, aChildList);
351 // XXX should this try to allocate twips based on an even pixel boundary?
352 void nsHTMLFramesetFrame::Scale(nscoord aDesired, int32_t aNumIndicies,
353 int32_t* aIndicies, int32_t aNumItems,
354 int32_t* aItems) {
355 int32_t actual = 0;
356 int32_t i, j;
357 // get the actual total
358 for (i = 0; i < aNumIndicies; i++) {
359 j = aIndicies[i];
360 actual += aItems[j];
363 if (actual > 0) {
364 float factor = (float)aDesired / (float)actual;
365 actual = 0;
366 // scale the items up or down
367 for (i = 0; i < aNumIndicies; i++) {
368 j = aIndicies[i];
369 aItems[j] = NSToCoordRound((float)aItems[j] * factor);
370 actual += aItems[j];
372 } else if (aNumIndicies != 0) {
373 // All the specs say zero width, but we have to fill up space
374 // somehow. Distribute it equally.
375 nscoord width = NSToCoordRound((float)aDesired / (float)aNumIndicies);
376 actual = width * aNumIndicies;
377 for (i = 0; i < aNumIndicies; i++) {
378 aItems[aIndicies[i]] = width;
382 if (aNumIndicies > 0 && aDesired != actual) {
383 int32_t unit = (aDesired > actual) ? 1 : -1;
384 for (i = 0; (i < aNumIndicies) && (aDesired != actual); i++) {
385 j = aIndicies[i];
386 if (j < aNumItems) {
387 aItems[j] += unit;
388 actual += unit;
395 * Translate the rows/cols specs into an array of integer sizes for
396 * each cell in the frameset. Sizes are allocated based on the priorities of the
397 * specifier - fixed sizes have the highest priority, percentage sizes have the
398 * next highest priority and relative sizes have the lowest.
400 void nsHTMLFramesetFrame::CalculateRowCol(nsPresContext* aPresContext,
401 nscoord aSize, int32_t aNumSpecs,
402 const nsFramesetSpec* aSpecs,
403 nscoord* aValues) {
404 static_assert(NS_MAX_FRAMESET_SPEC_COUNT < UINT_MAX / sizeof(int32_t),
405 "aNumSpecs maximum value is NS_MAX_FRAMESET_SPEC_COUNT");
407 int32_t fixedTotal = 0;
408 int32_t numFixed = 0;
409 auto fixed = MakeUnique<int32_t[]>(aNumSpecs);
410 int32_t numPercent = 0;
411 auto percent = MakeUnique<int32_t[]>(aNumSpecs);
412 int32_t relativeSums = 0;
413 int32_t numRelative = 0;
414 auto relative = MakeUnique<int32_t[]>(aNumSpecs);
416 if (MOZ_UNLIKELY(!fixed || !percent || !relative)) {
417 return; // NS_ERROR_OUT_OF_MEMORY
420 int32_t i, j;
422 // initialize the fixed, percent, relative indices, allocate the fixed sizes
423 // and zero the others
424 for (i = 0; i < aNumSpecs; i++) {
425 aValues[i] = 0;
426 switch (aSpecs[i].mUnit) {
427 case eFramesetUnit_Fixed:
428 aValues[i] = nsPresContext::CSSPixelsToAppUnits(aSpecs[i].mValue);
429 fixedTotal += aValues[i];
430 fixed[numFixed] = i;
431 numFixed++;
432 break;
433 case eFramesetUnit_Percent:
434 percent[numPercent] = i;
435 numPercent++;
436 break;
437 case eFramesetUnit_Relative:
438 relative[numRelative] = i;
439 numRelative++;
440 relativeSums += aSpecs[i].mValue;
441 break;
445 // scale the fixed sizes if they total too much (or too little and there
446 // aren't any percent or relative)
447 if ((fixedTotal > aSize) ||
448 ((fixedTotal < aSize) && (0 == numPercent) && (0 == numRelative))) {
449 Scale(aSize, numFixed, fixed.get(), aNumSpecs, aValues);
450 return;
453 int32_t percentMax = aSize - fixedTotal;
454 int32_t percentTotal = 0;
455 // allocate the percentage sizes from what is left over from the fixed
456 // allocation
457 for (i = 0; i < numPercent; i++) {
458 j = percent[i];
459 aValues[j] =
460 NSToCoordRound((float)aSpecs[j].mValue * (float)aSize / 100.0f);
461 percentTotal += aValues[j];
464 // scale the percent sizes if they total too much (or too little and there
465 // aren't any relative)
466 if ((percentTotal > percentMax) ||
467 ((percentTotal < percentMax) && (0 == numRelative))) {
468 Scale(percentMax, numPercent, percent.get(), aNumSpecs, aValues);
469 return;
472 int32_t relativeMax = percentMax - percentTotal;
473 int32_t relativeTotal = 0;
474 // allocate the relative sizes from what is left over from the percent
475 // allocation
476 for (i = 0; i < numRelative; i++) {
477 j = relative[i];
478 aValues[j] = NSToCoordRound((float)aSpecs[j].mValue * (float)relativeMax /
479 (float)relativeSums);
480 relativeTotal += aValues[j];
483 // scale the relative sizes if they take up too much or too little
484 if (relativeTotal != relativeMax) {
485 Scale(relativeMax, numRelative, relative.get(), aNumSpecs, aValues);
490 * Translate the rows/cols integer sizes into an array of specs for
491 * each cell in the frameset. Reverse of CalculateRowCol() behaviour.
492 * This allows us to maintain the user size info through reflows.
494 void nsHTMLFramesetFrame::GenerateRowCol(nsPresContext* aPresContext,
495 nscoord aSize, int32_t aNumSpecs,
496 const nsFramesetSpec* aSpecs,
497 nscoord* aValues, nsString& aNewAttr) {
498 int32_t i;
500 for (i = 0; i < aNumSpecs; i++) {
501 if (!aNewAttr.IsEmpty()) aNewAttr.Append(char16_t(','));
503 switch (aSpecs[i].mUnit) {
504 case eFramesetUnit_Fixed:
505 aNewAttr.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(aValues[i]));
506 break;
507 case eFramesetUnit_Percent: // XXX Only accurate to 1%, need 1 pixel
508 case eFramesetUnit_Relative:
509 // Add 0.5 to the percentage to make rounding work right.
510 aNewAttr.AppendInt(uint32_t((100.0 * aValues[i]) / aSize + 0.5));
511 aNewAttr.Append(char16_t('%'));
512 break;
517 int32_t nsHTMLFramesetFrame::GetBorderWidth(nsPresContext* aPresContext,
518 bool aTakeForcingIntoAccount) {
519 nsFrameborder frameborder = GetFrameBorder();
520 if (frameborder == eFrameborder_No) {
521 return 0;
523 nsGenericHTMLElement* content = nsGenericHTMLElement::FromNode(mContent);
525 if (content) {
526 const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::border);
527 if (attr) {
528 int32_t intVal = 0;
529 if (attr->Type() == nsAttrValue::eInteger) {
530 intVal = attr->GetIntegerValue();
531 if (intVal < 0) {
532 intVal = 0;
536 return nsPresContext::CSSPixelsToAppUnits(intVal);
540 if (mParentBorderWidth >= 0) {
541 return mParentBorderWidth;
544 return nsPresContext::CSSPixelsToAppUnits(DEFAULT_BORDER_WIDTH_PX);
547 void nsHTMLFramesetFrame::GetDesiredSize(nsPresContext* aPresContext,
548 const ReflowInput& aReflowInput,
549 ReflowOutput& aDesiredSize) {
550 WritingMode wm = aReflowInput.GetWritingMode();
551 LogicalSize desiredSize(wm);
552 nsHTMLFramesetFrame* framesetParent = do_QueryFrame(GetParent());
553 if (nullptr == framesetParent) {
554 if (aPresContext->IsPaginated()) {
555 // XXX This needs to be changed when framesets paginate properly
556 desiredSize.ISize(wm) = aReflowInput.AvailableISize();
557 desiredSize.BSize(wm) = aReflowInput.AvailableBSize();
558 } else {
559 LogicalSize area(wm, aPresContext->GetVisibleArea().Size());
561 desiredSize.ISize(wm) = area.ISize(wm);
562 desiredSize.BSize(wm) = area.BSize(wm);
564 } else {
565 LogicalSize size(wm);
566 framesetParent->GetSizeOfChild(this, wm, size);
567 desiredSize.ISize(wm) = size.ISize(wm);
568 desiredSize.BSize(wm) = size.BSize(wm);
570 aDesiredSize.SetSize(wm, desiredSize);
573 // only valid for non border children
574 void nsHTMLFramesetFrame::GetSizeOfChildAt(int32_t aIndexInParent,
575 WritingMode aWM, LogicalSize& aSize,
576 nsIntPoint& aCellIndex) {
577 int32_t row = aIndexInParent / mNumCols;
578 int32_t col = aIndexInParent -
579 (row * mNumCols); // remainder from dividing index by mNumCols
580 if ((row < mNumRows) && (col < mNumCols)) {
581 aSize.ISize(aWM) = mColSizes[col];
582 aSize.BSize(aWM) = mRowSizes[row];
583 aCellIndex.x = col;
584 aCellIndex.y = row;
585 } else {
586 aSize.SizeTo(aWM, 0, 0);
587 aCellIndex.x = aCellIndex.y = 0;
591 // only valid for non border children
592 void nsHTMLFramesetFrame::GetSizeOfChild(nsIFrame* aChild, WritingMode aWM,
593 LogicalSize& aSize) {
594 // Reflow only creates children frames for <frameset> and <frame> content.
595 // this assumption is used here
596 int i = 0;
597 for (nsIFrame* child : mFrames) {
598 if (aChild == child) {
599 nsIntPoint ignore;
600 GetSizeOfChildAt(i, aWM, aSize, ignore);
601 return;
603 i++;
605 aSize.SizeTo(aWM, 0, 0);
608 nsresult nsHTMLFramesetFrame::HandleEvent(nsPresContext* aPresContext,
609 WidgetGUIEvent* aEvent,
610 nsEventStatus* aEventStatus) {
611 NS_ENSURE_ARG_POINTER(aEventStatus);
612 if (mDragger) {
613 // the nsFramesetBorderFrame has captured NS_MOUSE_DOWN
614 switch (aEvent->mMessage) {
615 case eMouseMove:
616 MouseDrag(aPresContext, aEvent);
617 break;
618 case eMouseUp:
619 if (aEvent->AsMouseEvent()->mButton == MouseButton::ePrimary) {
620 EndMouseDrag(aPresContext);
622 break;
623 default:
624 break;
626 *aEventStatus = nsEventStatus_eConsumeNoDefault;
627 } else {
628 *aEventStatus = nsEventStatus_eIgnore;
630 return NS_OK;
633 Maybe<nsIFrame::Cursor> nsHTMLFramesetFrame::GetCursor(const nsPoint&) {
634 auto kind = StyleCursorKind::Default;
635 if (mDragger) {
636 kind = mDragger->mVertical ? StyleCursorKind::EwResize
637 : StyleCursorKind::NsResize;
639 return Some(Cursor{kind, AllowCustomCursorImage::No});
642 void nsHTMLFramesetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
643 const nsDisplayListSet& aLists) {
644 BuildDisplayListForInline(aBuilder, aLists);
646 if (mDragger && aBuilder->IsForEventDelivery()) {
647 aLists.Content()->AppendNewToTop<nsDisplayEventReceiver>(aBuilder, this);
651 void nsHTMLFramesetFrame::ReflowPlaceChild(nsIFrame* aChild,
652 nsPresContext* aPresContext,
653 const ReflowInput& aReflowInput,
654 nsPoint& aOffset, nsSize& aSize,
655 nsIntPoint* aCellIndex) {
656 // reflow the child
657 ReflowInput reflowInput(aPresContext, aReflowInput, aChild,
658 LogicalSize(aChild->GetWritingMode(), aSize));
659 reflowInput.SetComputedWidth(std::max(
661 aSize.width - reflowInput.ComputedPhysicalBorderPadding().LeftRight()));
662 reflowInput.SetComputedHeight(std::max(
664 aSize.height - reflowInput.ComputedPhysicalBorderPadding().TopBottom()));
665 ReflowOutput reflowOutput(aReflowInput);
666 reflowOutput.Width() = aSize.width;
667 reflowOutput.Height() = aSize.height;
668 nsReflowStatus status;
670 ReflowChild(aChild, aPresContext, reflowOutput, reflowInput, aOffset.x,
671 aOffset.y, ReflowChildFlags::Default, status);
672 NS_ASSERTION(status.IsComplete(), "bad status");
674 // Place and size the child
675 reflowOutput.Width() = aSize.width;
676 reflowOutput.Height() = aSize.height;
677 FinishReflowChild(aChild, aPresContext, reflowOutput, &reflowInput, aOffset.x,
678 aOffset.y, ReflowChildFlags::Default);
681 static nsFrameborder GetFrameBorderHelper(nsGenericHTMLElement* aContent) {
682 if (nullptr != aContent) {
683 const nsAttrValue* attr = aContent->GetParsedAttr(nsGkAtoms::frameborder);
684 if (attr && attr->Type() == nsAttrValue::eEnum) {
685 switch (attr->GetEnumValue()) {
686 case NS_STYLE_FRAME_YES:
687 case NS_STYLE_FRAME_1:
688 return eFrameborder_Yes;
690 case NS_STYLE_FRAME_NO:
691 case NS_STYLE_FRAME_0:
692 return eFrameborder_No;
696 return eFrameborder_Notset;
699 nsFrameborder nsHTMLFramesetFrame::GetFrameBorder() {
700 nsFrameborder result = eFrameborder_Notset;
701 nsGenericHTMLElement* content = nsGenericHTMLElement::FromNode(mContent);
703 if (content) {
704 result = GetFrameBorderHelper(content);
706 if (eFrameborder_Notset == result) {
707 return mParentFrameborder;
709 return result;
712 nsFrameborder nsHTMLFramesetFrame::GetFrameBorder(nsIContent* aContent) {
713 nsFrameborder result = eFrameborder_Notset;
715 nsGenericHTMLElement* content = nsGenericHTMLElement::FromNode(aContent);
717 if (content) {
718 result = GetFrameBorderHelper(content);
720 if (eFrameborder_Notset == result) {
721 return GetFrameBorder();
723 return result;
726 nscolor nsHTMLFramesetFrame::GetBorderColor() {
727 nsGenericHTMLElement* content = nsGenericHTMLElement::FromNode(mContent);
729 if (content) {
730 const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::bordercolor);
731 if (attr) {
732 nscolor color;
733 if (attr->GetColorValue(color)) {
734 return color;
739 return mParentBorderColor;
742 nscolor nsHTMLFramesetFrame::GetBorderColor(nsIContent* aContent) {
743 nsGenericHTMLElement* content = nsGenericHTMLElement::FromNode(aContent);
745 if (content) {
746 const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::bordercolor);
747 if (attr) {
748 nscolor color;
749 if (attr->GetColorValue(color)) {
750 return color;
754 return GetBorderColor();
757 void nsHTMLFramesetFrame::Reflow(nsPresContext* aPresContext,
758 ReflowOutput& aDesiredSize,
759 const ReflowInput& aReflowInput,
760 nsReflowStatus& aStatus) {
761 MarkInReflow();
762 DO_GLOBAL_REFLOW_COUNT("nsHTMLFramesetFrame");
763 DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
764 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
766 mozilla::PresShell* presShell = aPresContext->PresShell();
767 ServoStyleSet* styleSet = presShell->StyleSet();
769 GetParent()->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
771 // printf("FramesetFrame2::Reflow %X (%d,%d) \n", this,
772 // aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight());
773 // Always get the size so that the caller knows how big we are
774 GetDesiredSize(aPresContext, aReflowInput, aDesiredSize);
776 nscoord width = (aDesiredSize.Width() <= aReflowInput.AvailableWidth())
777 ? aDesiredSize.Width()
778 : aReflowInput.AvailableWidth();
779 nscoord height = (aDesiredSize.Height() <= aReflowInput.AvailableHeight())
780 ? aDesiredSize.Height()
781 : aReflowInput.AvailableHeight();
783 // We might be reflowed more than once with NS_FRAME_FIRST_REFLOW;
784 // that's allowed. (Though it will only happen for misuse of frameset
785 // that includes it within other content.) So measure firstTime by
786 // what we care about, which is whether we've processed the data we
787 // process below if firstTime is true.
788 MOZ_ASSERT(!mChildFrameborder == !mChildBorderColors);
789 bool firstTime = !!mChildFrameborder;
791 // subtract out the width of all of the potential borders. There are
792 // only borders between <frame>s. There are none on the edges (e.g the
793 // leftmost <frame> has no left border).
794 int32_t borderWidth = GetBorderWidth(aPresContext, true);
796 width -= (mNumCols - 1) * borderWidth;
797 if (width < 0) width = 0;
799 height -= (mNumRows - 1) * borderWidth;
800 if (height < 0) height = 0;
802 HTMLFrameSetElement* ourContent = HTMLFrameSetElement::FromNode(mContent);
803 NS_ASSERTION(ourContent, "Someone gave us a broken frameset element!");
804 const nsFramesetSpec* rowSpecs = nullptr;
805 const nsFramesetSpec* colSpecs = nullptr;
806 int32_t rows = 0;
807 int32_t cols = 0;
808 ourContent->GetRowSpec(&rows, &rowSpecs);
809 ourContent->GetColSpec(&cols, &colSpecs);
810 // If the number of cols or rows has changed, the frame for the frameset
811 // will be re-created.
812 if (mNumRows != rows || mNumCols != cols) {
813 mDrag.UnSet();
814 NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
815 return;
818 CalculateRowCol(aPresContext, width, mNumCols, colSpecs, mColSizes.get());
819 CalculateRowCol(aPresContext, height, mNumRows, rowSpecs, mRowSizes.get());
821 UniquePtr<bool[]> verBordersVis; // vertical borders visibility
822 UniquePtr<nscolor[]> verBorderColors;
823 UniquePtr<bool[]> horBordersVis; // horizontal borders visibility
824 UniquePtr<nscolor[]> horBorderColors;
825 nscolor borderColor = GetBorderColor();
826 nsFrameborder frameborder = GetFrameBorder();
828 if (firstTime) {
829 // Check for overflow in memory allocations using mNumCols and mNumRows
830 // which have a maxium value of NS_MAX_FRAMESET_SPEC_COUNT.
831 static_assert(NS_MAX_FRAMESET_SPEC_COUNT < UINT_MAX / sizeof(bool),
832 "Check for overflow");
833 static_assert(NS_MAX_FRAMESET_SPEC_COUNT < UINT_MAX / sizeof(nscolor),
834 "Check for overflow");
836 verBordersVis = MakeUnique<bool[]>(mNumCols);
837 verBorderColors = MakeUnique<nscolor[]>(mNumCols);
838 for (int verX = 0; verX < mNumCols; verX++) {
839 verBordersVis[verX] = false;
840 verBorderColors[verX] = NO_COLOR;
843 horBordersVis = MakeUnique<bool[]>(mNumRows);
844 horBorderColors = MakeUnique<nscolor[]>(mNumRows);
845 for (int horX = 0; horX < mNumRows; horX++) {
846 horBordersVis[horX] = false;
847 horBorderColors[horX] = NO_COLOR;
851 // reflow the children
852 int32_t lastRow = 0;
853 int32_t lastCol = 0;
854 int32_t borderChildX = mNonBorderChildCount; // index of border children
855 nsHTMLFramesetBorderFrame* borderFrame = nullptr;
856 nsPoint offset(0, 0);
857 nsSize size, lastSize;
858 WritingMode wm = GetWritingMode();
859 LogicalSize logicalSize(wm);
860 nsIFrame* child = mFrames.FirstChild();
862 for (int32_t childX = 0; childX < mNonBorderChildCount; childX++) {
863 nsIntPoint cellIndex;
864 GetSizeOfChildAt(childX, wm, logicalSize, cellIndex);
865 size = logicalSize.GetPhysicalSize(wm);
867 if (lastRow != cellIndex.y) { // changed to next row
868 offset.x = 0;
869 offset.y += lastSize.height;
870 if (firstTime) { // create horizontal border
872 RefPtr<ComputedStyle> pseudoComputedStyle;
873 pseudoComputedStyle = styleSet->ResolveNonInheritingAnonymousBoxStyle(
874 PseudoStyleType::horizontalFramesetBorder);
876 borderFrame = new (presShell) nsHTMLFramesetBorderFrame(
877 pseudoComputedStyle, PresContext(), borderWidth, false, false);
878 borderFrame->Init(mContent, this, nullptr);
879 mChildCount++;
880 mFrames.AppendFrame(nullptr, borderFrame);
881 mHorBorders[cellIndex.y - 1] = borderFrame;
882 // set the neighbors for determining drag boundaries
883 borderFrame->mPrevNeighbor = lastRow;
884 borderFrame->mNextNeighbor = cellIndex.y;
885 } else {
886 borderFrame = (nsHTMLFramesetBorderFrame*)mFrames.FrameAt(borderChildX);
887 borderFrame->mWidth = borderWidth;
888 borderChildX++;
890 nsSize borderSize(aDesiredSize.Width(), borderWidth);
891 ReflowPlaceChild(borderFrame, aPresContext, aReflowInput, offset,
892 borderSize);
893 borderFrame = nullptr;
894 offset.y += borderWidth;
895 } else {
896 if (cellIndex.x > 0) { // moved to next col in same row
897 if (0 == cellIndex.y) { // in 1st row
898 if (firstTime) { // create vertical border
900 RefPtr<ComputedStyle> pseudoComputedStyle;
901 pseudoComputedStyle =
902 styleSet->ResolveNonInheritingAnonymousBoxStyle(
903 PseudoStyleType::verticalFramesetBorder);
905 borderFrame = new (presShell) nsHTMLFramesetBorderFrame(
906 pseudoComputedStyle, PresContext(), borderWidth, true, false);
907 borderFrame->Init(mContent, this, nullptr);
908 mChildCount++;
909 mFrames.AppendFrame(nullptr, borderFrame);
910 mVerBorders[cellIndex.x - 1] = borderFrame;
911 // set the neighbors for determining drag boundaries
912 borderFrame->mPrevNeighbor = lastCol;
913 borderFrame->mNextNeighbor = cellIndex.x;
914 } else {
915 borderFrame =
916 (nsHTMLFramesetBorderFrame*)mFrames.FrameAt(borderChildX);
917 borderFrame->mWidth = borderWidth;
918 borderChildX++;
920 nsSize borderSize(borderWidth, aDesiredSize.Height());
921 ReflowPlaceChild(borderFrame, aPresContext, aReflowInput, offset,
922 borderSize);
923 borderFrame = nullptr;
925 offset.x += borderWidth;
929 ReflowPlaceChild(child, aPresContext, aReflowInput, offset, size,
930 &cellIndex);
932 if (firstTime) {
933 int32_t childVis;
934 nsHTMLFramesetFrame* framesetFrame = do_QueryFrame(child);
935 if (framesetFrame) {
936 childVis = framesetFrame->mEdgeVisibility;
937 mChildBorderColors[childX] = framesetFrame->mEdgeColors;
938 } else if (child->IsSubDocumentFrame()) {
939 if (eFrameborder_Yes == mChildFrameborder[childX]) {
940 childVis = ALL_VIS;
941 } else if (eFrameborder_No == mChildFrameborder[childX]) {
942 childVis = NONE_VIS;
943 } else { // notset
944 childVis = (eFrameborder_No == frameborder) ? NONE_VIS : ALL_VIS;
946 } else { // blank
947 #ifdef DEBUG
948 nsHTMLFramesetBlankFrame* blank = do_QueryFrame(child);
949 MOZ_ASSERT(blank, "unexpected child frame type");
950 #endif
951 childVis = NONE_VIS;
953 nsBorderColor childColors = mChildBorderColors[childX];
954 // set the visibility, color of our edge borders based on children
955 if (0 == cellIndex.x) {
956 if (!(mEdgeVisibility & LEFT_VIS)) {
957 mEdgeVisibility |= (LEFT_VIS & childVis);
959 if (NO_COLOR == mEdgeColors.mLeft) {
960 mEdgeColors.mLeft = childColors.mLeft;
963 if (0 == cellIndex.y) {
964 if (!(mEdgeVisibility & TOP_VIS)) {
965 mEdgeVisibility |= (TOP_VIS & childVis);
967 if (NO_COLOR == mEdgeColors.mTop) {
968 mEdgeColors.mTop = childColors.mTop;
971 if (mNumCols - 1 == cellIndex.x) {
972 if (!(mEdgeVisibility & RIGHT_VIS)) {
973 mEdgeVisibility |= (RIGHT_VIS & childVis);
975 if (NO_COLOR == mEdgeColors.mRight) {
976 mEdgeColors.mRight = childColors.mRight;
979 if (mNumRows - 1 == cellIndex.y) {
980 if (!(mEdgeVisibility & BOTTOM_VIS)) {
981 mEdgeVisibility |= (BOTTOM_VIS & childVis);
983 if (NO_COLOR == mEdgeColors.mBottom) {
984 mEdgeColors.mBottom = childColors.mBottom;
987 // set the visibility of borders that the child may affect
988 if (childVis & RIGHT_VIS) {
989 verBordersVis[cellIndex.x] = true;
991 if (childVis & BOTTOM_VIS) {
992 horBordersVis[cellIndex.y] = true;
994 if ((cellIndex.x > 0) && (childVis & LEFT_VIS)) {
995 verBordersVis[cellIndex.x - 1] = true;
997 if ((cellIndex.y > 0) && (childVis & TOP_VIS)) {
998 horBordersVis[cellIndex.y - 1] = true;
1000 // set the colors of borders that the child may affect
1001 if (NO_COLOR == verBorderColors[cellIndex.x]) {
1002 verBorderColors[cellIndex.x] = mChildBorderColors[childX].mRight;
1004 if (NO_COLOR == horBorderColors[cellIndex.y]) {
1005 horBorderColors[cellIndex.y] = mChildBorderColors[childX].mBottom;
1007 if ((cellIndex.x > 0) && (NO_COLOR == verBorderColors[cellIndex.x - 1])) {
1008 verBorderColors[cellIndex.x - 1] = mChildBorderColors[childX].mLeft;
1010 if ((cellIndex.y > 0) && (NO_COLOR == horBorderColors[cellIndex.y - 1])) {
1011 horBorderColors[cellIndex.y - 1] = mChildBorderColors[childX].mTop;
1014 lastRow = cellIndex.y;
1015 lastCol = cellIndex.x;
1016 lastSize = size;
1017 offset.x += size.width;
1018 child = child->GetNextSibling();
1021 if (firstTime) {
1022 nscolor childColor;
1023 // set the visibility, color, mouse sensitivity of borders
1024 for (int verX = 0; verX < mNumCols - 1; verX++) {
1025 if (mVerBorders[verX]) {
1026 mVerBorders[verX]->SetVisibility(verBordersVis[verX]);
1027 SetBorderResize(mVerBorders[verX]);
1028 childColor = (NO_COLOR == verBorderColors[verX])
1029 ? borderColor
1030 : verBorderColors[verX];
1031 mVerBorders[verX]->SetColor(childColor);
1034 for (int horX = 0; horX < mNumRows - 1; horX++) {
1035 if (mHorBorders[horX]) {
1036 mHorBorders[horX]->SetVisibility(horBordersVis[horX]);
1037 SetBorderResize(mHorBorders[horX]);
1038 childColor = (NO_COLOR == horBorderColors[horX])
1039 ? borderColor
1040 : horBorderColors[horX];
1041 mHorBorders[horX]->SetColor(childColor);
1045 mChildFrameborder.reset();
1046 mChildBorderColors.reset();
1049 mDrag.UnSet();
1051 aDesiredSize.SetOverflowAreasToDesiredBounds();
1052 FinishAndStoreOverflow(&aDesiredSize);
1054 NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
1057 #ifdef DEBUG_FRAME_DUMP
1058 nsresult nsHTMLFramesetFrame::GetFrameName(nsAString& aResult) const {
1059 return MakeFrameName(u"Frameset"_ns, aResult);
1061 #endif
1063 bool nsHTMLFramesetFrame::CanResize(bool aVertical, bool aLeft) {
1064 int32_t childX;
1065 int32_t startX;
1066 if (aVertical) {
1067 startX = (aLeft) ? 0 : mNumCols - 1;
1068 for (childX = startX; childX < mNonBorderChildCount; childX += mNumCols) {
1069 if (!CanChildResize(aVertical, aLeft, childX)) {
1070 return false;
1073 } else {
1074 startX = (aLeft) ? 0 : (mNumRows - 1) * mNumCols;
1075 int32_t endX = startX + mNumCols;
1076 for (childX = startX; childX < endX; childX++) {
1077 if (!CanChildResize(aVertical, aLeft, childX)) {
1078 return false;
1082 return true;
1085 bool nsHTMLFramesetFrame::GetNoResize(nsIFrame* aChildFrame) {
1086 nsIContent* content = aChildFrame->GetContent();
1088 return content && content->IsElement() &&
1089 content->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::noresize);
1092 bool nsHTMLFramesetFrame::CanChildResize(bool aVertical, bool aLeft,
1093 int32_t aChildX) {
1094 nsIFrame* child = mFrames.FrameAt(aChildX);
1095 nsHTMLFramesetFrame* frameset = do_QueryFrame(child);
1096 return frameset ? frameset->CanResize(aVertical, aLeft) : !GetNoResize(child);
1099 // This calculates and sets the resizability of all border frames
1101 void nsHTMLFramesetFrame::RecalculateBorderResize() {
1102 if (!mContent) {
1103 return;
1106 static_assert(
1107 NS_MAX_FRAMESET_SPEC_COUNT < INT32_MAX / NS_MAX_FRAMESET_SPEC_COUNT,
1108 "Check for overflow");
1109 static_assert(NS_MAX_FRAMESET_SPEC_COUNT <
1110 UINT_MAX / sizeof(int32_t) / NS_MAX_FRAMESET_SPEC_COUNT,
1111 "Check for overflow");
1112 // set the visibility and mouse sensitivity of borders
1113 int32_t verX;
1114 for (verX = 0; verX < mNumCols - 1; verX++) {
1115 if (mVerBorders[verX]) {
1116 mVerBorders[verX]->mCanResize = true;
1117 SetBorderResize(mVerBorders[verX]);
1120 int32_t horX;
1121 for (horX = 0; horX < mNumRows - 1; horX++) {
1122 if (mHorBorders[horX]) {
1123 mHorBorders[horX]->mCanResize = true;
1124 SetBorderResize(mHorBorders[horX]);
1129 void nsHTMLFramesetFrame::SetBorderResize(
1130 nsHTMLFramesetBorderFrame* aBorderFrame) {
1131 if (aBorderFrame->mVertical) {
1132 for (int rowX = 0; rowX < mNumRows; rowX++) {
1133 int32_t childX = aBorderFrame->mPrevNeighbor + (rowX * mNumCols);
1134 if (!CanChildResize(true, false, childX) ||
1135 !CanChildResize(true, true, childX + 1)) {
1136 aBorderFrame->mCanResize = false;
1139 } else {
1140 int32_t childX = aBorderFrame->mPrevNeighbor * mNumCols;
1141 int32_t endX = childX + mNumCols;
1142 for (; childX < endX; childX++) {
1143 if (!CanChildResize(false, false, childX)) {
1144 aBorderFrame->mCanResize = false;
1147 endX = endX + mNumCols;
1148 for (; childX < endX; childX++) {
1149 if (!CanChildResize(false, true, childX)) {
1150 aBorderFrame->mCanResize = false;
1156 void nsHTMLFramesetFrame::StartMouseDrag(nsPresContext* aPresContext,
1157 nsHTMLFramesetBorderFrame* aBorder,
1158 WidgetGUIEvent* aEvent) {
1159 #if 0
1160 int32_t index;
1161 IndexOf(aBorder, index);
1162 NS_ASSERTION((nullptr != aBorder) && (index >= 0), "invalid dragger");
1163 #endif
1165 PresShell::SetCapturingContent(GetContent(),
1166 CaptureFlags::IgnoreAllowedState);
1168 mDragger = aBorder;
1170 mFirstDragPoint = aEvent->mRefPoint;
1172 // Store the original frame sizes
1173 if (mDragger->mVertical) {
1174 mPrevNeighborOrigSize = mColSizes[mDragger->mPrevNeighbor];
1175 mNextNeighborOrigSize = mColSizes[mDragger->mNextNeighbor];
1176 } else {
1177 mPrevNeighborOrigSize = mRowSizes[mDragger->mPrevNeighbor];
1178 mNextNeighborOrigSize = mRowSizes[mDragger->mNextNeighbor];
1181 gDragInProgress = true;
1184 void nsHTMLFramesetFrame::MouseDrag(nsPresContext* aPresContext,
1185 WidgetGUIEvent* aEvent) {
1186 // if the capture ended, reset the drag state
1187 if (PresShell::GetCapturingContent() != GetContent()) {
1188 mDragger = nullptr;
1189 gDragInProgress = false;
1190 return;
1193 int32_t change; // measured positive from left-to-right or top-to-bottom
1194 AutoWeakFrame weakFrame(this);
1195 if (mDragger->mVertical) {
1196 change = aPresContext->DevPixelsToAppUnits(aEvent->mRefPoint.x -
1197 mFirstDragPoint.x);
1198 if (change > mNextNeighborOrigSize - mMinDrag) {
1199 change = mNextNeighborOrigSize - mMinDrag;
1200 } else if (change <= mMinDrag - mPrevNeighborOrigSize) {
1201 change = mMinDrag - mPrevNeighborOrigSize;
1203 mColSizes[mDragger->mPrevNeighbor] = mPrevNeighborOrigSize + change;
1204 mColSizes[mDragger->mNextNeighbor] = mNextNeighborOrigSize - change;
1206 if (change != 0) {
1207 // Recompute the specs from the new sizes.
1208 nscoord width =
1209 mRect.width - (mNumCols - 1) * GetBorderWidth(aPresContext, true);
1210 HTMLFrameSetElement* ourContent = HTMLFrameSetElement::FromNode(mContent);
1211 NS_ASSERTION(ourContent, "Someone gave us a broken frameset element!");
1212 const nsFramesetSpec* colSpecs = nullptr;
1213 ourContent->GetColSpec(&mNumCols, &colSpecs);
1214 nsAutoString newColAttr;
1215 GenerateRowCol(aPresContext, width, mNumCols, colSpecs, mColSizes.get(),
1216 newColAttr);
1217 // Setting the attr will trigger a reflow
1218 mContent->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::cols,
1219 newColAttr, true);
1221 } else {
1222 change = aPresContext->DevPixelsToAppUnits(aEvent->mRefPoint.y -
1223 mFirstDragPoint.y);
1224 if (change > mNextNeighborOrigSize - mMinDrag) {
1225 change = mNextNeighborOrigSize - mMinDrag;
1226 } else if (change <= mMinDrag - mPrevNeighborOrigSize) {
1227 change = mMinDrag - mPrevNeighborOrigSize;
1229 mRowSizes[mDragger->mPrevNeighbor] = mPrevNeighborOrigSize + change;
1230 mRowSizes[mDragger->mNextNeighbor] = mNextNeighborOrigSize - change;
1232 if (change != 0) {
1233 // Recompute the specs from the new sizes.
1234 nscoord height =
1235 mRect.height - (mNumRows - 1) * GetBorderWidth(aPresContext, true);
1236 HTMLFrameSetElement* ourContent = HTMLFrameSetElement::FromNode(mContent);
1237 NS_ASSERTION(ourContent, "Someone gave us a broken frameset element!");
1238 const nsFramesetSpec* rowSpecs = nullptr;
1239 ourContent->GetRowSpec(&mNumRows, &rowSpecs);
1240 nsAutoString newRowAttr;
1241 GenerateRowCol(aPresContext, height, mNumRows, rowSpecs, mRowSizes.get(),
1242 newRowAttr);
1243 // Setting the attr will trigger a reflow
1244 mContent->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::rows,
1245 newRowAttr, true);
1249 NS_ENSURE_TRUE_VOID(weakFrame.IsAlive());
1250 if (change != 0) {
1251 mDrag.Reset(mDragger->mVertical, mDragger->mPrevNeighbor, change, this);
1255 void nsHTMLFramesetFrame::EndMouseDrag(nsPresContext* aPresContext) {
1256 PresShell::ReleaseCapturingContent();
1257 mDragger = nullptr;
1258 gDragInProgress = false;
1261 nsIFrame* NS_NewHTMLFramesetFrame(PresShell* aPresShell,
1262 ComputedStyle* aStyle) {
1263 #ifdef DEBUG
1264 const nsStyleDisplay* disp = aStyle->StyleDisplay();
1265 NS_ASSERTION(!disp->IsAbsolutelyPositionedStyle() && !disp->IsFloatingStyle(),
1266 "Framesets should not be positioned and should not float");
1267 #endif
1269 return new (aPresShell)
1270 nsHTMLFramesetFrame(aStyle, aPresShell->GetPresContext());
1273 NS_IMPL_FRAMEARENA_HELPERS(nsHTMLFramesetFrame)
1275 /*******************************************************************************
1276 * nsHTMLFramesetBorderFrame
1277 ******************************************************************************/
1278 nsHTMLFramesetBorderFrame::nsHTMLFramesetBorderFrame(
1279 ComputedStyle* aStyle, nsPresContext* aPresContext, int32_t aWidth,
1280 bool aVertical, bool aVisibility)
1281 : nsLeafFrame(aStyle, aPresContext, kClassID),
1282 mWidth(aWidth),
1283 mVertical(aVertical),
1284 mVisibility(aVisibility) {
1285 mCanResize = true;
1286 mColor = NO_COLOR;
1287 mPrevNeighbor = 0;
1288 mNextNeighbor = 0;
1291 nsHTMLFramesetBorderFrame::~nsHTMLFramesetBorderFrame() {
1292 // printf("nsHTMLFramesetBorderFrame destructor %p \n", this);
1295 NS_IMPL_FRAMEARENA_HELPERS(nsHTMLFramesetBorderFrame)
1297 nscoord nsHTMLFramesetBorderFrame::GetIntrinsicISize() {
1298 // No intrinsic width
1299 return 0;
1302 nscoord nsHTMLFramesetBorderFrame::GetIntrinsicBSize() {
1303 // No intrinsic height
1304 return 0;
1307 void nsHTMLFramesetBorderFrame::SetVisibility(bool aVisibility) {
1308 mVisibility = aVisibility;
1311 void nsHTMLFramesetBorderFrame::SetColor(nscolor aColor) { mColor = aColor; }
1313 void nsHTMLFramesetBorderFrame::Reflow(nsPresContext* aPresContext,
1314 ReflowOutput& aDesiredSize,
1315 const ReflowInput& aReflowInput,
1316 nsReflowStatus& aStatus) {
1317 DO_GLOBAL_REFLOW_COUNT("nsHTMLFramesetBorderFrame");
1318 DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
1319 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
1321 // Override Reflow(), since we don't want to deal with what our
1322 // computed values are.
1323 SizeToAvailSize(aReflowInput, aDesiredSize);
1325 aDesiredSize.SetOverflowAreasToDesiredBounds();
1328 class nsDisplayFramesetBorder : public nsPaintedDisplayItem {
1329 public:
1330 nsDisplayFramesetBorder(nsDisplayListBuilder* aBuilder,
1331 nsHTMLFramesetBorderFrame* aFrame)
1332 : nsPaintedDisplayItem(aBuilder, aFrame) {
1333 MOZ_COUNT_CTOR(nsDisplayFramesetBorder);
1335 MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayFramesetBorder)
1337 // REVIEW: see old GetFrameForPoint
1338 // Receives events in its bounds
1339 virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
1340 HitTestState* aState,
1341 nsTArray<nsIFrame*>* aOutFrames) override {
1342 aOutFrames->AppendElement(mFrame);
1344 virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
1345 NS_DISPLAY_DECL_NAME("FramesetBorder", TYPE_FRAMESET_BORDER)
1348 void nsDisplayFramesetBorder::Paint(nsDisplayListBuilder* aBuilder,
1349 gfxContext* aCtx) {
1350 static_cast<nsHTMLFramesetBorderFrame*>(mFrame)->PaintBorder(
1351 aCtx->GetDrawTarget(), ToReferenceFrame());
1354 void nsHTMLFramesetBorderFrame::BuildDisplayList(
1355 nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) {
1356 aLists.Content()->AppendNewToTop<nsDisplayFramesetBorder>(aBuilder, this);
1359 void nsHTMLFramesetBorderFrame::PaintBorder(DrawTarget* aDrawTarget,
1360 nsPoint aPt) {
1361 nscoord widthInPixels = nsPresContext::AppUnitsToIntCSSPixels(mWidth);
1362 nscoord pixelWidth = nsPresContext::CSSPixelsToAppUnits(1);
1364 if (widthInPixels <= 0) return;
1366 ColorPattern bgColor(ToDeviceColor(LookAndFeel::GetColor(
1367 LookAndFeel::ColorID::WidgetBackground, NS_RGB(200, 200, 200))));
1369 ColorPattern fgColor(ToDeviceColor(LookAndFeel::GetColor(
1370 LookAndFeel::ColorID::WidgetForeground, NS_RGB(0, 0, 0))));
1372 ColorPattern hltColor(ToDeviceColor(LookAndFeel::GetColor(
1373 LookAndFeel::ColorID::Widget3DHighlight, NS_RGB(255, 255, 255))));
1375 ColorPattern sdwColor(ToDeviceColor(LookAndFeel::GetColor(
1376 LookAndFeel::ColorID::Widget3DShadow, NS_RGB(128, 128, 128))));
1378 ColorPattern color(ToDeviceColor(NS_RGB(255, 255, 255))); // default to white
1379 if (mVisibility) {
1380 color =
1381 (NO_COLOR == mColor) ? bgColor : ColorPattern(ToDeviceColor(mColor));
1384 int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
1386 Point toRefFrame = NSPointToPoint(aPt, appUnitsPerDevPixel);
1388 AutoRestoreTransform autoRestoreTransform(aDrawTarget);
1389 aDrawTarget->SetTransform(
1390 aDrawTarget->GetTransform().PreTranslate(toRefFrame));
1392 nsPoint start(0, 0);
1393 nsPoint end = mVertical ? nsPoint(0, mRect.height) : nsPoint(mRect.width, 0);
1395 // draw grey or white first
1396 for (int i = 0; i < widthInPixels; i++) {
1397 StrokeLineWithSnapping(start, end, appUnitsPerDevPixel, *aDrawTarget,
1398 color);
1399 if (mVertical) {
1400 start.x += pixelWidth;
1401 end.x = start.x;
1402 } else {
1403 start.y += pixelWidth;
1404 end.y = start.y;
1408 if (!mVisibility) return;
1410 if (widthInPixels >= 5) {
1411 start.x = (mVertical) ? pixelWidth : 0;
1412 start.y = (mVertical) ? 0 : pixelWidth;
1413 end.x = (mVertical) ? start.x : mRect.width;
1414 end.y = (mVertical) ? mRect.height : start.y;
1415 StrokeLineWithSnapping(start, end, appUnitsPerDevPixel, *aDrawTarget,
1416 hltColor);
1419 if (widthInPixels >= 2) {
1420 start.x = (mVertical) ? mRect.width - (2 * pixelWidth) : 0;
1421 start.y = (mVertical) ? 0 : mRect.height - (2 * pixelWidth);
1422 end.x = (mVertical) ? start.x : mRect.width;
1423 end.y = (mVertical) ? mRect.height : start.y;
1424 StrokeLineWithSnapping(start, end, appUnitsPerDevPixel, *aDrawTarget,
1425 sdwColor);
1428 if (widthInPixels >= 1) {
1429 start.x = (mVertical) ? mRect.width - pixelWidth : 0;
1430 start.y = (mVertical) ? 0 : mRect.height - pixelWidth;
1431 end.x = (mVertical) ? start.x : mRect.width;
1432 end.y = (mVertical) ? mRect.height : start.y;
1433 StrokeLineWithSnapping(start, end, appUnitsPerDevPixel, *aDrawTarget,
1434 fgColor);
1438 nsresult nsHTMLFramesetBorderFrame::HandleEvent(nsPresContext* aPresContext,
1439 WidgetGUIEvent* aEvent,
1440 nsEventStatus* aEventStatus) {
1441 NS_ENSURE_ARG_POINTER(aEventStatus);
1442 *aEventStatus = nsEventStatus_eIgnore;
1444 // XXX Mouse setting logic removed. The remaining logic should also move.
1445 if (!mCanResize) {
1446 return NS_OK;
1449 if (aEvent->mMessage == eMouseDown &&
1450 aEvent->AsMouseEvent()->mButton == MouseButton::ePrimary) {
1451 nsHTMLFramesetFrame* parentFrame = do_QueryFrame(GetParent());
1452 if (parentFrame) {
1453 parentFrame->StartMouseDrag(aPresContext, this, aEvent);
1454 *aEventStatus = nsEventStatus_eConsumeNoDefault;
1457 return NS_OK;
1460 Maybe<nsIFrame::Cursor> nsHTMLFramesetBorderFrame::GetCursor(const nsPoint&) {
1461 auto kind = StyleCursorKind::Default;
1462 if (mCanResize) {
1463 kind = mVertical ? StyleCursorKind::EwResize : StyleCursorKind::NsResize;
1465 return Some(Cursor{kind, AllowCustomCursorImage::No});
1468 #ifdef DEBUG_FRAME_DUMP
1469 nsresult nsHTMLFramesetBorderFrame::GetFrameName(nsAString& aResult) const {
1470 return MakeFrameName(u"FramesetBorder"_ns, aResult);
1472 #endif
1474 /*******************************************************************************
1475 * nsHTMLFramesetBlankFrame
1476 ******************************************************************************/
1478 NS_QUERYFRAME_HEAD(nsHTMLFramesetBlankFrame)
1479 NS_QUERYFRAME_ENTRY(nsHTMLFramesetBlankFrame)
1480 NS_QUERYFRAME_TAIL_INHERITING(nsLeafFrame)
1482 NS_IMPL_FRAMEARENA_HELPERS(nsHTMLFramesetBlankFrame)
1484 nsHTMLFramesetBlankFrame::~nsHTMLFramesetBlankFrame() {
1485 // printf("nsHTMLFramesetBlankFrame destructor %p \n", this);
1488 nscoord nsHTMLFramesetBlankFrame::GetIntrinsicISize() {
1489 // No intrinsic width
1490 return 0;
1493 nscoord nsHTMLFramesetBlankFrame::GetIntrinsicBSize() {
1494 // No intrinsic height
1495 return 0;
1498 void nsHTMLFramesetBlankFrame::Reflow(nsPresContext* aPresContext,
1499 ReflowOutput& aDesiredSize,
1500 const ReflowInput& aReflowInput,
1501 nsReflowStatus& aStatus) {
1502 DO_GLOBAL_REFLOW_COUNT("nsHTMLFramesetBlankFrame");
1503 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
1505 // Override Reflow(), since we don't want to deal with what our
1506 // computed values are.
1507 SizeToAvailSize(aReflowInput, aDesiredSize);
1509 aDesiredSize.SetOverflowAreasToDesiredBounds();
1512 class nsDisplayFramesetBlank : public nsPaintedDisplayItem {
1513 public:
1514 nsDisplayFramesetBlank(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
1515 : nsPaintedDisplayItem(aBuilder, aFrame) {
1516 MOZ_COUNT_CTOR(nsDisplayFramesetBlank);
1518 MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayFramesetBlank)
1520 virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
1521 NS_DISPLAY_DECL_NAME("FramesetBlank", TYPE_FRAMESET_BLANK)
1524 void nsDisplayFramesetBlank::Paint(nsDisplayListBuilder* aBuilder,
1525 gfxContext* aCtx) {
1526 DrawTarget* drawTarget = aCtx->GetDrawTarget();
1527 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
1528 Rect rect =
1529 NSRectToSnappedRect(GetPaintRect(), appUnitsPerDevPixel, *drawTarget);
1530 ColorPattern white(ToDeviceColor(sRGBColor::OpaqueWhite()));
1531 drawTarget->FillRect(rect, white);
1534 void nsHTMLFramesetBlankFrame::BuildDisplayList(
1535 nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) {
1536 aLists.Content()->AppendNewToTop<nsDisplayFramesetBlank>(aBuilder, this);