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