|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "nsTreeBoxObject.h" |
|
7 #include "nsCOMPtr.h" |
|
8 #include "nsIDOMXULElement.h" |
|
9 #include "nsIXULTemplateBuilder.h" |
|
10 #include "nsTreeContentView.h" |
|
11 #include "nsITreeSelection.h" |
|
12 #include "ChildIterator.h" |
|
13 #include "nsContentUtils.h" |
|
14 #include "nsError.h" |
|
15 #include "nsTreeBodyFrame.h" |
|
16 |
|
17 NS_IMPL_CYCLE_COLLECTION(nsTreeBoxObject, mView) |
|
18 |
|
19 NS_IMPL_ADDREF_INHERITED(nsTreeBoxObject, nsBoxObject) |
|
20 NS_IMPL_RELEASE_INHERITED(nsTreeBoxObject, nsBoxObject) |
|
21 |
|
22 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsTreeBoxObject) |
|
23 NS_INTERFACE_MAP_ENTRY(nsITreeBoxObject) |
|
24 NS_INTERFACE_MAP_END_INHERITING(nsBoxObject) |
|
25 |
|
26 void |
|
27 nsTreeBoxObject::Clear() |
|
28 { |
|
29 ClearCachedValues(); |
|
30 |
|
31 // Drop the view's ref to us. |
|
32 if (mView) { |
|
33 nsCOMPtr<nsITreeSelection> sel; |
|
34 mView->GetSelection(getter_AddRefs(sel)); |
|
35 if (sel) |
|
36 sel->SetTree(nullptr); |
|
37 mView->SetTree(nullptr); // Break the circular ref between the view and us. |
|
38 } |
|
39 mView = nullptr; |
|
40 |
|
41 nsBoxObject::Clear(); |
|
42 } |
|
43 |
|
44 |
|
45 nsTreeBoxObject::nsTreeBoxObject() |
|
46 : mTreeBody(nullptr) |
|
47 { |
|
48 } |
|
49 |
|
50 nsTreeBoxObject::~nsTreeBoxObject() |
|
51 { |
|
52 /* destructor code */ |
|
53 } |
|
54 |
|
55 static nsIContent* FindBodyElement(nsIContent* aParent) |
|
56 { |
|
57 mozilla::dom::FlattenedChildIterator iter(aParent); |
|
58 for (nsIContent* content = iter.GetNextChild(); content; content = iter.GetNextChild()) { |
|
59 nsINodeInfo *ni = content->NodeInfo(); |
|
60 if (ni->Equals(nsGkAtoms::treechildren, kNameSpaceID_XUL)) { |
|
61 return content; |
|
62 } else if (ni->Equals(nsGkAtoms::tree, kNameSpaceID_XUL)) { |
|
63 // There are nesting tree elements. Only the innermost should |
|
64 // find the treechilren. |
|
65 return nullptr; |
|
66 } else if (content->IsElement() && |
|
67 !ni->Equals(nsGkAtoms::_template, kNameSpaceID_XUL)) { |
|
68 nsIContent* result = FindBodyElement(content); |
|
69 if (result) |
|
70 return result; |
|
71 } |
|
72 } |
|
73 |
|
74 return nullptr; |
|
75 } |
|
76 |
|
77 nsTreeBodyFrame* |
|
78 nsTreeBoxObject::GetTreeBody(bool aFlushLayout) |
|
79 { |
|
80 // Make sure our frames are up to date, and layout as needed. We |
|
81 // have to do this before checking for our cached mTreeBody, since |
|
82 // it might go away on style flush, and in any case if aFlushLayout |
|
83 // is true we need to make sure to flush no matter what. |
|
84 // XXXbz except that flushing style when we were not asked to flush |
|
85 // layout here breaks things. See bug 585123. |
|
86 nsIFrame* frame; |
|
87 if (aFlushLayout) { |
|
88 frame = GetFrame(aFlushLayout); |
|
89 if (!frame) |
|
90 return nullptr; |
|
91 } |
|
92 |
|
93 if (mTreeBody) { |
|
94 // Have one cached already. |
|
95 return mTreeBody; |
|
96 } |
|
97 |
|
98 if (!aFlushLayout) { |
|
99 frame = GetFrame(aFlushLayout); |
|
100 if (!frame) |
|
101 return nullptr; |
|
102 } |
|
103 |
|
104 // Iterate over our content model children looking for the body. |
|
105 nsCOMPtr<nsIContent> content = FindBodyElement(frame->GetContent()); |
|
106 if (!content) |
|
107 return nullptr; |
|
108 |
|
109 frame = content->GetPrimaryFrame(); |
|
110 if (!frame) |
|
111 return nullptr; |
|
112 |
|
113 // Make sure that the treebodyframe has a pointer to |this|. |
|
114 nsTreeBodyFrame *treeBody = do_QueryFrame(frame); |
|
115 NS_ENSURE_TRUE(treeBody && treeBody->GetTreeBoxObject() == this, nullptr); |
|
116 |
|
117 mTreeBody = treeBody; |
|
118 return mTreeBody; |
|
119 } |
|
120 |
|
121 NS_IMETHODIMP nsTreeBoxObject::GetView(nsITreeView * *aView) |
|
122 { |
|
123 if (!mTreeBody) { |
|
124 if (!GetTreeBody()) { |
|
125 // Don't return an uninitialised view |
|
126 *aView = nullptr; |
|
127 return NS_OK; |
|
128 } |
|
129 |
|
130 if (mView) |
|
131 // Our new frame needs to initialise itself |
|
132 return mTreeBody->GetView(aView); |
|
133 } |
|
134 if (!mView) { |
|
135 nsCOMPtr<nsIDOMXULElement> xulele = do_QueryInterface(mContent); |
|
136 if (xulele) { |
|
137 // See if there is a XUL tree builder associated with the element |
|
138 nsCOMPtr<nsIXULTemplateBuilder> builder; |
|
139 xulele->GetBuilder(getter_AddRefs(builder)); |
|
140 mView = do_QueryInterface(builder); |
|
141 |
|
142 if (!mView) { |
|
143 // No tree builder, create a tree content view. |
|
144 nsresult rv = NS_NewTreeContentView(getter_AddRefs(mView)); |
|
145 NS_ENSURE_SUCCESS(rv, rv); |
|
146 } |
|
147 |
|
148 // Initialise the frame and view |
|
149 mTreeBody->SetView(mView); |
|
150 } |
|
151 } |
|
152 NS_IF_ADDREF(*aView = mView); |
|
153 return NS_OK; |
|
154 } |
|
155 |
|
156 static bool |
|
157 CanTrustView(nsISupports* aValue) |
|
158 { |
|
159 // Untrusted content is only allowed to specify known-good views |
|
160 if (nsContentUtils::IsCallerChrome()) |
|
161 return true; |
|
162 nsCOMPtr<nsINativeTreeView> nativeTreeView = do_QueryInterface(aValue); |
|
163 if (!nativeTreeView || NS_FAILED(nativeTreeView->EnsureNative())) { |
|
164 // XXX ERRMSG need a good error here for developers |
|
165 return false; |
|
166 } |
|
167 return true; |
|
168 } |
|
169 |
|
170 NS_IMETHODIMP nsTreeBoxObject::SetView(nsITreeView * aView) |
|
171 { |
|
172 if (!CanTrustView(aView)) |
|
173 return NS_ERROR_DOM_SECURITY_ERR; |
|
174 |
|
175 mView = aView; |
|
176 nsTreeBodyFrame* body = GetTreeBody(); |
|
177 if (body) |
|
178 body->SetView(aView); |
|
179 |
|
180 return NS_OK; |
|
181 } |
|
182 |
|
183 NS_IMETHODIMP nsTreeBoxObject::GetFocused(bool* aFocused) |
|
184 { |
|
185 *aFocused = false; |
|
186 nsTreeBodyFrame* body = GetTreeBody(); |
|
187 if (body) |
|
188 return body->GetFocused(aFocused); |
|
189 return NS_OK; |
|
190 } |
|
191 |
|
192 NS_IMETHODIMP nsTreeBoxObject::SetFocused(bool aFocused) |
|
193 { |
|
194 nsTreeBodyFrame* body = GetTreeBody(); |
|
195 if (body) |
|
196 return body->SetFocused(aFocused); |
|
197 return NS_OK; |
|
198 } |
|
199 |
|
200 NS_IMETHODIMP nsTreeBoxObject::GetTreeBody(nsIDOMElement** aElement) |
|
201 { |
|
202 *aElement = nullptr; |
|
203 nsTreeBodyFrame* body = GetTreeBody(); |
|
204 if (body) |
|
205 return body->GetTreeBody(aElement); |
|
206 return NS_OK; |
|
207 } |
|
208 |
|
209 NS_IMETHODIMP nsTreeBoxObject::GetColumns(nsITreeColumns** aColumns) |
|
210 { |
|
211 *aColumns = nullptr; |
|
212 nsTreeBodyFrame* body = GetTreeBody(); |
|
213 if (body) |
|
214 *aColumns = body->Columns().take(); |
|
215 return NS_OK; |
|
216 } |
|
217 |
|
218 NS_IMETHODIMP nsTreeBoxObject::GetRowHeight(int32_t* aRowHeight) |
|
219 { |
|
220 *aRowHeight = 0; |
|
221 nsTreeBodyFrame* body = GetTreeBody(); |
|
222 if (body) |
|
223 return body->GetRowHeight(aRowHeight); |
|
224 return NS_OK; |
|
225 } |
|
226 |
|
227 NS_IMETHODIMP nsTreeBoxObject::GetRowWidth(int32_t *aRowWidth) |
|
228 { |
|
229 *aRowWidth = 0; |
|
230 nsTreeBodyFrame* body = GetTreeBody(); |
|
231 if (body) |
|
232 return body->GetRowWidth(aRowWidth); |
|
233 return NS_OK; |
|
234 } |
|
235 |
|
236 NS_IMETHODIMP nsTreeBoxObject::GetFirstVisibleRow(int32_t *aFirstVisibleRow) |
|
237 { |
|
238 *aFirstVisibleRow = 0; |
|
239 nsTreeBodyFrame* body = GetTreeBody(); |
|
240 if (body) |
|
241 *aFirstVisibleRow = body->FirstVisibleRow(); |
|
242 return NS_OK; |
|
243 } |
|
244 |
|
245 NS_IMETHODIMP nsTreeBoxObject::GetLastVisibleRow(int32_t *aLastVisibleRow) |
|
246 { |
|
247 *aLastVisibleRow = 0; |
|
248 nsTreeBodyFrame* body = GetTreeBody(); |
|
249 if (body) |
|
250 *aLastVisibleRow = body->LastVisibleRow(); |
|
251 return NS_OK; |
|
252 } |
|
253 |
|
254 NS_IMETHODIMP nsTreeBoxObject::GetHorizontalPosition(int32_t *aHorizontalPosition) |
|
255 { |
|
256 *aHorizontalPosition = 0; |
|
257 nsTreeBodyFrame* body = GetTreeBody(); |
|
258 if (body) |
|
259 return body->GetHorizontalPosition(aHorizontalPosition); |
|
260 return NS_OK; |
|
261 } |
|
262 |
|
263 NS_IMETHODIMP nsTreeBoxObject::GetPageLength(int32_t *aPageLength) |
|
264 { |
|
265 *aPageLength = 0; |
|
266 nsTreeBodyFrame* body = GetTreeBody(); |
|
267 if (body) |
|
268 *aPageLength = body->PageLength(); |
|
269 return NS_OK; |
|
270 } |
|
271 |
|
272 NS_IMETHODIMP nsTreeBoxObject::GetSelectionRegion(nsIScriptableRegion **aRegion) |
|
273 { |
|
274 *aRegion = nullptr; |
|
275 nsTreeBodyFrame* body = GetTreeBody(); |
|
276 if (body) |
|
277 return body->GetSelectionRegion(aRegion); |
|
278 return NS_OK; |
|
279 } |
|
280 |
|
281 NS_IMETHODIMP |
|
282 nsTreeBoxObject::EnsureRowIsVisible(int32_t aRow) |
|
283 { |
|
284 nsTreeBodyFrame* body = GetTreeBody(); |
|
285 if (body) |
|
286 return body->EnsureRowIsVisible(aRow); |
|
287 return NS_OK; |
|
288 } |
|
289 |
|
290 NS_IMETHODIMP |
|
291 nsTreeBoxObject::EnsureCellIsVisible(int32_t aRow, nsITreeColumn* aCol) |
|
292 { |
|
293 nsTreeBodyFrame* body = GetTreeBody(); |
|
294 if (body) |
|
295 return body->EnsureCellIsVisible(aRow, aCol); |
|
296 return NS_OK; |
|
297 return NS_ERROR_NOT_IMPLEMENTED; |
|
298 } |
|
299 |
|
300 NS_IMETHODIMP |
|
301 nsTreeBoxObject::ScrollToRow(int32_t aRow) |
|
302 { |
|
303 nsTreeBodyFrame* body = GetTreeBody(true); |
|
304 if (body) |
|
305 return body->ScrollToRow(aRow); |
|
306 return NS_OK; |
|
307 } |
|
308 |
|
309 NS_IMETHODIMP |
|
310 nsTreeBoxObject::ScrollByLines(int32_t aNumLines) |
|
311 { |
|
312 nsTreeBodyFrame* body = GetTreeBody(); |
|
313 if (body) |
|
314 return body->ScrollByLines(aNumLines); |
|
315 return NS_OK; |
|
316 } |
|
317 |
|
318 NS_IMETHODIMP |
|
319 nsTreeBoxObject::ScrollByPages(int32_t aNumPages) |
|
320 { |
|
321 nsTreeBodyFrame* body = GetTreeBody(); |
|
322 if (body) |
|
323 return body->ScrollByPages(aNumPages); |
|
324 return NS_OK; |
|
325 } |
|
326 |
|
327 NS_IMETHODIMP |
|
328 nsTreeBoxObject::ScrollToCell(int32_t aRow, nsITreeColumn* aCol) |
|
329 { |
|
330 nsTreeBodyFrame* body = GetTreeBody(); |
|
331 if (body) |
|
332 return body->ScrollToCell(aRow, aCol); |
|
333 return NS_OK; |
|
334 } |
|
335 |
|
336 NS_IMETHODIMP |
|
337 nsTreeBoxObject::ScrollToColumn(nsITreeColumn* aCol) |
|
338 { |
|
339 nsTreeBodyFrame* body = GetTreeBody(); |
|
340 if (body) |
|
341 return body->ScrollToColumn(aCol); |
|
342 return NS_OK; |
|
343 } |
|
344 |
|
345 NS_IMETHODIMP |
|
346 nsTreeBoxObject::ScrollToHorizontalPosition(int32_t aHorizontalPosition) |
|
347 { |
|
348 nsTreeBodyFrame* body = GetTreeBody(); |
|
349 if (body) |
|
350 return body->ScrollToHorizontalPosition(aHorizontalPosition); |
|
351 return NS_OK; |
|
352 } |
|
353 |
|
354 NS_IMETHODIMP nsTreeBoxObject::Invalidate() |
|
355 { |
|
356 nsTreeBodyFrame* body = GetTreeBody(); |
|
357 if (body) |
|
358 return body->Invalidate(); |
|
359 return NS_OK; |
|
360 } |
|
361 |
|
362 NS_IMETHODIMP nsTreeBoxObject::InvalidateColumn(nsITreeColumn* aCol) |
|
363 { |
|
364 nsTreeBodyFrame* body = GetTreeBody(); |
|
365 if (body) |
|
366 return body->InvalidateColumn(aCol); |
|
367 return NS_OK; |
|
368 } |
|
369 |
|
370 NS_IMETHODIMP nsTreeBoxObject::InvalidateRow(int32_t aIndex) |
|
371 { |
|
372 nsTreeBodyFrame* body = GetTreeBody(); |
|
373 if (body) |
|
374 return body->InvalidateRow(aIndex); |
|
375 return NS_OK; |
|
376 } |
|
377 |
|
378 NS_IMETHODIMP nsTreeBoxObject::InvalidateCell(int32_t aRow, nsITreeColumn* aCol) |
|
379 { |
|
380 nsTreeBodyFrame* body = GetTreeBody(); |
|
381 if (body) |
|
382 return body->InvalidateCell(aRow, aCol); |
|
383 return NS_OK; |
|
384 } |
|
385 |
|
386 NS_IMETHODIMP nsTreeBoxObject::InvalidateRange(int32_t aStart, int32_t aEnd) |
|
387 { |
|
388 nsTreeBodyFrame* body = GetTreeBody(); |
|
389 if (body) |
|
390 return body->InvalidateRange(aStart, aEnd); |
|
391 return NS_OK; |
|
392 } |
|
393 |
|
394 NS_IMETHODIMP nsTreeBoxObject::InvalidateColumnRange(int32_t aStart, int32_t aEnd, nsITreeColumn* aCol) |
|
395 { |
|
396 nsTreeBodyFrame* body = GetTreeBody(); |
|
397 if (body) |
|
398 return body->InvalidateColumnRange(aStart, aEnd, aCol); |
|
399 return NS_OK; |
|
400 } |
|
401 |
|
402 NS_IMETHODIMP nsTreeBoxObject::GetRowAt(int32_t x, int32_t y, int32_t *aRow) |
|
403 { |
|
404 *aRow = 0; |
|
405 nsTreeBodyFrame* body = GetTreeBody(); |
|
406 if (body) |
|
407 return body->GetRowAt(x, y, aRow); |
|
408 return NS_OK; |
|
409 } |
|
410 |
|
411 NS_IMETHODIMP nsTreeBoxObject::GetCellAt(int32_t aX, int32_t aY, int32_t *aRow, nsITreeColumn** aCol, |
|
412 nsACString& aChildElt) |
|
413 { |
|
414 *aRow = 0; |
|
415 *aCol = nullptr; |
|
416 nsTreeBodyFrame* body = GetTreeBody(); |
|
417 if (body) |
|
418 return body->GetCellAt(aX, aY, aRow, aCol, aChildElt); |
|
419 return NS_OK; |
|
420 } |
|
421 |
|
422 NS_IMETHODIMP |
|
423 nsTreeBoxObject::GetCoordsForCellItem(int32_t aRow, nsITreeColumn* aCol, const nsACString& aElement, |
|
424 int32_t *aX, int32_t *aY, int32_t *aWidth, int32_t *aHeight) |
|
425 { |
|
426 *aX = *aY = *aWidth = *aHeight = 0; |
|
427 nsTreeBodyFrame* body = GetTreeBody(); |
|
428 if (body) |
|
429 return body->GetCoordsForCellItem(aRow, aCol, aElement, aX, aY, aWidth, aHeight); |
|
430 return NS_OK; |
|
431 } |
|
432 |
|
433 NS_IMETHODIMP |
|
434 nsTreeBoxObject::IsCellCropped(int32_t aRow, nsITreeColumn* aCol, bool *aIsCropped) |
|
435 { |
|
436 *aIsCropped = false; |
|
437 nsTreeBodyFrame* body = GetTreeBody(); |
|
438 if (body) |
|
439 return body->IsCellCropped(aRow, aCol, aIsCropped); |
|
440 return NS_OK; |
|
441 } |
|
442 |
|
443 NS_IMETHODIMP nsTreeBoxObject::RowCountChanged(int32_t aIndex, int32_t aDelta) |
|
444 { |
|
445 nsTreeBodyFrame* body = GetTreeBody(); |
|
446 if (body) |
|
447 return body->RowCountChanged(aIndex, aDelta); |
|
448 return NS_OK; |
|
449 } |
|
450 |
|
451 NS_IMETHODIMP nsTreeBoxObject::BeginUpdateBatch() |
|
452 { |
|
453 nsTreeBodyFrame* body = GetTreeBody(); |
|
454 if (body) |
|
455 return body->BeginUpdateBatch(); |
|
456 return NS_OK; |
|
457 } |
|
458 |
|
459 NS_IMETHODIMP nsTreeBoxObject::EndUpdateBatch() |
|
460 { |
|
461 nsTreeBodyFrame* body = GetTreeBody(); |
|
462 if (body) |
|
463 return body->EndUpdateBatch(); |
|
464 return NS_OK; |
|
465 } |
|
466 |
|
467 NS_IMETHODIMP nsTreeBoxObject::ClearStyleAndImageCaches() |
|
468 { |
|
469 nsTreeBodyFrame* body = GetTreeBody(); |
|
470 if (body) |
|
471 return body->ClearStyleAndImageCaches(); |
|
472 return NS_OK; |
|
473 } |
|
474 |
|
475 void |
|
476 nsTreeBoxObject::ClearCachedValues() |
|
477 { |
|
478 mTreeBody = nullptr; |
|
479 } |
|
480 |
|
481 // Creation Routine /////////////////////////////////////////////////////////////////////// |
|
482 |
|
483 nsresult |
|
484 NS_NewTreeBoxObject(nsIBoxObject** aResult) |
|
485 { |
|
486 *aResult = new nsTreeBoxObject; |
|
487 if (!*aResult) |
|
488 return NS_ERROR_OUT_OF_MEMORY; |
|
489 NS_ADDREF(*aResult); |
|
490 return NS_OK; |
|
491 } |
|
492 |