summaryrefslogtreecommitdiffstats
path: root/layout/xul/nsRootBoxFrame.cpp
blob: 9cf8757cb4d88e86b780547e59fe2dce7fe55567 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "nsHTMLParts.h"
#include "nsStyleConsts.h"
#include "nsGkAtoms.h"
#include "nsBoxFrame.h"
#include "nsDisplayList.h"
#include "nsStackLayout.h"
#include "nsIPopupContainer.h"
#include "nsIContent.h"
#include "nsFrameManager.h"
#include "nsLayoutUtils.h"
#include "mozilla/BasicEvents.h"
#include "mozilla/DisplayPortUtils.h"
#include "mozilla/PresShell.h"

using namespace mozilla;

// Interface IDs

// static
nsIPopupContainer* nsIPopupContainer::GetPopupContainer(PresShell* aPresShell) {
  if (!aPresShell) {
    return nullptr;
  }
  nsIFrame* rootFrame = aPresShell->GetRootFrame();
  if (!rootFrame) {
    return nullptr;
  }

  if (rootFrame) {
    rootFrame = rootFrame->PrincipalChildList().FirstChild();
  }

  nsIPopupContainer* rootBox = do_QueryFrame(rootFrame);

  // If the rootBox was not found yet this may be a top level non-XUL document.
  if (rootFrame && !rootBox) {
    // In a non-XUL document the rootFrame here will be a nsHTMLScrollFrame,
    // get the nsCanvasFrame (which is the popup container) from it.
    rootFrame = rootFrame->GetContentInsertionFrame();
    rootBox = do_QueryFrame(rootFrame);
  }

  return rootBox;
}

class nsRootBoxFrame final : public nsBoxFrame, public nsIPopupContainer {
 public:
  friend nsIFrame* NS_NewBoxFrame(mozilla::PresShell* aPresShell,
                                  ComputedStyle* aStyle);

  explicit nsRootBoxFrame(ComputedStyle* aStyle, nsPresContext* aPresContext);

  NS_DECL_QUERYFRAME
  NS_DECL_FRAMEARENA_HELPERS(nsRootBoxFrame)

  virtual nsPopupSetFrame* GetPopupSetFrame() override;
  virtual void SetPopupSetFrame(nsPopupSetFrame* aPopupSet) override;
  virtual dom::Element* GetDefaultTooltip() override;
  virtual void SetDefaultTooltip(dom::Element* aTooltip) override;

  virtual void AppendFrames(ChildListID aListID,
                            nsFrameList& aFrameList) override;
  virtual void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
                            const nsLineList::iterator* aPrevFrameLine,
                            nsFrameList& aFrameList) override;
  virtual void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) override;

  virtual void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
                      const ReflowInput& aReflowInput,
                      nsReflowStatus& aStatus) override;
  virtual nsresult HandleEvent(nsPresContext* aPresContext,
                               WidgetGUIEvent* aEvent,
                               nsEventStatus* aEventStatus) override;

  virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
                                const nsDisplayListSet& aLists) override;

  virtual bool IsFrameOfType(uint32_t aFlags) const override {
    // Override bogus IsFrameOfType in nsBoxFrame.
    if (aFlags & (nsIFrame::eReplacedContainsBlock | nsIFrame::eReplaced))
      return false;
    return nsBoxFrame::IsFrameOfType(aFlags);
  }

#ifdef DEBUG_FRAME_DUMP
  virtual nsresult GetFrameName(nsAString& aResult) const override;
#endif

  nsPopupSetFrame* mPopupSetFrame;

 protected:
  dom::Element* mDefaultTooltip;
};

//----------------------------------------------------------------------

nsContainerFrame* NS_NewRootBoxFrame(PresShell* aPresShell,
                                     ComputedStyle* aStyle) {
  return new (aPresShell) nsRootBoxFrame(aStyle, aPresShell->GetPresContext());
}

NS_IMPL_FRAMEARENA_HELPERS(nsRootBoxFrame)

nsRootBoxFrame::nsRootBoxFrame(ComputedStyle* aStyle,
                               nsPresContext* aPresContext)
    : nsBoxFrame(aStyle, aPresContext, kClassID, true),
      mPopupSetFrame(nullptr),
      mDefaultTooltip(nullptr) {
  nsCOMPtr<nsBoxLayout> layout;
  NS_NewStackLayout(layout);
  SetXULLayoutManager(layout);
}

void nsRootBoxFrame::AppendFrames(ChildListID aListID,
                                  nsFrameList& aFrameList) {
  MOZ_ASSERT(aListID == kPrincipalList, "unexpected child list ID");
  MOZ_ASSERT(mFrames.IsEmpty(), "already have a child frame");
  nsBoxFrame::AppendFrames(aListID, aFrameList);
}

void nsRootBoxFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
                                  const nsLineList::iterator* aPrevFrameLine,
                                  nsFrameList& aFrameList) {
  // Because we only support a single child frame inserting is the same
  // as appending.
  MOZ_ASSERT(!aPrevFrame, "unexpected previous sibling frame");
  AppendFrames(aListID, aFrameList);
}

void nsRootBoxFrame::RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) {
  NS_ASSERTION(aListID == kPrincipalList, "unexpected child list ID");
  if (aOldFrame == mFrames.FirstChild()) {
    nsBoxFrame::RemoveFrame(aListID, aOldFrame);
  } else {
    MOZ_CRASH("unknown aOldFrame");
  }
}

void nsRootBoxFrame::Reflow(nsPresContext* aPresContext,
                            ReflowOutput& aDesiredSize,
                            const ReflowInput& aReflowInput,
                            nsReflowStatus& aStatus) {
  DO_GLOBAL_REFLOW_COUNT("nsRootBoxFrame");
  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");

  return nsBoxFrame::Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
}

void nsRootBoxFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
                                      const nsDisplayListSet& aLists) {
  if (mContent && mContent->GetProperty(nsGkAtoms::DisplayPortMargins)) {
    // The XUL document's root element may have displayport margins set in
    // ChromeProcessController::InitializeRoot, and we should to supply the
    // base rect.
    nsRect displayPortBase =
        aBuilder->GetVisibleRect().Intersect(nsRect(nsPoint(0, 0), GetSize()));
    DisplayPortUtils::SetDisplayPortBase(mContent, displayPortBase);
  }

  // root boxes don't need a debug border/outline or a selection overlay...
  // They *may* have a background propagated to them, so force creation
  // of a background display list element.
  DisplayBorderBackgroundOutline(aBuilder, aLists, true);

  BuildDisplayListForChildren(aBuilder, aLists);
}

nsresult nsRootBoxFrame::HandleEvent(nsPresContext* aPresContext,
                                     WidgetGUIEvent* aEvent,
                                     nsEventStatus* aEventStatus) {
  NS_ENSURE_ARG_POINTER(aEventStatus);
  if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
    return NS_OK;
  }

  if (aEvent->mMessage == eMouseUp) {
    nsIFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
  }

  return NS_OK;
}

nsPopupSetFrame* nsRootBoxFrame::GetPopupSetFrame() { return mPopupSetFrame; }

void nsRootBoxFrame::SetPopupSetFrame(nsPopupSetFrame* aPopupSet) {
  // Under normal conditions this should only be called once.  However,
  // if something triggers ReconstructDocElementHierarchy, we will
  // destroy this frame's child (the nsDocElementBoxFrame), but not this
  // frame.  This will cause the popupset to remove itself by calling
  // |SetPopupSetFrame(nullptr)|, and then we'll be able to accept a new
  // popupset.  Since the anonymous content is associated with the
  // nsDocElementBoxFrame, we'll get a new popupset when the new doc
  // element box frame is created.
  MOZ_ASSERT(!aPopupSet || !mPopupSetFrame,
             "Popup set is already defined! Only 1 allowed.");
  mPopupSetFrame = aPopupSet;
}

dom::Element* nsRootBoxFrame::GetDefaultTooltip() { return mDefaultTooltip; }

void nsRootBoxFrame::SetDefaultTooltip(dom::Element* aTooltip) {
  mDefaultTooltip = aTooltip;
}

NS_QUERYFRAME_HEAD(nsRootBoxFrame)
  NS_QUERYFRAME_ENTRY(nsIPopupContainer)
NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)

#ifdef DEBUG_FRAME_DUMP
nsresult nsRootBoxFrame::GetFrameName(nsAString& aResult) const {
  return MakeFrameName(u"RootBox"_ns, aResult);
}
#endif