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/. */
6
7 /* rendering object for HTML <frameset> elements */
8
9 #include "nsFrameSetFrame.h"
10
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"
20
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"
44
45 using namespace mozilla;
46 using namespace mozilla::dom;
47 using namespace mozilla::gfx;
48
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
56
57 /*******************************************************************************
58 * nsFramesetDrag
59 ******************************************************************************/
nsFramesetDrag()60 nsFramesetDrag::nsFramesetDrag() { UnSet(); }
61
Reset(bool aVertical,int32_t aIndex,int32_t aChange,nsHTMLFramesetFrame * aSource)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;
68 }
69
UnSet()70 void nsFramesetDrag::UnSet() {
71 mVertical = true;
72 mIndex = -1;
73 mChange = 0;
74 mSource = nullptr;
75 }
76
77 /*******************************************************************************
78 * nsHTMLFramesetBorderFrame
79 ******************************************************************************/
80 class nsHTMLFramesetBorderFrame final : public nsLeafFrame {
81 public:
82 NS_DECL_FRAMEARENA_HELPERS(nsHTMLFramesetBorderFrame)
83
84 #ifdef DEBUG_FRAME_DUMP
85 virtual nsresult GetFrameName(nsAString& aResult) const override;
86 #endif
87
88 virtual nsresult HandleEvent(nsPresContext* aPresContext,
89 WidgetGUIEvent* aEvent,
90 nsEventStatus* aEventStatus) override;
91
92 Maybe<Cursor> GetCursor(const nsPoint&) override;
93
94 virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
95 const nsDisplayListSet& aLists) override;
96
97 virtual void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
98 const ReflowInput& aReflowInput,
99 nsReflowStatus& aStatus) override;
100
GetVisibility()101 bool GetVisibility() { return mVisibility; }
102 void SetVisibility(bool aVisibility);
103 void SetColor(nscolor aColor);
104
105 void PaintBorder(DrawTarget* aDrawTarget, nsPoint aPt);
106
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;
113
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;
125 };
126 /*******************************************************************************
127 * nsHTMLFramesetBlankFrame
128 ******************************************************************************/
129 class nsHTMLFramesetBlankFrame final : public nsLeafFrame {
130 public:
131 NS_DECL_QUERYFRAME
NS_DECL_FRAMEARENA_HELPERS(nsHTMLFramesetBlankFrame)132 NS_DECL_FRAMEARENA_HELPERS(nsHTMLFramesetBlankFrame)
133
134 #ifdef DEBUG_FRAME_DUMP
135 virtual nsresult GetFrameName(nsAString& aResult) const override {
136 return MakeFrameName(u"FramesetBlank"_ns, aResult);
137 }
138 #endif
139
140 virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
141 const nsDisplayListSet& aLists) override;
142
143 virtual void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
144 const ReflowInput& aReflowInput,
145 nsReflowStatus& aStatus) override;
146
147 protected:
nsHTMLFramesetBlankFrame(ComputedStyle * aStyle,nsPresContext * aPresContext)148 explicit nsHTMLFramesetBlankFrame(ComputedStyle* aStyle,
149 nsPresContext* aPresContext)
150 : nsLeafFrame(aStyle, aPresContext, kClassID) {}
151
152 virtual ~nsHTMLFramesetBlankFrame();
153 virtual nscoord GetIntrinsicISize() override;
154 virtual nscoord GetIntrinsicBSize() override;
155
156 friend class nsHTMLFramesetFrame;
157 friend class nsHTMLFrameset;
158 };
159
160 /*******************************************************************************
161 * nsHTMLFramesetFrame
162 ******************************************************************************/
163 bool nsHTMLFramesetFrame::gDragInProgress = false;
164 #define DEFAULT_BORDER_WIDTH_PX 6
165
nsHTMLFramesetFrame(ComputedStyle * aStyle,nsPresContext * aPresContext)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);
183 }
184
185 nsHTMLFramesetFrame::~nsHTMLFramesetFrame() = default;
186
187 NS_QUERYFRAME_HEAD(nsHTMLFramesetFrame)
NS_QUERYFRAME_ENTRY(nsHTMLFramesetFrame)188 NS_QUERYFRAME_ENTRY(nsHTMLFramesetFrame)
189 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
190
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;
204 }
205 }
206
207 nsPresContext* presContext = PresContext();
208 mozilla::PresShell* presShell = presContext->PresShell();
209
210 nsFrameborder frameborder = GetFrameBorder();
211 int32_t borderWidth = GetBorderWidth(presContext, false);
212 nscolor borderColor = GetBorderColor();
213
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);
223
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);
229
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;
234
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
240
241 for (int verX = 0; verX < mNumCols; verX++) mVerBorders[verX] = nullptr;
242
243 mHorBorders = MakeUnique<nsHTMLFramesetBorderFrame*[]>(
244 mNumRows); // 1 more than number of hor borders
245
246 for (int horX = 0; horX < mNumRows; horX++) mHorBorders[horX] = nullptr;
247
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);
259
260 // create the children frames; skip content which isn't <frameset> or <frame>
261 mChildCount = 0; // number of <frame> or <frameset> children
262
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);
272 }
273 break;
274 }
275 child->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
276
277 // IMPORTANT: This must match the conditions in
278 // nsCSSFrameConstructor::ContentAppended/Inserted/Removed
279 if (!child->IsAnyOfHTMLElements(nsGkAtoms::frameset, nsGkAtoms::frame)) {
280 continue;
281 }
282
283 // FIXME(emilio): This doesn't even respect display: none, but that matches
284 // other browsers ;_;
285 //
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);
292
293 nsHTMLFramesetFrame* childFrame = (nsHTMLFramesetFrame*)frame;
294 childFrame->SetParentFrameborder(frameborder);
295 childFrame->SetParentBorderWidth(borderWidth);
296 childFrame->SetParentBorderColor(borderColor);
297 frame->Init(child, this, nullptr);
298
299 mChildBorderColors[mChildCount].Set(childFrame->GetBorderColor());
300 } else { // frame
301 frame = NS_NewSubDocumentFrame(presShell, kidStyle);
302
303 frame->Init(child, this, nullptr);
304
305 mChildFrameborder[mChildCount] = GetFrameBorder(child);
306 mChildBorderColors[mChildCount].Set(GetBorderColor(child));
307 }
308 child->SetPrimaryFrame(frame);
309
310 mFrames.AppendFrame(nullptr, frame);
311
312 mChildCount++;
313 }
314
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);
321
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());
326
327 blankFrame->Init(mContent, this, nullptr);
328
329 mFrames.AppendFrame(nullptr, blankFrame);
330
331 mChildBorderColors[mChildCount].Set(NO_COLOR);
332 mChildCount++;
333 }
334
335 mNonBorderChildCount = mChildCount;
336 }
337
SetInitialChildList(ChildListID aListID,nsFrameList & aChildList)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;
346 }
347
348 nsContainerFrame::SetInitialChildList(aListID, aChildList);
349 }
350
351 // XXX should this try to allocate twips based on an even pixel boundary?
Scale(nscoord aDesired,int32_t aNumIndicies,int32_t * aIndicies,int32_t aNumItems,int32_t * aItems)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];
361 }
362
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];
371 }
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;
379 }
380 }
381
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;
389 }
390 }
391 }
392 }
393
394 /**
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.
399 */
CalculateRowCol(nsPresContext * aPresContext,nscoord aSize,int32_t aNumSpecs,const nsFramesetSpec * aSpecs,nscoord * aValues)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");
406
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);
415
416 if (MOZ_UNLIKELY(!fixed || !percent || !relative)) {
417 return; // NS_ERROR_OUT_OF_MEMORY
418 }
419
420 int32_t i, j;
421
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;
442 }
443 }
444
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;
451 }
452
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];
462 }
463
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;
470 }
471
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];
481 }
482
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);
486 }
487 }
488
489 /**
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.
493 */
GenerateRowCol(nsPresContext * aPresContext,nscoord aSize,int32_t aNumSpecs,const nsFramesetSpec * aSpecs,nscoord * aValues,nsString & aNewAttr)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;
499
500 for (i = 0; i < aNumSpecs; i++) {
501 if (!aNewAttr.IsEmpty()) aNewAttr.Append(char16_t(','));
502
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;
513 }
514 }
515 }
516
GetBorderWidth(nsPresContext * aPresContext,bool aTakeForcingIntoAccount)517 int32_t nsHTMLFramesetFrame::GetBorderWidth(nsPresContext* aPresContext,
518 bool aTakeForcingIntoAccount) {
519 nsFrameborder frameborder = GetFrameBorder();
520 if (frameborder == eFrameborder_No) {
521 return 0;
522 }
523 nsGenericHTMLElement* content = nsGenericHTMLElement::FromNode(mContent);
524
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;
533 }
534 }
535
536 return nsPresContext::CSSPixelsToAppUnits(intVal);
537 }
538 }
539
540 if (mParentBorderWidth >= 0) {
541 return mParentBorderWidth;
542 }
543
544 return nsPresContext::CSSPixelsToAppUnits(DEFAULT_BORDER_WIDTH_PX);
545 }
546
GetDesiredSize(nsPresContext * aPresContext,const ReflowInput & aReflowInput,ReflowOutput & aDesiredSize)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());
560
561 desiredSize.ISize(wm) = area.ISize(wm);
562 desiredSize.BSize(wm) = area.BSize(wm);
563 }
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);
569 }
570 aDesiredSize.SetSize(wm, desiredSize);
571 }
572
573 // only valid for non border children
GetSizeOfChildAt(int32_t aIndexInParent,WritingMode aWM,LogicalSize & aSize,nsIntPoint & aCellIndex)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;
588 }
589 }
590
591 // only valid for non border children
GetSizeOfChild(nsIFrame * aChild,WritingMode aWM,LogicalSize & aSize)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;
602 }
603 i++;
604 }
605 aSize.SizeTo(aWM, 0, 0);
606 }
607
HandleEvent(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)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);
621 }
622 break;
623 default:
624 break;
625 }
626 *aEventStatus = nsEventStatus_eConsumeNoDefault;
627 } else {
628 *aEventStatus = nsEventStatus_eIgnore;
629 }
630 return NS_OK;
631 }
632
GetCursor(const nsPoint &)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;
638 }
639 return Some(Cursor{kind, AllowCustomCursorImage::No});
640 }
641
BuildDisplayList(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)642 void nsHTMLFramesetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
643 const nsDisplayListSet& aLists) {
644 BuildDisplayListForInline(aBuilder, aLists);
645
646 if (mDragger && aBuilder->IsForEventDelivery()) {
647 aLists.Content()->AppendNewToTop<nsDisplayEventReceiver>(aBuilder, this);
648 }
649 }
650
ReflowPlaceChild(nsIFrame * aChild,nsPresContext * aPresContext,const ReflowInput & aReflowInput,nsPoint & aOffset,nsSize & aSize,nsIntPoint * aCellIndex)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(
660 0,
661 aSize.width - reflowInput.ComputedPhysicalBorderPadding().LeftRight()));
662 reflowInput.SetComputedHeight(std::max(
663 0,
664 aSize.height - reflowInput.ComputedPhysicalBorderPadding().TopBottom()));
665 ReflowOutput reflowOutput(aReflowInput);
666 reflowOutput.Width() = aSize.width;
667 reflowOutput.Height() = aSize.height;
668 nsReflowStatus status;
669
670 ReflowChild(aChild, aPresContext, reflowOutput, reflowInput, aOffset.x,
671 aOffset.y, ReflowChildFlags::Default, status);
672 NS_ASSERTION(status.IsComplete(), "bad status");
673
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);
679 }
680
GetFrameBorderHelper(nsGenericHTMLElement * aContent)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;
689
690 case NS_STYLE_FRAME_NO:
691 case NS_STYLE_FRAME_0:
692 return eFrameborder_No;
693 }
694 }
695 }
696 return eFrameborder_Notset;
697 }
698
GetFrameBorder()699 nsFrameborder nsHTMLFramesetFrame::GetFrameBorder() {
700 nsFrameborder result = eFrameborder_Notset;
701 nsGenericHTMLElement* content = nsGenericHTMLElement::FromNode(mContent);
702
703 if (content) {
704 result = GetFrameBorderHelper(content);
705 }
706 if (eFrameborder_Notset == result) {
707 return mParentFrameborder;
708 }
709 return result;
710 }
711
GetFrameBorder(nsIContent * aContent)712 nsFrameborder nsHTMLFramesetFrame::GetFrameBorder(nsIContent* aContent) {
713 nsFrameborder result = eFrameborder_Notset;
714
715 nsGenericHTMLElement* content = nsGenericHTMLElement::FromNode(aContent);
716
717 if (content) {
718 result = GetFrameBorderHelper(content);
719 }
720 if (eFrameborder_Notset == result) {
721 return GetFrameBorder();
722 }
723 return result;
724 }
725
GetBorderColor()726 nscolor nsHTMLFramesetFrame::GetBorderColor() {
727 nsGenericHTMLElement* content = nsGenericHTMLElement::FromNode(mContent);
728
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;
735 }
736 }
737 }
738
739 return mParentBorderColor;
740 }
741
GetBorderColor(nsIContent * aContent)742 nscolor nsHTMLFramesetFrame::GetBorderColor(nsIContent* aContent) {
743 nsGenericHTMLElement* content = nsGenericHTMLElement::FromNode(aContent);
744
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;
751 }
752 }
753 }
754 return GetBorderColor();
755 }
756
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)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!");
765
766 mozilla::PresShell* presShell = aPresContext->PresShell();
767 ServoStyleSet* styleSet = presShell->StyleSet();
768
769 GetParent()->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
770
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);
775
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();
782
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;
790
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);
795
796 width -= (mNumCols - 1) * borderWidth;
797 if (width < 0) width = 0;
798
799 height -= (mNumRows - 1) * borderWidth;
800 if (height < 0) height = 0;
801
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;
816 }
817
818 CalculateRowCol(aPresContext, width, mNumCols, colSpecs, mColSizes.get());
819 CalculateRowCol(aPresContext, height, mNumRows, rowSpecs, mRowSizes.get());
820
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();
827
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");
835
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;
841 }
842
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;
848 }
849 }
850
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();
861
862 for (int32_t childX = 0; childX < mNonBorderChildCount; childX++) {
863 nsIntPoint cellIndex;
864 GetSizeOfChildAt(childX, wm, logicalSize, cellIndex);
865 size = logicalSize.GetPhysicalSize(wm);
866
867 if (lastRow != cellIndex.y) { // changed to next row
868 offset.x = 0;
869 offset.y += lastSize.height;
870 if (firstTime) { // create horizontal border
871
872 RefPtr<ComputedStyle> pseudoComputedStyle;
873 pseudoComputedStyle = styleSet->ResolveNonInheritingAnonymousBoxStyle(
874 PseudoStyleType::horizontalFramesetBorder);
875
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++;
889 }
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
899
900 RefPtr<ComputedStyle> pseudoComputedStyle;
901 pseudoComputedStyle =
902 styleSet->ResolveNonInheritingAnonymousBoxStyle(
903 PseudoStyleType::verticalFramesetBorder);
904
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++;
919 }
920 nsSize borderSize(borderWidth, aDesiredSize.Height());
921 ReflowPlaceChild(borderFrame, aPresContext, aReflowInput, offset,
922 borderSize);
923 borderFrame = nullptr;
924 }
925 offset.x += borderWidth;
926 }
927 }
928
929 ReflowPlaceChild(child, aPresContext, aReflowInput, offset, size,
930 &cellIndex);
931
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;
945 }
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;
952 }
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);
958 }
959 if (NO_COLOR == mEdgeColors.mLeft) {
960 mEdgeColors.mLeft = childColors.mLeft;
961 }
962 }
963 if (0 == cellIndex.y) {
964 if (!(mEdgeVisibility & TOP_VIS)) {
965 mEdgeVisibility |= (TOP_VIS & childVis);
966 }
967 if (NO_COLOR == mEdgeColors.mTop) {
968 mEdgeColors.mTop = childColors.mTop;
969 }
970 }
971 if (mNumCols - 1 == cellIndex.x) {
972 if (!(mEdgeVisibility & RIGHT_VIS)) {
973 mEdgeVisibility |= (RIGHT_VIS & childVis);
974 }
975 if (NO_COLOR == mEdgeColors.mRight) {
976 mEdgeColors.mRight = childColors.mRight;
977 }
978 }
979 if (mNumRows - 1 == cellIndex.y) {
980 if (!(mEdgeVisibility & BOTTOM_VIS)) {
981 mEdgeVisibility |= (BOTTOM_VIS & childVis);
982 }
983 if (NO_COLOR == mEdgeColors.mBottom) {
984 mEdgeColors.mBottom = childColors.mBottom;
985 }
986 }
987 // set the visibility of borders that the child may affect
988 if (childVis & RIGHT_VIS) {
989 verBordersVis[cellIndex.x] = true;
990 }
991 if (childVis & BOTTOM_VIS) {
992 horBordersVis[cellIndex.y] = true;
993 }
994 if ((cellIndex.x > 0) && (childVis & LEFT_VIS)) {
995 verBordersVis[cellIndex.x - 1] = true;
996 }
997 if ((cellIndex.y > 0) && (childVis & TOP_VIS)) {
998 horBordersVis[cellIndex.y - 1] = true;
999 }
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;
1003 }
1004 if (NO_COLOR == horBorderColors[cellIndex.y]) {
1005 horBorderColors[cellIndex.y] = mChildBorderColors[childX].mBottom;
1006 }
1007 if ((cellIndex.x > 0) && (NO_COLOR == verBorderColors[cellIndex.x - 1])) {
1008 verBorderColors[cellIndex.x - 1] = mChildBorderColors[childX].mLeft;
1009 }
1010 if ((cellIndex.y > 0) && (NO_COLOR == horBorderColors[cellIndex.y - 1])) {
1011 horBorderColors[cellIndex.y - 1] = mChildBorderColors[childX].mTop;
1012 }
1013 }
1014 lastRow = cellIndex.y;
1015 lastCol = cellIndex.x;
1016 lastSize = size;
1017 offset.x += size.width;
1018 child = child->GetNextSibling();
1019 }
1020
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);
1032 }
1033 }
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);
1042 }
1043 }
1044
1045 mChildFrameborder.reset();
1046 mChildBorderColors.reset();
1047 }
1048
1049 mDrag.UnSet();
1050
1051 aDesiredSize.SetOverflowAreasToDesiredBounds();
1052 FinishAndStoreOverflow(&aDesiredSize);
1053
1054 NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
1055 }
1056
1057 #ifdef DEBUG_FRAME_DUMP
GetFrameName(nsAString & aResult) const1058 nsresult nsHTMLFramesetFrame::GetFrameName(nsAString& aResult) const {
1059 return MakeFrameName(u"Frameset"_ns, aResult);
1060 }
1061 #endif
1062
CanResize(bool aVertical,bool aLeft)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;
1071 }
1072 }
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;
1079 }
1080 }
1081 }
1082 return true;
1083 }
1084
GetNoResize(nsIFrame * aChildFrame)1085 bool nsHTMLFramesetFrame::GetNoResize(nsIFrame* aChildFrame) {
1086 nsIContent* content = aChildFrame->GetContent();
1087
1088 return content && content->IsElement() &&
1089 content->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::noresize);
1090 }
1091
CanChildResize(bool aVertical,bool aLeft,int32_t aChildX)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);
1097 }
1098
1099 // This calculates and sets the resizability of all border frames
1100
RecalculateBorderResize()1101 void nsHTMLFramesetFrame::RecalculateBorderResize() {
1102 if (!mContent) {
1103 return;
1104 }
1105
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]);
1118 }
1119 }
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]);
1125 }
1126 }
1127 }
1128
SetBorderResize(nsHTMLFramesetBorderFrame * aBorderFrame)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;
1137 }
1138 }
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;
1145 }
1146 }
1147 endX = endX + mNumCols;
1148 for (; childX < endX; childX++) {
1149 if (!CanChildResize(false, true, childX)) {
1150 aBorderFrame->mCanResize = false;
1151 }
1152 }
1153 }
1154 }
1155
StartMouseDrag(nsPresContext * aPresContext,nsHTMLFramesetBorderFrame * aBorder,WidgetGUIEvent * aEvent)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
1164
1165 PresShell::SetCapturingContent(GetContent(),
1166 CaptureFlags::IgnoreAllowedState);
1167
1168 mDragger = aBorder;
1169
1170 mFirstDragPoint = aEvent->mRefPoint;
1171
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];
1179 }
1180
1181 gDragInProgress = true;
1182 }
1183
MouseDrag(nsPresContext * aPresContext,WidgetGUIEvent * aEvent)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;
1191 }
1192
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;
1202 }
1203 mColSizes[mDragger->mPrevNeighbor] = mPrevNeighborOrigSize + change;
1204 mColSizes[mDragger->mNextNeighbor] = mNextNeighborOrigSize - change;
1205
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);
1220 }
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;
1228 }
1229 mRowSizes[mDragger->mPrevNeighbor] = mPrevNeighborOrigSize + change;
1230 mRowSizes[mDragger->mNextNeighbor] = mNextNeighborOrigSize - change;
1231
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);
1246 }
1247 }
1248
1249 NS_ENSURE_TRUE_VOID(weakFrame.IsAlive());
1250 if (change != 0) {
1251 mDrag.Reset(mDragger->mVertical, mDragger->mPrevNeighbor, change, this);
1252 }
1253 }
1254
EndMouseDrag(nsPresContext * aPresContext)1255 void nsHTMLFramesetFrame::EndMouseDrag(nsPresContext* aPresContext) {
1256 PresShell::ReleaseCapturingContent();
1257 mDragger = nullptr;
1258 gDragInProgress = false;
1259 }
1260
NS_NewHTMLFramesetFrame(PresShell * aPresShell,ComputedStyle * aStyle)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
1268
1269 return new (aPresShell)
1270 nsHTMLFramesetFrame(aStyle, aPresShell->GetPresContext());
1271 }
1272
NS_IMPL_FRAMEARENA_HELPERS(nsHTMLFramesetFrame)1273 NS_IMPL_FRAMEARENA_HELPERS(nsHTMLFramesetFrame)
1274
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;
1289 }
1290
~nsHTMLFramesetBorderFrame()1291 nsHTMLFramesetBorderFrame::~nsHTMLFramesetBorderFrame() {
1292 // printf("nsHTMLFramesetBorderFrame destructor %p \n", this);
1293 }
1294
NS_IMPL_FRAMEARENA_HELPERS(nsHTMLFramesetBorderFrame)1295 NS_IMPL_FRAMEARENA_HELPERS(nsHTMLFramesetBorderFrame)
1296
1297 nscoord nsHTMLFramesetBorderFrame::GetIntrinsicISize() {
1298 // No intrinsic width
1299 return 0;
1300 }
1301
GetIntrinsicBSize()1302 nscoord nsHTMLFramesetBorderFrame::GetIntrinsicBSize() {
1303 // No intrinsic height
1304 return 0;
1305 }
1306
SetVisibility(bool aVisibility)1307 void nsHTMLFramesetBorderFrame::SetVisibility(bool aVisibility) {
1308 mVisibility = aVisibility;
1309 }
1310
SetColor(nscolor aColor)1311 void nsHTMLFramesetBorderFrame::SetColor(nscolor aColor) { mColor = aColor; }
1312
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)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!");
1320
1321 // Override Reflow(), since we don't want to deal with what our
1322 // computed values are.
1323 SizeToAvailSize(aReflowInput, aDesiredSize);
1324
1325 aDesiredSize.SetOverflowAreasToDesiredBounds();
1326 }
1327
1328 class nsDisplayFramesetBorder : public nsPaintedDisplayItem {
1329 public:
nsDisplayFramesetBorder(nsDisplayListBuilder * aBuilder,nsHTMLFramesetBorderFrame * aFrame)1330 nsDisplayFramesetBorder(nsDisplayListBuilder* aBuilder,
1331 nsHTMLFramesetBorderFrame* aFrame)
1332 : nsPaintedDisplayItem(aBuilder, aFrame) {
1333 MOZ_COUNT_CTOR(nsDisplayFramesetBorder);
1334 }
MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayFramesetBorder)1335 MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayFramesetBorder)
1336
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);
1343 }
1344 virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
1345 NS_DISPLAY_DECL_NAME("FramesetBorder", TYPE_FRAMESET_BORDER)
1346 };
1347
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)1348 void nsDisplayFramesetBorder::Paint(nsDisplayListBuilder* aBuilder,
1349 gfxContext* aCtx) {
1350 static_cast<nsHTMLFramesetBorderFrame*>(mFrame)->PaintBorder(
1351 aCtx->GetDrawTarget(), ToReferenceFrame());
1352 }
1353
BuildDisplayList(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)1354 void nsHTMLFramesetBorderFrame::BuildDisplayList(
1355 nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) {
1356 aLists.Content()->AppendNewToTop<nsDisplayFramesetBorder>(aBuilder, this);
1357 }
1358
PaintBorder(DrawTarget * aDrawTarget,nsPoint aPt)1359 void nsHTMLFramesetBorderFrame::PaintBorder(DrawTarget* aDrawTarget,
1360 nsPoint aPt) {
1361 nscoord widthInPixels = nsPresContext::AppUnitsToIntCSSPixels(mWidth);
1362 nscoord pixelWidth = nsPresContext::CSSPixelsToAppUnits(1);
1363
1364 if (widthInPixels <= 0) return;
1365
1366 ColorPattern bgColor(ToDeviceColor(LookAndFeel::Color(
1367 LookAndFeel::ColorID::WidgetBackground, this, NS_RGB(200, 200, 200))));
1368
1369 ColorPattern fgColor(ToDeviceColor(LookAndFeel::Color(
1370 LookAndFeel::ColorID::WidgetForeground, this, NS_RGB(0, 0, 0))));
1371
1372 ColorPattern hltColor(ToDeviceColor(LookAndFeel::Color(
1373 LookAndFeel::ColorID::Widget3DHighlight, this, NS_RGB(255, 255, 255))));
1374
1375 ColorPattern sdwColor(ToDeviceColor(LookAndFeel::Color(
1376 LookAndFeel::ColorID::Widget3DShadow, this, NS_RGB(128, 128, 128))));
1377
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));
1382 }
1383
1384 int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
1385
1386 Point toRefFrame = NSPointToPoint(aPt, appUnitsPerDevPixel);
1387
1388 AutoRestoreTransform autoRestoreTransform(aDrawTarget);
1389 aDrawTarget->SetTransform(
1390 aDrawTarget->GetTransform().PreTranslate(toRefFrame));
1391
1392 nsPoint start(0, 0);
1393 nsPoint end = mVertical ? nsPoint(0, mRect.height) : nsPoint(mRect.width, 0);
1394
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;
1405 }
1406 }
1407
1408 if (!mVisibility) return;
1409
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);
1417 }
1418
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);
1426 }
1427
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);
1435 }
1436 }
1437
HandleEvent(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)1438 nsresult nsHTMLFramesetBorderFrame::HandleEvent(nsPresContext* aPresContext,
1439 WidgetGUIEvent* aEvent,
1440 nsEventStatus* aEventStatus) {
1441 NS_ENSURE_ARG_POINTER(aEventStatus);
1442 *aEventStatus = nsEventStatus_eIgnore;
1443
1444 // XXX Mouse setting logic removed. The remaining logic should also move.
1445 if (!mCanResize) {
1446 return NS_OK;
1447 }
1448
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;
1455 }
1456 }
1457 return NS_OK;
1458 }
1459
GetCursor(const nsPoint &)1460 Maybe<nsIFrame::Cursor> nsHTMLFramesetBorderFrame::GetCursor(const nsPoint&) {
1461 auto kind = StyleCursorKind::Default;
1462 if (mCanResize) {
1463 kind = mVertical ? StyleCursorKind::EwResize : StyleCursorKind::NsResize;
1464 }
1465 return Some(Cursor{kind, AllowCustomCursorImage::No});
1466 }
1467
1468 #ifdef DEBUG_FRAME_DUMP
GetFrameName(nsAString & aResult) const1469 nsresult nsHTMLFramesetBorderFrame::GetFrameName(nsAString& aResult) const {
1470 return MakeFrameName(u"FramesetBorder"_ns, aResult);
1471 }
1472 #endif
1473
1474 /*******************************************************************************
1475 * nsHTMLFramesetBlankFrame
1476 ******************************************************************************/
1477
1478 NS_QUERYFRAME_HEAD(nsHTMLFramesetBlankFrame)
NS_QUERYFRAME_ENTRY(nsHTMLFramesetBlankFrame)1479 NS_QUERYFRAME_ENTRY(nsHTMLFramesetBlankFrame)
1480 NS_QUERYFRAME_TAIL_INHERITING(nsLeafFrame)
1481
1482 NS_IMPL_FRAMEARENA_HELPERS(nsHTMLFramesetBlankFrame)
1483
1484 nsHTMLFramesetBlankFrame::~nsHTMLFramesetBlankFrame() {
1485 // printf("nsHTMLFramesetBlankFrame destructor %p \n", this);
1486 }
1487
GetIntrinsicISize()1488 nscoord nsHTMLFramesetBlankFrame::GetIntrinsicISize() {
1489 // No intrinsic width
1490 return 0;
1491 }
1492
GetIntrinsicBSize()1493 nscoord nsHTMLFramesetBlankFrame::GetIntrinsicBSize() {
1494 // No intrinsic height
1495 return 0;
1496 }
1497
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)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!");
1504
1505 // Override Reflow(), since we don't want to deal with what our
1506 // computed values are.
1507 SizeToAvailSize(aReflowInput, aDesiredSize);
1508
1509 aDesiredSize.SetOverflowAreasToDesiredBounds();
1510 }
1511
1512 class nsDisplayFramesetBlank : public nsPaintedDisplayItem {
1513 public:
nsDisplayFramesetBlank(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame)1514 nsDisplayFramesetBlank(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
1515 : nsPaintedDisplayItem(aBuilder, aFrame) {
1516 MOZ_COUNT_CTOR(nsDisplayFramesetBlank);
1517 }
1518 MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayFramesetBlank)
1519
1520 virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
1521 NS_DISPLAY_DECL_NAME("FramesetBlank", TYPE_FRAMESET_BLANK)
1522 };
1523
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)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);
1532 }
1533
BuildDisplayList(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)1534 void nsHTMLFramesetBlankFrame::BuildDisplayList(
1535 nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) {
1536 aLists.Content()->AppendNewToTop<nsDisplayFramesetBlank>(aBuilder, this);
1537 }
1538