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