Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/xul/nsSplitterFrame.cpp
Line
Count
Source (jump to first uncovered line)
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
//
8
// Eric Vaughan
9
// Netscape Communications
10
//
11
// See documentation in associated header file
12
//
13
14
#include "gfxContext.h"
15
#include "nsSplitterFrame.h"
16
#include "nsGkAtoms.h"
17
#include "nsXULElement.h"
18
#include "nsPresContext.h"
19
#include "nsIDocument.h"
20
#include "nsNameSpaceManager.h"
21
#include "nsScrollbarButtonFrame.h"
22
#include "nsIDOMEventListener.h"
23
#include "nsIPresShell.h"
24
#include "nsFrameList.h"
25
#include "nsHTMLParts.h"
26
#include "mozilla/ComputedStyle.h"
27
#include "nsBoxLayoutState.h"
28
#include "nsIServiceManager.h"
29
#include "nsContainerFrame.h"
30
#include "nsContentCID.h"
31
#include "nsLayoutUtils.h"
32
#include "nsDisplayList.h"
33
#include "nsContentUtils.h"
34
#include "mozilla/dom/Element.h"
35
#include "mozilla/dom/Event.h"
36
#include "mozilla/dom/MouseEvent.h"
37
#include "mozilla/MouseEvents.h"
38
#include "mozilla/UniquePtr.h"
39
#include "nsBindingManager.h"
40
41
using namespace mozilla;
42
43
class nsSplitterInfo {
44
public:
45
  nscoord min;
46
  nscoord max;
47
  nscoord current;
48
  nscoord changed;
49
  nsCOMPtr<nsIContent> childElem;
50
  int32_t flex;
51
  int32_t index;
52
};
53
54
class nsSplitterFrameInner final : public nsIDOMEventListener
55
{
56
protected:
57
  virtual ~nsSplitterFrameInner();
58
59
public:
60
61
  NS_DECL_ISUPPORTS
62
  NS_DECL_NSIDOMEVENTLISTENER
63
64
  explicit nsSplitterFrameInner(nsSplitterFrame* aSplitter)
65
    : mDidDrag(false)
66
    , mDragStart(0)
67
    , mParentBox(nullptr)
68
    , mChildInfosBeforeCount(0)
69
    , mChildInfosAfterCount(0)
70
    , mState(Open)
71
    , mSplitterPos(0)
72
    , mDragging(false)
73
0
  {
74
0
    mOuter = aSplitter;
75
0
    mPressed = false;
76
0
  }
77
78
0
  void Disconnect() { mOuter = nullptr; }
79
80
  nsresult MouseDown(Event* aMouseEvent);
81
  nsresult MouseUp(Event* aMouseEvent);
82
  nsresult MouseMove(Event* aMouseEvent);
83
84
  void MouseDrag(nsPresContext* aPresContext, WidgetGUIEvent* aEvent);
85
  void MouseUp(nsPresContext* aPresContext, WidgetGUIEvent* aEvent);
86
87
  void AdjustChildren(nsPresContext* aPresContext);
88
  void AdjustChildren(nsPresContext* aPresContext, nsSplitterInfo* aChildInfos, int32_t aCount, bool aIsHorizontal);
89
90
  void AddRemoveSpace(nscoord aDiff,
91
                    nsSplitterInfo* aChildInfos,
92
                    int32_t aCount,
93
                    int32_t& aSpaceLeft);
94
95
  void ResizeChildTo(nscoord& aDiff,
96
                     nsSplitterInfo* aChildrenBeforeInfos,
97
                     nsSplitterInfo* aChildrenAfterInfos,
98
                     int32_t aChildrenBeforeCount,
99
                     int32_t aChildrenAfterCount,
100
                     bool aBounded);
101
102
  void UpdateState();
103
104
  void AddListener();
105
  void RemoveListener();
106
107
  enum ResizeType { Closest, Farthest, Flex, Grow };
108
  enum State { Open, CollapsedBefore, CollapsedAfter, Dragging };
109
  enum CollapseDirection { Before, After };
110
111
  ResizeType GetResizeBefore();
112
  ResizeType GetResizeAfter();
113
  State GetState();
114
115
  void Reverse(UniquePtr<nsSplitterInfo[]>& aIndexes, int32_t aCount);
116
  bool SupportsCollapseDirection(CollapseDirection aDirection);
117
118
  void EnsureOrient();
119
  void SetPreferredSize(nsBoxLayoutState& aState, nsIFrame* aChildBox, nscoord aOnePixel, bool aIsHorizontal, nscoord* aSize);
120
121
  nsSplitterFrame* mOuter;
122
  bool mDidDrag;
123
  nscoord mDragStart;
124
  nsIFrame* mParentBox;
125
  bool mPressed;
126
  UniquePtr<nsSplitterInfo[]> mChildInfosBefore;
127
  UniquePtr<nsSplitterInfo[]> mChildInfosAfter;
128
  int32_t mChildInfosBeforeCount;
129
  int32_t mChildInfosAfterCount;
130
  State mState;
131
  nscoord mSplitterPos;
132
  bool mDragging;
133
134
0
  const Element* SplitterElement() const {
135
0
    return mOuter->GetContent()->AsElement();
136
0
  }
137
};
138
139
NS_IMPL_ISUPPORTS(nsSplitterFrameInner, nsIDOMEventListener)
140
141
nsSplitterFrameInner::ResizeType
142
nsSplitterFrameInner::GetResizeBefore()
143
0
{
144
0
  static Element::AttrValuesArray strings[] =
145
0
    {&nsGkAtoms::farthest, &nsGkAtoms::flex, nullptr};
146
0
  switch (SplitterElement()->FindAttrValueIn(kNameSpaceID_None,
147
0
                                             nsGkAtoms::resizebefore,
148
0
                                             strings, eCaseMatters)) {
149
0
    case 0: return Farthest;
150
0
    case 1: return Flex;
151
0
  }
152
0
  return Closest;
153
0
}
154
155
nsSplitterFrameInner::~nsSplitterFrameInner()
156
0
{
157
0
}
158
159
nsSplitterFrameInner::ResizeType
160
nsSplitterFrameInner::GetResizeAfter()
161
0
{
162
0
  static Element::AttrValuesArray strings[] =
163
0
    {&nsGkAtoms::farthest, &nsGkAtoms::flex, &nsGkAtoms::grow, nullptr};
164
0
  switch (SplitterElement()->FindAttrValueIn(kNameSpaceID_None,
165
0
                                             nsGkAtoms::resizeafter,
166
0
                                             strings, eCaseMatters)) {
167
0
    case 0: return Farthest;
168
0
    case 1: return Flex;
169
0
    case 2: return Grow;
170
0
  }
171
0
  return Closest;
172
0
}
173
174
nsSplitterFrameInner::State
175
nsSplitterFrameInner::GetState()
176
0
{
177
0
  static Element::AttrValuesArray strings[] =
178
0
    {&nsGkAtoms::dragging, &nsGkAtoms::collapsed, nullptr};
179
0
  static Element::AttrValuesArray strings_substate[] =
180
0
    {&nsGkAtoms::before, &nsGkAtoms::after, nullptr};
181
0
  switch (SplitterElement()->FindAttrValueIn(kNameSpaceID_None,
182
0
                                             nsGkAtoms::state,
183
0
                                             strings, eCaseMatters)) {
184
0
    case 0: return Dragging;
185
0
    case 1:
186
0
      switch (SplitterElement()->FindAttrValueIn(kNameSpaceID_None,
187
0
                                                 nsGkAtoms::substate,
188
0
                                                 strings_substate,
189
0
                                                 eCaseMatters)) {
190
0
        case 0: return CollapsedBefore;
191
0
        case 1: return CollapsedAfter;
192
0
        default:
193
0
          if (SupportsCollapseDirection(After))
194
0
            return CollapsedAfter;
195
0
          return CollapsedBefore;
196
0
      }
197
0
  }
198
0
  return Open;
199
0
}
200
201
//
202
// NS_NewSplitterFrame
203
//
204
// Creates a new Toolbar frame and returns it
205
//
206
nsIFrame*
207
NS_NewSplitterFrame (nsIPresShell* aPresShell, ComputedStyle* aStyle)
208
0
{
209
0
  return new (aPresShell) nsSplitterFrame(aStyle);
210
0
}
211
212
NS_IMPL_FRAMEARENA_HELPERS(nsSplitterFrame)
213
214
nsSplitterFrame::nsSplitterFrame(ComputedStyle* aStyle)
215
: nsBoxFrame(aStyle, kClassID),
216
  mInner(0)
217
0
{
218
0
}
219
220
void
221
nsSplitterFrame::DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData)
222
0
{
223
0
  if (mInner) {
224
0
    mInner->RemoveListener();
225
0
    mInner->Disconnect();
226
0
    mInner->Release();
227
0
    mInner = nullptr;
228
0
  }
229
0
  nsBoxFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
230
0
}
231
232
233
nsresult
234
nsSplitterFrame::GetCursor(const nsPoint&    aPoint,
235
                           nsIFrame::Cursor& aCursor)
236
0
{
237
0
  return nsBoxFrame::GetCursor(aPoint, aCursor);
238
0
239
0
  /*
240
0
    if (IsXULHorizontal())
241
0
      aCursor = NS_STYLE_CURSOR_N_RESIZE;
242
0
    else
243
0
      aCursor = NS_STYLE_CURSOR_W_RESIZE;
244
0
245
0
    return NS_OK;
246
0
  */
247
0
}
248
249
nsresult
250
nsSplitterFrame::AttributeChanged(int32_t aNameSpaceID,
251
                                  nsAtom* aAttribute,
252
                                  int32_t aModType)
253
0
{
254
0
  nsresult rv = nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute,
255
0
                                             aModType);
256
0
  if (aAttribute == nsGkAtoms::state) {
257
0
    mInner->UpdateState();
258
0
  }
259
0
260
0
  return rv;
261
0
}
262
263
/**
264
 * Initialize us. If we are in a box get our alignment so we know what direction we are
265
 */
266
void
267
nsSplitterFrame::Init(nsIContent*       aContent,
268
                      nsContainerFrame* aParent,
269
                      nsIFrame*         aPrevInFlow)
270
0
{
271
0
  MOZ_ASSERT(!mInner);
272
0
  mInner = new nsSplitterFrameInner(this);
273
0
274
0
  mInner->AddRef();
275
0
276
0
  // determine orientation of parent, and if vertical, set orient to vertical
277
0
  // on splitter content, then re-resolve style
278
0
  // XXXbz this is pretty messed up, since this can change whether we should
279
0
  // have a frame at all.  This really needs a better solution.
280
0
  if (aParent && aParent->IsXULBoxFrame()) {
281
0
    if (!aParent->IsXULHorizontal()) {
282
0
      if (!nsContentUtils::HasNonEmptyAttr(aContent, kNameSpaceID_None,
283
0
                                           nsGkAtoms::orient)) {
284
0
        aContent->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::orient,
285
0
                                       NS_LITERAL_STRING("vertical"), false);
286
0
      }
287
0
    }
288
0
  }
289
0
290
0
  nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
291
0
292
0
  mInner->mState = nsSplitterFrameInner::Open;
293
0
  mInner->AddListener();
294
0
  mInner->mParentBox = nullptr;
295
0
}
296
297
NS_IMETHODIMP
298
nsSplitterFrame::DoXULLayout(nsBoxLayoutState& aState)
299
0
{
300
0
  if (GetStateBits() & NS_FRAME_FIRST_REFLOW)
301
0
  {
302
0
    mInner->mParentBox = nsBox::GetParentXULBox(this);
303
0
    mInner->UpdateState();
304
0
  }
305
0
306
0
  return nsBoxFrame::DoXULLayout(aState);
307
0
}
308
309
310
void
311
nsSplitterFrame::GetInitialOrientation(bool& aIsHorizontal)
312
0
{
313
0
  nsIFrame* box = nsBox::GetParentXULBox(this);
314
0
  if (box) {
315
0
    aIsHorizontal = !box->IsXULHorizontal();
316
0
  }
317
0
  else
318
0
    nsBoxFrame::GetInitialOrientation(aIsHorizontal);
319
0
}
320
321
NS_IMETHODIMP
322
nsSplitterFrame::HandlePress(nsPresContext* aPresContext,
323
                             WidgetGUIEvent* aEvent,
324
                             nsEventStatus* aEventStatus)
325
0
{
326
0
  return NS_OK;
327
0
}
328
329
NS_IMETHODIMP
330
nsSplitterFrame::HandleMultiplePress(nsPresContext* aPresContext,
331
                                     WidgetGUIEvent* aEvent,
332
                                     nsEventStatus* aEventStatus,
333
                                     bool aControlHeld)
334
0
{
335
0
  return NS_OK;
336
0
}
337
338
NS_IMETHODIMP
339
nsSplitterFrame::HandleDrag(nsPresContext* aPresContext,
340
                            WidgetGUIEvent* aEvent,
341
                            nsEventStatus* aEventStatus)
342
0
{
343
0
  return NS_OK;
344
0
}
345
346
NS_IMETHODIMP
347
nsSplitterFrame::HandleRelease(nsPresContext* aPresContext,
348
                               WidgetGUIEvent* aEvent,
349
                               nsEventStatus* aEventStatus)
350
0
{
351
0
  return NS_OK;
352
0
}
353
354
void
355
nsSplitterFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
356
                                  const nsDisplayListSet& aLists)
357
0
{
358
0
  nsBoxFrame::BuildDisplayList(aBuilder, aLists);
359
0
360
0
  // if the mouse is captured always return us as the frame.
361
0
  if (mInner->mDragging && aBuilder->IsForEventDelivery())
362
0
  {
363
0
    // XXX It's probably better not to check visibility here, right?
364
0
    aLists.Outlines()->AppendToTop(
365
0
      MakeDisplayItem<nsDisplayEventReceiver>(aBuilder, this));
366
0
    return;
367
0
  }
368
0
}
369
370
nsresult
371
nsSplitterFrame::HandleEvent(nsPresContext* aPresContext,
372
                             WidgetGUIEvent* aEvent,
373
                             nsEventStatus* aEventStatus)
374
0
{
375
0
  NS_ENSURE_ARG_POINTER(aEventStatus);
376
0
  if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
377
0
    return NS_OK;
378
0
  }
379
0
380
0
  AutoWeakFrame weakFrame(this);
381
0
  RefPtr<nsSplitterFrameInner> inner(mInner);
382
0
  switch (aEvent->mMessage) {
383
0
    case eMouseMove:
384
0
      inner->MouseDrag(aPresContext, aEvent);
385
0
      break;
386
0
387
0
    case eMouseUp:
388
0
      if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) {
389
0
        inner->MouseUp(aPresContext, aEvent);
390
0
      }
391
0
      break;
392
0
393
0
    default:
394
0
      break;
395
0
  }
396
0
397
0
  NS_ENSURE_STATE(weakFrame.IsAlive());
398
0
  return nsBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
399
0
}
400
401
void
402
nsSplitterFrameInner::MouseUp(nsPresContext* aPresContext,
403
                              WidgetGUIEvent* aEvent)
404
0
{
405
0
  if (mDragging && mOuter) {
406
0
    AdjustChildren(aPresContext);
407
0
    AddListener();
408
0
    nsIPresShell::SetCapturingContent(nullptr, 0); // XXXndeakin is this needed?
409
0
    mDragging = false;
410
0
    State newState = GetState();
411
0
    // if the state is dragging then make it Open.
412
0
    if (newState == Dragging) {
413
0
      mOuter->mContent->AsElement()->SetAttr(kNameSpaceID_None,
414
0
                                             nsGkAtoms::state, EmptyString(),
415
0
                                             true);
416
0
    }
417
0
418
0
    mPressed = false;
419
0
420
0
    // if we dragged then fire a command event.
421
0
    if (mDidDrag) {
422
0
      RefPtr<nsXULElement> element =
423
0
        nsXULElement::FromNode(mOuter->GetContent());
424
0
      element->DoCommand();
425
0
    }
426
0
427
0
    //printf("MouseUp\n");
428
0
  }
429
0
430
0
  mChildInfosBefore = nullptr;
431
0
  mChildInfosAfter = nullptr;
432
0
  mChildInfosBeforeCount = 0;
433
0
  mChildInfosAfterCount = 0;
434
0
}
435
436
void
437
nsSplitterFrameInner::MouseDrag(nsPresContext* aPresContext,
438
                                WidgetGUIEvent* aEvent)
439
0
{
440
0
  if (mDragging && mOuter) {
441
0
442
0
    //printf("Dragging\n");
443
0
444
0
    bool isHorizontal = !mOuter->IsXULHorizontal();
445
0
    // convert coord to pixels
446
0
    nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent,
447
0
                                                              mParentBox);
448
0
    nscoord pos = isHorizontal ? pt.x : pt.y;
449
0
450
0
    // mDragStart is in frame coordinates
451
0
    nscoord start = mDragStart;
452
0
453
0
    // take our current position and subtract the start location
454
0
    pos -= start;
455
0
456
0
    //printf("Diff=%d\n", pos);
457
0
458
0
    ResizeType resizeAfter  = GetResizeAfter();
459
0
460
0
    bool bounded;
461
0
462
0
    if (resizeAfter == nsSplitterFrameInner::Grow)
463
0
      bounded = false;
464
0
    else
465
0
      bounded = true;
466
0
467
0
    int i;
468
0
    for (i=0; i < mChildInfosBeforeCount; i++)
469
0
      mChildInfosBefore[i].changed = mChildInfosBefore[i].current;
470
0
471
0
    for (i=0; i < mChildInfosAfterCount; i++)
472
0
      mChildInfosAfter[i].changed = mChildInfosAfter[i].current;
473
0
474
0
    nscoord oldPos = pos;
475
0
476
0
    ResizeChildTo(pos,
477
0
                  mChildInfosBefore.get(), mChildInfosAfter.get(),
478
0
                  mChildInfosBeforeCount, mChildInfosAfterCount, bounded);
479
0
480
0
    State currentState = GetState();
481
0
    bool supportsBefore = SupportsCollapseDirection(Before);
482
0
    bool supportsAfter = SupportsCollapseDirection(After);
483
0
484
0
    const bool isRTL = mOuter->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
485
0
    bool pastEnd = oldPos > 0 && oldPos > pos;
486
0
    bool pastBegin = oldPos < 0 && oldPos < pos;
487
0
    if (isRTL) {
488
0
      // Swap the boundary checks in RTL mode
489
0
      bool tmp = pastEnd;
490
0
      pastEnd = pastBegin;
491
0
      pastBegin = tmp;
492
0
    }
493
0
    const bool isCollapsedBefore = pastBegin && supportsBefore;
494
0
    const bool isCollapsedAfter = pastEnd && supportsAfter;
495
0
496
0
    // if we are in a collapsed position
497
0
    if (isCollapsedBefore || isCollapsedAfter)
498
0
    {
499
0
      // and we are not collapsed then collapse
500
0
      if (currentState == Dragging) {
501
0
        if (pastEnd)
502
0
        {
503
0
          //printf("Collapse right\n");
504
0
          if (supportsAfter)
505
0
          {
506
0
            RefPtr<Element> outer = mOuter->mContent->AsElement();
507
0
            outer->SetAttr(kNameSpaceID_None, nsGkAtoms::substate,
508
0
                           NS_LITERAL_STRING("after"),
509
0
                           true);
510
0
            outer->SetAttr(kNameSpaceID_None, nsGkAtoms::state,
511
0
                           NS_LITERAL_STRING("collapsed"),
512
0
                           true);
513
0
          }
514
0
515
0
        } else if (pastBegin)
516
0
        {
517
0
          //printf("Collapse left\n");
518
0
          if (supportsBefore)
519
0
          {
520
0
            RefPtr<Element> outer = mOuter->mContent->AsElement();
521
0
            outer->SetAttr(kNameSpaceID_None, nsGkAtoms::substate,
522
0
                           NS_LITERAL_STRING("before"),
523
0
                           true);
524
0
            outer->SetAttr(kNameSpaceID_None, nsGkAtoms::state,
525
0
                           NS_LITERAL_STRING("collapsed"),
526
0
                           true);
527
0
          }
528
0
        }
529
0
      }
530
0
    } else {
531
0
      // if we are not in a collapsed position and we are not dragging make sure
532
0
      // we are dragging.
533
0
      if (currentState != Dragging) {
534
0
        mOuter->mContent->AsElement()->SetAttr(kNameSpaceID_None,
535
0
                                               nsGkAtoms::state,
536
0
                                               NS_LITERAL_STRING("dragging"),
537
0
                                               true);
538
0
      }
539
0
      AdjustChildren(aPresContext);
540
0
    }
541
0
542
0
    mDidDrag = true;
543
0
  }
544
0
}
545
546
void
547
nsSplitterFrameInner::AddListener()
548
0
{
549
0
  mOuter->GetContent()->
550
0
    AddEventListener(NS_LITERAL_STRING("mouseup"), this, false, false);
551
0
  mOuter->GetContent()->
552
0
    AddEventListener(NS_LITERAL_STRING("mousedown"), this, false, false);
553
0
  mOuter->GetContent()->
554
0
    AddEventListener(NS_LITERAL_STRING("mousemove"), this, false, false);
555
0
  mOuter->GetContent()->
556
0
    AddEventListener(NS_LITERAL_STRING("mouseout"), this, false, false);
557
0
}
558
559
void
560
nsSplitterFrameInner::RemoveListener()
561
0
{
562
0
  NS_ENSURE_TRUE_VOID(mOuter);
563
0
  mOuter->GetContent()->
564
0
    RemoveEventListener(NS_LITERAL_STRING("mouseup"), this, false);
565
0
  mOuter->GetContent()->
566
0
    RemoveEventListener(NS_LITERAL_STRING("mousedown"), this, false);
567
0
  mOuter->GetContent()->
568
0
    RemoveEventListener(NS_LITERAL_STRING("mousemove"), this, false);
569
0
  mOuter->GetContent()->
570
0
    RemoveEventListener(NS_LITERAL_STRING("mouseout"), this, false);
571
0
}
572
573
nsresult
574
nsSplitterFrameInner::HandleEvent(dom::Event* aEvent)
575
0
{
576
0
  nsAutoString eventType;
577
0
  aEvent->GetType(eventType);
578
0
  if (eventType.EqualsLiteral("mouseup"))
579
0
    return MouseUp(aEvent);
580
0
  if (eventType.EqualsLiteral("mousedown"))
581
0
    return MouseDown(aEvent);
582
0
  if (eventType.EqualsLiteral("mousemove") ||
583
0
      eventType.EqualsLiteral("mouseout"))
584
0
    return MouseMove(aEvent);
585
0
586
0
  MOZ_ASSERT_UNREACHABLE("Unexpected eventType");
587
0
  return NS_OK;
588
0
}
589
590
nsresult
591
nsSplitterFrameInner::MouseUp(Event* aMouseEvent)
592
0
{
593
0
  NS_ENSURE_TRUE(mOuter, NS_OK);
594
0
  mPressed = false;
595
0
596
0
  nsIPresShell::SetCapturingContent(nullptr, 0);
597
0
598
0
  return NS_OK;
599
0
}
600
601
nsresult
602
nsSplitterFrameInner::MouseDown(Event* aMouseEvent)
603
0
{
604
0
  NS_ENSURE_TRUE(mOuter, NS_OK);
605
0
  dom::MouseEvent* mouseEvent = aMouseEvent->AsMouseEvent();
606
0
  if (!mouseEvent) {
607
0
    return NS_OK;
608
0
  }
609
0
610
0
  // only if left button
611
0
  if (mouseEvent->Button() != 0)
612
0
     return NS_OK;
613
0
614
0
  if (SplitterElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
615
0
                                     nsGkAtoms::_true, eCaseMatters))
616
0
    return NS_OK;
617
0
618
0
  mParentBox = nsBox::GetParentXULBox(mOuter);
619
0
  if (!mParentBox)
620
0
    return NS_OK;
621
0
622
0
  // get our index
623
0
  nsPresContext* outerPresContext = mOuter->PresContext();
624
0
  const nsFrameList& siblingList(mParentBox->PrincipalChildList());
625
0
  int32_t childIndex = siblingList.IndexOf(mOuter);
626
0
  // if it's 0 (or not found) then stop right here.
627
0
  // It might be not found if we're not in the parent's primary frame list.
628
0
  if (childIndex <= 0)
629
0
    return NS_OK;
630
0
631
0
  int32_t childCount = siblingList.GetLength();
632
0
  // if it's the last index then we need to allow for resizeafter="grow"
633
0
  if (childIndex == childCount - 1 && GetResizeAfter() != Grow)
634
0
    return NS_OK;
635
0
636
0
  RefPtr<gfxContext> rc =
637
0
    outerPresContext->PresShell()->CreateReferenceRenderingContext();
638
0
  nsBoxLayoutState state(outerPresContext, rc);
639
0
  mPressed = true;
640
0
641
0
  mDidDrag = false;
642
0
643
0
  EnsureOrient();
644
0
  bool isHorizontal = !mOuter->IsXULHorizontal();
645
0
646
0
  ResizeType resizeBefore = GetResizeBefore();
647
0
  ResizeType resizeAfter  = GetResizeAfter();
648
0
649
0
  mChildInfosBefore = MakeUnique<nsSplitterInfo[]>(childCount);
650
0
  mChildInfosAfter  = MakeUnique<nsSplitterInfo[]>(childCount);
651
0
652
0
  // create info 2 lists. One of the children before us and one after.
653
0
  int32_t count = 0;
654
0
  mChildInfosBeforeCount = 0;
655
0
  mChildInfosAfterCount = 0;
656
0
657
0
  nsIFrame* childBox = nsBox::GetChildXULBox(mParentBox);
658
0
659
0
  while (nullptr != childBox)
660
0
  {
661
0
    nsIContent* content = childBox->GetContent();
662
0
    nsIDocument* doc = content->OwnerDoc();
663
0
    int32_t dummy;
664
0
    nsAtom* atom = doc->BindingManager()->ResolveTag(content, &dummy);
665
0
666
0
    // skip over any splitters
667
0
    if (atom != nsGkAtoms::splitter) {
668
0
        nsSize prefSize = childBox->GetXULPrefSize(state);
669
0
        nsSize minSize = childBox->GetXULMinSize(state);
670
0
        nsSize maxSize = nsBox::BoundsCheckMinMax(minSize, childBox->GetXULMaxSize(state));
671
0
        prefSize = nsBox::BoundsCheck(minSize, prefSize, maxSize);
672
0
673
0
        nsSplitterFrame::AddMargin(childBox, minSize);
674
0
        nsSplitterFrame::AddMargin(childBox, prefSize);
675
0
        nsSplitterFrame::AddMargin(childBox, maxSize);
676
0
677
0
        nscoord flex = childBox->GetXULFlex();
678
0
679
0
        nsMargin margin(0,0,0,0);
680
0
        childBox->GetXULMargin(margin);
681
0
        nsRect r(childBox->GetRect());
682
0
        r.Inflate(margin);
683
0
684
0
        // We need to check for hidden attribute too, since treecols with
685
0
        // the hidden="true" attribute are not really hidden, just collapsed
686
0
        if (!content->IsElement() ||
687
0
            (!content->AsElement()->AttrValueIs(kNameSpaceID_None,
688
0
                                                nsGkAtoms::fixed,
689
0
                                                nsGkAtoms::_true,
690
0
                                                eCaseMatters) &&
691
0
             !content->AsElement()->AttrValueIs(kNameSpaceID_None,
692
0
                                                nsGkAtoms::hidden,
693
0
                                                nsGkAtoms::_true,
694
0
                                                eCaseMatters))) {
695
0
            if (count < childIndex && (resizeBefore != Flex || flex > 0)) {
696
0
                mChildInfosBefore[mChildInfosBeforeCount].childElem = content;
697
0
                mChildInfosBefore[mChildInfosBeforeCount].min     = isHorizontal ? minSize.width : minSize.height;
698
0
                mChildInfosBefore[mChildInfosBeforeCount].max     = isHorizontal ? maxSize.width : maxSize.height;
699
0
                mChildInfosBefore[mChildInfosBeforeCount].current = isHorizontal ? r.width : r.height;
700
0
                mChildInfosBefore[mChildInfosBeforeCount].flex    = flex;
701
0
                mChildInfosBefore[mChildInfosBeforeCount].index   = count;
702
0
                mChildInfosBefore[mChildInfosBeforeCount].changed = mChildInfosBefore[mChildInfosBeforeCount].current;
703
0
                mChildInfosBeforeCount++;
704
0
            } else if (count > childIndex && (resizeAfter != Flex || flex > 0)) {
705
0
                mChildInfosAfter[mChildInfosAfterCount].childElem = content;
706
0
                mChildInfosAfter[mChildInfosAfterCount].min     = isHorizontal ? minSize.width : minSize.height;
707
0
                mChildInfosAfter[mChildInfosAfterCount].max     = isHorizontal ? maxSize.width : maxSize.height;
708
0
                mChildInfosAfter[mChildInfosAfterCount].current = isHorizontal ? r.width : r.height;
709
0
                mChildInfosAfter[mChildInfosAfterCount].flex    = flex;
710
0
                mChildInfosAfter[mChildInfosAfterCount].index   = count;
711
0
                mChildInfosAfter[mChildInfosAfterCount].changed = mChildInfosAfter[mChildInfosAfterCount].current;
712
0
                mChildInfosAfterCount++;
713
0
            }
714
0
        }
715
0
    }
716
0
717
0
    childBox = nsBox::GetNextXULBox(childBox);
718
0
    count++;
719
0
  }
720
0
721
0
  if (!mParentBox->IsXULNormalDirection()) {
722
0
    // The before array is really the after array, and the order needs to be reversed.
723
0
    // First reverse both arrays.
724
0
    Reverse(mChildInfosBefore, mChildInfosBeforeCount);
725
0
    Reverse(mChildInfosAfter, mChildInfosAfterCount);
726
0
727
0
    // Now swap the two arrays.
728
0
    Swap(mChildInfosBeforeCount, mChildInfosAfterCount);
729
0
    Swap(mChildInfosBefore, mChildInfosAfter);
730
0
  }
731
0
732
0
  // if resizebefore is not Farthest, reverse the list because the first child
733
0
  // in the list is the farthest, and we want the first child to be the closest.
734
0
  if (resizeBefore != Farthest)
735
0
     Reverse(mChildInfosBefore, mChildInfosBeforeCount);
736
0
737
0
  // if the resizeafter is the Farthest we must reverse the list because the first child in the list
738
0
  // is the closest we want the first child to be the Farthest.
739
0
  if (resizeAfter == Farthest)
740
0
     Reverse(mChildInfosAfter, mChildInfosAfterCount);
741
0
742
0
  // grow only applys to the children after. If grow is set then no space should be taken out of any children after
743
0
  // us. To do this we just set the size of that list to be 0.
744
0
  if (resizeAfter == Grow)
745
0
     mChildInfosAfterCount = 0;
746
0
747
0
  int32_t c;
748
0
  nsPoint pt = nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(mouseEvent,
749
0
                                                               mParentBox);
750
0
  if (isHorizontal) {
751
0
     c = pt.x;
752
0
     mSplitterPos = mOuter->mRect.x;
753
0
  } else {
754
0
     c = pt.y;
755
0
     mSplitterPos = mOuter->mRect.y;
756
0
  }
757
0
758
0
  mDragStart = c;
759
0
760
0
  //printf("Pressed mDragStart=%d\n",mDragStart);
761
0
762
0
  nsIPresShell::SetCapturingContent(mOuter->GetContent(), CAPTURE_IGNOREALLOWED);
763
0
764
0
  return NS_OK;
765
0
}
766
767
nsresult
768
nsSplitterFrameInner::MouseMove(Event* aMouseEvent)
769
0
{
770
0
  NS_ENSURE_TRUE(mOuter, NS_OK);
771
0
  if (!mPressed)
772
0
    return NS_OK;
773
0
774
0
  if (mDragging)
775
0
    return NS_OK;
776
0
777
0
  nsCOMPtr<nsIDOMEventListener> kungfuDeathGrip(this);
778
0
  mOuter->mContent->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::state,
779
0
                                         NS_LITERAL_STRING("dragging"), true);
780
0
781
0
  RemoveListener();
782
0
  mDragging = true;
783
0
784
0
  return NS_OK;
785
0
}
786
787
void
788
nsSplitterFrameInner::Reverse(UniquePtr<nsSplitterInfo[]>& aChildInfos, int32_t aCount)
789
0
{
790
0
    UniquePtr<nsSplitterInfo[]> infos(new nsSplitterInfo[aCount]);
791
0
792
0
    for (int i=0; i < aCount; i++)
793
0
       infos[i] = aChildInfos[aCount - 1 - i];
794
0
795
0
    aChildInfos = std::move(infos);
796
0
}
797
798
bool
799
nsSplitterFrameInner::SupportsCollapseDirection
800
(
801
  nsSplitterFrameInner::CollapseDirection aDirection
802
)
803
0
{
804
0
  static Element::AttrValuesArray strings[] =
805
0
    {&nsGkAtoms::before, &nsGkAtoms::after, &nsGkAtoms::both, nullptr};
806
0
807
0
  switch (SplitterElement()->FindAttrValueIn(kNameSpaceID_None,
808
0
                                             nsGkAtoms::collapse,
809
0
                                             strings, eCaseMatters)) {
810
0
    case 0:
811
0
      return (aDirection == Before);
812
0
    case 1:
813
0
      return (aDirection == After);
814
0
    case 2:
815
0
      return true;
816
0
  }
817
0
818
0
  return false;
819
0
}
820
821
void
822
nsSplitterFrameInner::UpdateState()
823
0
{
824
0
  // State Transitions:
825
0
  //   Open            -> Dragging
826
0
  //   Open            -> CollapsedBefore
827
0
  //   Open            -> CollapsedAfter
828
0
  //   CollapsedBefore -> Open
829
0
  //   CollapsedBefore -> Dragging
830
0
  //   CollapsedAfter  -> Open
831
0
  //   CollapsedAfter  -> Dragging
832
0
  //   Dragging        -> Open
833
0
  //   Dragging        -> CollapsedBefore (auto collapse)
834
0
  //   Dragging        -> CollapsedAfter (auto collapse)
835
0
836
0
  State newState = GetState();
837
0
838
0
  if (newState == mState) {
839
0
    // No change.
840
0
    return;
841
0
  }
842
0
843
0
  if ((SupportsCollapseDirection(Before) || SupportsCollapseDirection(After)) &&
844
0
      mOuter->GetParent()->IsXULBoxFrame()) {
845
0
    // Find the splitter's immediate sibling.
846
0
    nsIFrame* splitterSibling;
847
0
    if (newState == CollapsedBefore || mState == CollapsedBefore) {
848
0
      splitterSibling = mOuter->GetPrevSibling();
849
0
    } else {
850
0
      splitterSibling = mOuter->GetNextSibling();
851
0
    }
852
0
853
0
    if (splitterSibling) {
854
0
      nsCOMPtr<nsIContent> sibling = splitterSibling->GetContent();
855
0
      if (sibling && sibling->IsElement()) {
856
0
        if (mState == CollapsedBefore || mState == CollapsedAfter) {
857
0
          // CollapsedBefore -> Open
858
0
          // CollapsedBefore -> Dragging
859
0
          // CollapsedAfter -> Open
860
0
          // CollapsedAfter -> Dragging
861
0
          nsContentUtils::AddScriptRunner(
862
0
            new nsUnsetAttrRunnable(sibling->AsElement(), nsGkAtoms::collapsed));
863
0
        } else if ((mState == Open || mState == Dragging)
864
0
                   && (newState == CollapsedBefore ||
865
0
                       newState == CollapsedAfter)) {
866
0
          // Open -> CollapsedBefore / CollapsedAfter
867
0
          // Dragging -> CollapsedBefore / CollapsedAfter
868
0
          nsContentUtils::AddScriptRunner(
869
0
            new nsSetAttrRunnable(sibling->AsElement(), nsGkAtoms::collapsed,
870
0
                                  NS_LITERAL_STRING("true")));
871
0
        }
872
0
      }
873
0
    }
874
0
  }
875
0
  mState = newState;
876
0
}
877
878
void
879
nsSplitterFrameInner::EnsureOrient()
880
0
{
881
0
  bool isHorizontal = !(mParentBox->GetStateBits() & NS_STATE_IS_HORIZONTAL);
882
0
  if (isHorizontal)
883
0
    mOuter->AddStateBits(NS_STATE_IS_HORIZONTAL);
884
0
  else
885
0
    mOuter->RemoveStateBits(NS_STATE_IS_HORIZONTAL);
886
0
}
887
888
void
889
nsSplitterFrameInner::AdjustChildren(nsPresContext* aPresContext)
890
0
{
891
0
  EnsureOrient();
892
0
  bool isHorizontal = !mOuter->IsXULHorizontal();
893
0
894
0
  AdjustChildren(aPresContext, mChildInfosBefore.get(),
895
0
                 mChildInfosBeforeCount, isHorizontal);
896
0
  AdjustChildren(aPresContext, mChildInfosAfter.get(),
897
0
                 mChildInfosAfterCount, isHorizontal);
898
0
}
899
900
static nsIFrame* GetChildBoxForContent(nsIFrame* aParentBox, nsIContent* aContent)
901
0
{
902
0
  nsIFrame* childBox = nsBox::GetChildXULBox(aParentBox);
903
0
904
0
  while (nullptr != childBox) {
905
0
    if (childBox->GetContent() == aContent) {
906
0
      return childBox;
907
0
    }
908
0
    childBox = nsBox::GetNextXULBox(childBox);
909
0
  }
910
0
  return nullptr;
911
0
}
912
913
void
914
nsSplitterFrameInner::AdjustChildren(nsPresContext* aPresContext, nsSplitterInfo* aChildInfos, int32_t aCount, bool aIsHorizontal)
915
0
{
916
0
  ///printf("------- AdjustChildren------\n");
917
0
918
0
  nsBoxLayoutState state(aPresContext);
919
0
920
0
  nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
921
0
922
0
  // first set all the widths.
923
0
  nsIFrame* child =  nsBox::GetChildXULBox(mOuter);
924
0
  while(child)
925
0
  {
926
0
    SetPreferredSize(state, child, onePixel, aIsHorizontal, nullptr);
927
0
    child = nsBox::GetNextXULBox(child);
928
0
  }
929
0
930
0
  // now set our changed widths.
931
0
  for (int i=0; i < aCount; i++)
932
0
  {
933
0
    nscoord   pref       = aChildInfos[i].changed;
934
0
    nsIFrame* childBox     = GetChildBoxForContent(mParentBox, aChildInfos[i].childElem);
935
0
936
0
    if (childBox) {
937
0
      SetPreferredSize(state, childBox, onePixel, aIsHorizontal, &pref);
938
0
    }
939
0
  }
940
0
}
941
942
void
943
nsSplitterFrameInner::SetPreferredSize(nsBoxLayoutState& aState, nsIFrame* aChildBox, nscoord aOnePixel, bool aIsHorizontal, nscoord* aSize)
944
0
{
945
0
  nsRect rect(aChildBox->GetRect());
946
0
  nscoord pref = 0;
947
0
948
0
  if (!aSize)
949
0
  {
950
0
    if (aIsHorizontal)
951
0
      pref = rect.width;
952
0
    else
953
0
      pref = rect.height;
954
0
  } else {
955
0
    pref = *aSize;
956
0
  }
957
0
958
0
  nsMargin margin(0,0,0,0);
959
0
  aChildBox->GetXULMargin(margin);
960
0
961
0
  RefPtr<nsAtom> attribute;
962
0
963
0
  if (aIsHorizontal) {
964
0
    pref -= (margin.left + margin.right);
965
0
    attribute = nsGkAtoms::width;
966
0
  } else {
967
0
    pref -= (margin.top + margin.bottom);
968
0
    attribute = nsGkAtoms::height;
969
0
  }
970
0
971
0
  nsIContent* content = aChildBox->GetContent();
972
0
  if (!content->IsElement()) {
973
0
    return;
974
0
  }
975
0
976
0
  // set its preferred size.
977
0
  nsAutoString prefValue;
978
0
  prefValue.AppendInt(pref/aOnePixel);
979
0
  if (content->AsElement()->AttrValueIs(kNameSpaceID_None, attribute,
980
0
                                        prefValue, eCaseMatters)) {
981
0
     return;
982
0
  }
983
0
984
0
  AutoWeakFrame weakBox(aChildBox);
985
0
  content->AsElement()->SetAttr(kNameSpaceID_None, attribute, prefValue, true);
986
0
  NS_ENSURE_TRUE_VOID(weakBox.IsAlive());
987
0
  aState.PresShell()->FrameNeedsReflow(aChildBox, nsIPresShell::eStyleChange,
988
0
                                       NS_FRAME_IS_DIRTY);
989
0
}
990
991
992
void
993
nsSplitterFrameInner::AddRemoveSpace(nscoord aDiff,
994
                                    nsSplitterInfo* aChildInfos,
995
                                    int32_t aCount,
996
                                    int32_t& aSpaceLeft)
997
0
{
998
0
  aSpaceLeft = 0;
999
0
1000
0
  for (int i=0; i < aCount; i++) {
1001
0
    nscoord min    = aChildInfos[i].min;
1002
0
    nscoord max    = aChildInfos[i].max;
1003
0
    nscoord& c     = aChildInfos[i].changed;
1004
0
1005
0
    // figure our how much space to add or remove
1006
0
    if (c + aDiff < min) {
1007
0
      aDiff += (c - min);
1008
0
      c = min;
1009
0
    } else if (c + aDiff > max) {
1010
0
      aDiff -= (max - c);
1011
0
      c = max;
1012
0
    } else {
1013
0
      c += aDiff;
1014
0
      aDiff = 0;
1015
0
    }
1016
0
1017
0
    // there is not space left? We are done
1018
0
    if (aDiff == 0)
1019
0
      break;
1020
0
  }
1021
0
1022
0
  aSpaceLeft = aDiff;
1023
0
}
1024
1025
/**
1026
 * Ok if we want to resize a child we will know the actual size in pixels we want it to be.
1027
 * This is not the preferred size. But they only way we can change a child is my manipulating its
1028
 * preferred size. So give the actual pixel size this return method will return figure out the preferred
1029
 * size and set it.
1030
 */
1031
1032
void
1033
nsSplitterFrameInner::ResizeChildTo(nscoord& aDiff,
1034
                                    nsSplitterInfo* aChildrenBeforeInfos,
1035
                                    nsSplitterInfo* aChildrenAfterInfos,
1036
                                    int32_t aChildrenBeforeCount,
1037
                                    int32_t aChildrenAfterCount,
1038
                                    bool aBounded)
1039
0
{
1040
0
  nscoord spaceLeft;
1041
0
  AddRemoveSpace(aDiff, aChildrenBeforeInfos,aChildrenBeforeCount,spaceLeft);
1042
0
1043
0
  // if there is any space left over remove it from the dif we were originally given
1044
0
  aDiff -= spaceLeft;
1045
0
  AddRemoveSpace(-aDiff, aChildrenAfterInfos,aChildrenAfterCount,spaceLeft);
1046
0
1047
0
  if (spaceLeft != 0) {
1048
0
    if (aBounded) {
1049
0
       aDiff += spaceLeft;
1050
0
       AddRemoveSpace(spaceLeft, aChildrenBeforeInfos,aChildrenBeforeCount,spaceLeft);
1051
0
    }
1052
0
  }
1053
0
}