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 #include "nsCOMPtr.h"
8 #include "nsTreeContentView.h"
9 #include "nsITreeSelection.h"
10 #include "ChildIterator.h"
11 #include "nsError.h"
12 #include "nsTreeBodyFrame.h"
13 #include "mozilla/dom/DOMRect.h"
14 #include "mozilla/dom/BindingUtils.h"
15 #include "mozilla/dom/Element.h"
16 #include "mozilla/dom/ToJSValue.h"
17 #include "mozilla/dom/XULTreeElement.h"
18 #include "mozilla/dom/XULTreeElementBinding.h"
19
20 namespace mozilla {
21 namespace dom {
22
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(XULTreeElement,nsXULElement)23 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(XULTreeElement, nsXULElement)
24 NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeElement, nsXULElement, mView)
25
26 JSObject* XULTreeElement::WrapNode(JSContext* aCx,
27 JS::Handle<JSObject*> aGivenProto) {
28 return XULTreeElement_Binding::Wrap(aCx, this, aGivenProto);
29 }
30
UnbindFromTree(bool aNullParent)31 void XULTreeElement::UnbindFromTree(bool aNullParent) {
32 // Drop the view's ref to us.
33 if (mView) {
34 nsCOMPtr<nsITreeSelection> sel;
35 mView->GetSelection(getter_AddRefs(sel));
36 if (sel) {
37 sel->SetTree(nullptr);
38 }
39 mView->SetTree(nullptr); // Break the circular ref between the view and us.
40 }
41 mView = nullptr;
42
43 nsXULElement::UnbindFromTree(aNullParent);
44 }
45
DestroyContent()46 void XULTreeElement::DestroyContent() {
47 // Drop the view's ref to us.
48 if (mView) {
49 nsCOMPtr<nsITreeSelection> sel;
50 mView->GetSelection(getter_AddRefs(sel));
51 if (sel) {
52 sel->SetTree(nullptr);
53 }
54 mView->SetTree(nullptr); // Break the circular ref between the view and us.
55 }
56 mView = nullptr;
57
58 nsXULElement::DestroyContent();
59 }
60
FindBodyElement(nsIContent * aParent)61 static nsIContent* FindBodyElement(nsIContent* aParent) {
62 mozilla::dom::FlattenedChildIterator iter(aParent);
63 for (nsIContent* content = iter.GetNextChild(); content;
64 content = iter.GetNextChild()) {
65 mozilla::dom::NodeInfo* ni = content->NodeInfo();
66 if (ni->Equals(nsGkAtoms::treechildren, kNameSpaceID_XUL)) {
67 return content;
68 } else if (ni->Equals(nsGkAtoms::tree, kNameSpaceID_XUL)) {
69 // There are nesting tree elements. Only the innermost should
70 // find the treechilren.
71 return nullptr;
72 } else if (content->IsElement() &&
73 !ni->Equals(nsGkAtoms::_template, kNameSpaceID_XUL)) {
74 nsIContent* result = FindBodyElement(content);
75 if (result) return result;
76 }
77 }
78
79 return nullptr;
80 }
81
GetTreeBodyFrame(FlushType aFlushType)82 nsTreeBodyFrame* XULTreeElement::GetTreeBodyFrame(FlushType aFlushType) {
83 MOZ_ASSERT(aFlushType == FlushType::Frames ||
84 aFlushType == FlushType::Layout || aFlushType == FlushType::None);
85 nsCOMPtr<nsIContent> kungFuDeathGrip = this; // keep a reference
86
87 // Make sure our frames are up to date, and layout as needed. We
88 // have to do this before checking for our cached mTreeBody, since
89 // it might go away on style flush, and in any case if aFlushLayout
90 // is true we need to make sure to flush no matter what.
91 if (aFlushType != FlushType::None) {
92 if (RefPtr<Document> doc = GetComposedDoc()) {
93 doc->FlushPendingNotifications(aFlushType);
94 }
95 }
96
97 if (mTreeBody) {
98 // Have one cached already.
99 return mTreeBody;
100 }
101
102 if (nsCOMPtr<nsIContent> tree = FindBodyElement(this)) {
103 mTreeBody = do_QueryFrame(tree->GetPrimaryFrame());
104 }
105
106 return mTreeBody;
107 }
108
GetView()109 already_AddRefed<nsITreeView> XULTreeElement::GetView() {
110 if (!mTreeBody) {
111 if (!GetTreeBodyFrame()) {
112 return nullptr;
113 }
114
115 if (mView) {
116 nsCOMPtr<nsITreeView> view;
117 // Our new frame needs to initialise itself
118 mTreeBody->GetView(getter_AddRefs(view));
119 return view.forget();
120 }
121 }
122 if (!mView) {
123 // No tree builder, create a tree content view.
124 if (NS_FAILED(NS_NewTreeContentView(getter_AddRefs(mView)))) {
125 return nullptr;
126 }
127
128 // Initialise the frame and view
129 mTreeBody->SetView(mView);
130 }
131
132 return do_AddRef(mView);
133 }
134
SetView(nsITreeView * aView,CallerType aCallerType,ErrorResult & aRv)135 void XULTreeElement::SetView(nsITreeView* aView, CallerType aCallerType,
136 ErrorResult& aRv) {
137 if (aCallerType != CallerType::System) {
138 // Don't trust views coming from random places.
139 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
140 return;
141 }
142
143 mView = aView;
144 nsTreeBodyFrame* body = GetTreeBodyFrame();
145 if (body) {
146 body->SetView(aView);
147 }
148 }
149
Focused()150 bool XULTreeElement::Focused() {
151 nsTreeBodyFrame* body = GetTreeBodyFrame();
152 if (body) {
153 return body->GetFocused();
154 }
155 return false;
156 }
157
SetFocused(bool aFocused)158 void XULTreeElement::SetFocused(bool aFocused) {
159 nsTreeBodyFrame* body = GetTreeBodyFrame();
160 if (body) {
161 body->SetFocused(aFocused);
162 }
163 }
164
GetTreeBody()165 already_AddRefed<Element> XULTreeElement::GetTreeBody() {
166 nsTreeBodyFrame* body = GetTreeBodyFrame();
167 if (body) {
168 nsCOMPtr<Element> element;
169 body->GetTreeBody(getter_AddRefs(element));
170 return element.forget();
171 }
172
173 return nullptr;
174 }
175
GetColumns(FlushType aFlushType)176 already_AddRefed<nsTreeColumns> XULTreeElement::GetColumns(
177 FlushType aFlushType) {
178 if (nsTreeBodyFrame* body = GetTreeBodyFrame(aFlushType)) {
179 return body->Columns();
180 }
181 return nullptr;
182 }
183
RowHeight()184 int32_t XULTreeElement::RowHeight() {
185 nsTreeBodyFrame* body = GetTreeBodyFrame();
186 if (body) {
187 return body->RowHeight();
188 }
189 return 0;
190 }
191
RowWidth()192 int32_t XULTreeElement::RowWidth() {
193 nsTreeBodyFrame* body = GetTreeBodyFrame();
194 if (body) {
195 return body->RowWidth();
196 }
197 return 0;
198 }
199
GetFirstVisibleRow()200 int32_t XULTreeElement::GetFirstVisibleRow() {
201 nsTreeBodyFrame* body = GetTreeBodyFrame();
202 if (body) {
203 return body->FirstVisibleRow();
204 }
205 return 0;
206 }
207
GetLastVisibleRow()208 int32_t XULTreeElement::GetLastVisibleRow() {
209 nsTreeBodyFrame* body = GetTreeBodyFrame();
210 if (body) {
211 return body->LastVisibleRow();
212 }
213 return 0;
214 }
215
HorizontalPosition()216 int32_t XULTreeElement::HorizontalPosition() {
217 nsTreeBodyFrame* body = GetTreeBodyFrame();
218 if (body) {
219 return body->GetHorizontalPosition();
220 }
221 return 0;
222 }
223
GetPageLength()224 int32_t XULTreeElement::GetPageLength() {
225 nsTreeBodyFrame* body = GetTreeBodyFrame();
226 if (body) {
227 return body->PageLength();
228 }
229 return 0;
230 }
231
EnsureRowIsVisible(int32_t aRow)232 void XULTreeElement::EnsureRowIsVisible(int32_t aRow) {
233 nsTreeBodyFrame* body = GetTreeBodyFrame();
234 if (body) {
235 body->EnsureRowIsVisible(aRow);
236 }
237 }
238
EnsureCellIsVisible(int32_t aRow,nsTreeColumn * aCol,ErrorResult & aRv)239 void XULTreeElement::EnsureCellIsVisible(int32_t aRow, nsTreeColumn* aCol,
240 ErrorResult& aRv) {
241 nsTreeBodyFrame* body = GetTreeBodyFrame();
242 if (body) {
243 nsresult rv = body->EnsureCellIsVisible(aRow, aCol);
244 if (NS_FAILED(rv)) {
245 aRv.Throw(rv);
246 }
247 }
248 }
249
ScrollToRow(int32_t aRow)250 void XULTreeElement::ScrollToRow(int32_t aRow) {
251 nsTreeBodyFrame* body = GetTreeBodyFrame(FlushType::Layout);
252 if (!body) {
253 return;
254 }
255
256 body->ScrollToRow(aRow);
257 }
258
ScrollByLines(int32_t aNumLines)259 void XULTreeElement::ScrollByLines(int32_t aNumLines) {
260 nsTreeBodyFrame* body = GetTreeBodyFrame();
261 if (!body) {
262 return;
263 }
264 body->ScrollByLines(aNumLines);
265 }
266
ScrollByPages(int32_t aNumPages)267 void XULTreeElement::ScrollByPages(int32_t aNumPages) {
268 nsTreeBodyFrame* body = GetTreeBodyFrame();
269 if (body) {
270 body->ScrollByPages(aNumPages);
271 }
272 }
273
Invalidate()274 void XULTreeElement::Invalidate() {
275 nsTreeBodyFrame* body = GetTreeBodyFrame();
276 if (body) {
277 body->Invalidate();
278 }
279 }
280
InvalidateColumn(nsTreeColumn * aCol)281 void XULTreeElement::InvalidateColumn(nsTreeColumn* aCol) {
282 nsTreeBodyFrame* body = GetTreeBodyFrame();
283 if (body) {
284 body->InvalidateColumn(aCol);
285 }
286 }
287
InvalidateRow(int32_t aIndex)288 void XULTreeElement::InvalidateRow(int32_t aIndex) {
289 nsTreeBodyFrame* body = GetTreeBodyFrame();
290 if (body) {
291 body->InvalidateRow(aIndex);
292 }
293 }
294
InvalidateCell(int32_t aRow,nsTreeColumn * aCol)295 void XULTreeElement::InvalidateCell(int32_t aRow, nsTreeColumn* aCol) {
296 nsTreeBodyFrame* body = GetTreeBodyFrame();
297 if (body) {
298 body->InvalidateCell(aRow, aCol);
299 }
300 }
301
InvalidateRange(int32_t aStart,int32_t aEnd)302 void XULTreeElement::InvalidateRange(int32_t aStart, int32_t aEnd) {
303 nsTreeBodyFrame* body = GetTreeBodyFrame();
304 if (body) {
305 body->InvalidateRange(aStart, aEnd);
306 }
307 }
308
GetRowAt(int32_t x,int32_t y)309 int32_t XULTreeElement::GetRowAt(int32_t x, int32_t y) {
310 nsTreeBodyFrame* body = GetTreeBodyFrame();
311 if (!body) {
312 return 0;
313 }
314 return body->GetRowAt(x, y);
315 }
316
GetCellAt(int32_t aX,int32_t aY,TreeCellInfo & aRetVal,ErrorResult & aRv)317 void XULTreeElement::GetCellAt(int32_t aX, int32_t aY, TreeCellInfo& aRetVal,
318 ErrorResult& aRv) {
319 aRetVal.mRow = 0;
320 aRetVal.mCol = nullptr;
321
322 nsTreeBodyFrame* body = GetTreeBodyFrame();
323 if (body) {
324 nsAutoCString element;
325 body->GetCellAt(aX, aY, &aRetVal.mRow, getter_AddRefs(aRetVal.mCol),
326 element);
327 CopyUTF8toUTF16(element, aRetVal.mChildElt);
328 }
329 }
330
GetCoordsForCellItem(int32_t aRow,nsTreeColumn * aCol,const nsAString & aElement,nsresult & rv)331 nsIntRect XULTreeElement::GetCoordsForCellItem(int32_t aRow, nsTreeColumn* aCol,
332 const nsAString& aElement,
333 nsresult& rv) {
334 rv = NS_OK;
335 nsIntRect rect;
336
337 nsTreeBodyFrame* body = GetTreeBodyFrame();
338 NS_ConvertUTF16toUTF8 element(aElement);
339 if (body) {
340 rv = body->GetCoordsForCellItem(aRow, aCol, element, &rect.x, &rect.y,
341 &rect.width, &rect.height);
342 }
343
344 return rect;
345 }
346
GetCoordsForCellItem(int32_t aRow,nsTreeColumn & aCol,const nsAString & aElement,ErrorResult & aRv)347 already_AddRefed<DOMRect> XULTreeElement::GetCoordsForCellItem(
348 int32_t aRow, nsTreeColumn& aCol, const nsAString& aElement,
349 ErrorResult& aRv) {
350 nsresult rv;
351 nsIntRect rect = GetCoordsForCellItem(aRow, &aCol, aElement, rv);
352 aRv = rv;
353
354 RefPtr<DOMRect> domRect =
355 new DOMRect(this, rect.x, rect.y, rect.width, rect.height);
356 return domRect.forget();
357 }
358
IsCellCropped(int32_t aRow,nsTreeColumn * aCol,ErrorResult & aRv)359 bool XULTreeElement::IsCellCropped(int32_t aRow, nsTreeColumn* aCol,
360 ErrorResult& aRv) {
361 bool cropped = false;
362
363 nsTreeBodyFrame* body = GetTreeBodyFrame();
364 if (body) {
365 aRv = body->IsCellCropped(aRow, aCol, &cropped);
366 }
367
368 return cropped;
369 }
370
RowCountChanged(int32_t aIndex,int32_t aDelta)371 void XULTreeElement::RowCountChanged(int32_t aIndex, int32_t aDelta) {
372 nsTreeBodyFrame* body = GetTreeBodyFrame();
373 if (body) {
374 body->RowCountChanged(aIndex, aDelta);
375 }
376 }
377
BeginUpdateBatch()378 void XULTreeElement::BeginUpdateBatch() {
379 nsTreeBodyFrame* body = GetTreeBodyFrame();
380 if (body) {
381 body->BeginUpdateBatch();
382 }
383 }
384
EndUpdateBatch()385 void XULTreeElement::EndUpdateBatch() {
386 nsTreeBodyFrame* body = GetTreeBodyFrame();
387 if (body) {
388 body->EndUpdateBatch();
389 }
390 }
391
ClearStyleAndImageCaches()392 void XULTreeElement::ClearStyleAndImageCaches() {
393 nsTreeBodyFrame* body = GetTreeBodyFrame();
394 if (body) {
395 body->ClearStyleAndImageCaches();
396 }
397 }
398
RemoveImageCacheEntry(int32_t aRowIndex,nsTreeColumn & aCol,ErrorResult & aRv)399 void XULTreeElement::RemoveImageCacheEntry(int32_t aRowIndex,
400 nsTreeColumn& aCol,
401 ErrorResult& aRv) {
402 if (NS_WARN_IF(aRowIndex < 0)) {
403 aRv.Throw(NS_ERROR_INVALID_ARG);
404 return;
405 }
406 nsTreeBodyFrame* body = GetTreeBodyFrame();
407 if (body) {
408 body->RemoveImageCacheEntry(aRowIndex, &aCol);
409 }
410 }
411
412 } // namespace dom
413 } // namespace mozilla
414