From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- layout/xul/BoxObject.cpp | 614 +++ layout/xul/BoxObject.h | 91 + layout/xul/ContainerBoxObject.cpp | 75 + layout/xul/ContainerBoxObject.h | 31 + layout/xul/ListBoxObject.cpp | 238 + layout/xul/ListBoxObject.h | 54 + layout/xul/MenuBoxObject.cpp | 151 + layout/xul/MenuBoxObject.h | 38 + layout/xul/PopupBoxObject.cpp | 384 ++ layout/xul/PopupBoxObject.h | 115 + layout/xul/ScrollBoxObject.cpp | 384 ++ layout/xul/ScrollBoxObject.h | 64 + layout/xul/crashtests/131008-1.xul | 11 + layout/xul/crashtests/137216-1.xul | 4 + layout/xul/crashtests/140218-1.xml | 4 + layout/xul/crashtests/151826-1.xul | 27 + layout/xul/crashtests/168724-1.xul | 18 + layout/xul/crashtests/189814-1.xul | 21 + layout/xul/crashtests/237787-1.xul | 9 + layout/xul/crashtests/265161-1.xul | 9 + layout/xul/crashtests/289410-1.xul | 14 + layout/xul/crashtests/290743.html | 6 + layout/xul/crashtests/291702-1.xul | 11 + layout/xul/crashtests/291702-2.xul | 11 + layout/xul/crashtests/291702-3.xul | 137 + layout/xul/crashtests/294371-1.xul | 53 + layout/xul/crashtests/311457-1.html | 12 + layout/xul/crashtests/321056-1.xhtml | 10 + layout/xul/crashtests/322786-1.xul | 6 + layout/xul/crashtests/325377.xul | 16 + layout/xul/crashtests/326834-1-inner.xul | 17 + layout/xul/crashtests/326834-1.html | 9 + layout/xul/crashtests/326879-1.xul | 31 + layout/xul/crashtests/327776-1.xul | 24 + layout/xul/crashtests/328135-1.xul | 27 + layout/xul/crashtests/329327-1.xul | 2 + layout/xul/crashtests/329407-1.xml | 14 + layout/xul/crashtests/329477-1.xhtml | 31 + layout/xul/crashtests/336962-1.xul | 17 + layout/xul/crashtests/344228-1.xul | 27 + layout/xul/crashtests/346083-1.xul | 13 + layout/xul/crashtests/346281-1.xul | 17 + layout/xul/crashtests/350460.xul | 8 + layout/xul/crashtests/360642-1.xul | 9 + layout/xul/crashtests/365151.xul | 39 + layout/xul/crashtests/366112-1.xul | 9 + layout/xul/crashtests/366203-1.xul | 40 + layout/xul/crashtests/367185-1.xhtml | 11 + layout/xul/crashtests/369942-1.xhtml | 36 + layout/xul/crashtests/374102-1.xul | 5 + layout/xul/crashtests/376137-1.html | 18 + layout/xul/crashtests/376137-2.html | 11 + layout/xul/crashtests/377592-1.svg | 27 + layout/xul/crashtests/378961.html | 9 + layout/xul/crashtests/381862.html | 23 + layout/xul/crashtests/382746-1.xul | 15 + layout/xul/crashtests/382899-1.xul | 9 + layout/xul/crashtests/383236-1.xul | 5 + layout/xul/crashtests/384037-1.xhtml | 9 + layout/xul/crashtests/384105-1-inner.xul | 21 + layout/xul/crashtests/384105-1.html | 9 + layout/xul/crashtests/384373-1.xul | 10 + layout/xul/crashtests/384373-2.xul | 4 + layout/xul/crashtests/384373.html | 23 + layout/xul/crashtests/384491-1.xhtml | 8 + layout/xul/crashtests/384871-1-inner.xul | 9 + layout/xul/crashtests/384871-1.html | 9 + layout/xul/crashtests/386642.xul | 31 + layout/xul/crashtests/387033-1.xhtml | 31 + layout/xul/crashtests/387080-1.xul | 6 + layout/xul/crashtests/391974-1-inner.xul | 19 + layout/xul/crashtests/391974-1.html | 9 + layout/xul/crashtests/394120-1.xhtml | 19 + layout/xul/crashtests/397293.xhtml | 21 + layout/xul/crashtests/397304-1.html | 1 + layout/xul/crashtests/398326-1.xhtml | 17 + layout/xul/crashtests/399013.xul | 31 + layout/xul/crashtests/400779-1.xhtml | 16 + layout/xul/crashtests/402912-1.xhtml | 5 + layout/xul/crashtests/404192.xhtml | 12 + layout/xul/crashtests/407152.xul | 7 + layout/xul/crashtests/408904-1.xul | 1 + layout/xul/crashtests/412479-1.xhtml | 4 + layout/xul/crashtests/415394-1.xhtml | 28 + layout/xul/crashtests/417509.xul | 7 + layout/xul/crashtests/420424-1.xul | 6 + layout/xul/crashtests/430356-1.xhtml | 5 + layout/xul/crashtests/431738.xhtml | 7 + layout/xul/crashtests/432058-1.xul | 31 + layout/xul/crashtests/432068-1.xul | 31 + layout/xul/crashtests/432068-2.xul | 24 + layout/xul/crashtests/433296-1.xul | 5 + layout/xul/crashtests/433429.xul | 23 + layout/xul/crashtests/434458-1.xul | 20 + layout/xul/crashtests/452185.html | 3 + layout/xul/crashtests/452185.xml | 5 + layout/xul/crashtests/460900-1.xul | 3 + layout/xul/crashtests/464149-1.xul | 24 + layout/xul/crashtests/464407-1.xhtml | 9 + layout/xul/crashtests/467080.xul | 24 + layout/xul/crashtests/467481-1.xul | 6 + layout/xul/crashtests/470063-1.html | 15 + layout/xul/crashtests/470272.html | 21 + layout/xul/crashtests/472189.xul | 13 + layout/xul/crashtests/475133.html | 20 + layout/xul/crashtests/488210-1.xhtml | 19 + layout/xul/crashtests/495728-1.xul | 239 + layout/xul/crashtests/508927-1.xul | 6 + layout/xul/crashtests/508927-2.xul | 6 + layout/xul/crashtests/514300-1.xul | 14 + layout/xul/crashtests/536931-1.xhtml | 4 + layout/xul/crashtests/538308-1.xul | 32 + layout/xul/crashtests/557174-1.xml | 1 + layout/xul/crashtests/564705-1.xul | 6 + layout/xul/crashtests/583957-1.html | 20 + layout/xul/crashtests/617089.html | 9 + layout/xul/crashtests/716503.html | 11 + layout/xul/crashtests/crashtests.list | 99 + layout/xul/crashtests/menulist-focused.xhtml | 5 + layout/xul/grid/crashtests/306911-crash.xul | 4 + .../xul/grid/crashtests/306911-grid-testcases.xul | 99 + .../xul/grid/crashtests/306911-grid-testcases2.xul | 98 + layout/xul/grid/crashtests/311710-1.xul | 22 + layout/xul/grid/crashtests/312784-1.xul | 29 + layout/xul/grid/crashtests/313173-1-inner.xul | 41 + layout/xul/grid/crashtests/313173-1.html | 9 + layout/xul/grid/crashtests/321066-1.xul | 8 + layout/xul/grid/crashtests/321073-1.xul | 7 + layout/xul/grid/crashtests/382750-1.xul | 5 + layout/xul/grid/crashtests/400790-1.xul | 20 + layout/xul/grid/crashtests/423802-crash.xul | 13 + layout/xul/grid/crashtests/crashtests.list | 11 + layout/xul/grid/examples/borderedcolumns.xul | 43 + layout/xul/grid/examples/borderedrowscolumns.xul | 55 + layout/xul/grid/examples/borderedrowscolumns2.xul | 44 + layout/xul/grid/examples/borderedrowscolumns3.xul | 57 + layout/xul/grid/examples/bordermargincolumns1.xul | 44 + layout/xul/grid/examples/collapsetest.xul | 67 + layout/xul/grid/examples/divcolumngrid.xul | 33 + layout/xul/grid/examples/divrowgrid.xul | 36 + layout/xul/grid/examples/dynamicgrid.xul | 370 ++ layout/xul/grid/examples/flexgroupgrid.xul | 47 + layout/xul/grid/examples/javascriptappend.xul | 42 + layout/xul/grid/examples/jumpygrid.xul | 82 + layout/xul/grid/examples/nestedrows.xul | 48 + layout/xul/grid/examples/rowspan.xul | 41 + layout/xul/grid/examples/scrollingcolumns.xul | 80 + layout/xul/grid/examples/scrollingrows.xul | 80 + layout/xul/grid/examples/splitter.xul | 40 + layout/xul/grid/moz.build | 43 + layout/xul/grid/nsGrid.cpp | 1276 +++++ layout/xul/grid/nsGrid.h | 132 + layout/xul/grid/nsGridCell.cpp | 127 + layout/xul/grid/nsGridCell.h | 53 + layout/xul/grid/nsGridLayout2.cpp | 266 ++ layout/xul/grid/nsGridLayout2.h | 78 + layout/xul/grid/nsGridRow.cpp | 57 + layout/xul/grid/nsGridRow.h | 57 + layout/xul/grid/nsGridRowGroupFrame.cpp | 63 + layout/xul/grid/nsGridRowGroupFrame.h | 49 + layout/xul/grid/nsGridRowGroupLayout.cpp | 265 ++ layout/xul/grid/nsGridRowGroupLayout.h | 51 + layout/xul/grid/nsGridRowLayout.cpp | 197 + layout/xul/grid/nsGridRowLayout.h | 60 + layout/xul/grid/nsGridRowLeafFrame.cpp | 94 + layout/xul/grid/nsGridRowLeafFrame.h | 54 + layout/xul/grid/nsGridRowLeafLayout.cpp | 328 ++ layout/xul/grid/nsGridRowLeafLayout.h | 62 + layout/xul/grid/nsIGridPart.h | 95 + layout/xul/grid/reftests/column-sizing-1-ref.xul | 13 + layout/xul/grid/reftests/column-sizing-1.xul | 15 + layout/xul/grid/reftests/not-full-basic-ref.xhtml | 27 + layout/xul/grid/reftests/not-full-basic.xul | 45 + .../xul/grid/reftests/not-full-grid-pack-align.xul | 46 + .../reftests/not-full-row-group-align-ref.xhtml | 27 + .../xul/grid/reftests/not-full-row-group-align.xul | 46 + .../not-full-row-group-direction-ref.xhtml | 27 + .../grid/reftests/not-full-row-group-direction.xul | 46 + .../reftests/not-full-row-group-pack-ref.xhtml | 31 + .../xul/grid/reftests/not-full-row-group-pack.xul | 46 + .../xul/grid/reftests/not-full-row-leaf-align.xul | 46 + .../grid/reftests/not-full-row-leaf-direction.xul | 46 + .../grid/reftests/not-full-row-leaf-pack-ref.xhtml | 27 + .../xul/grid/reftests/not-full-row-leaf-pack.xul | 46 + layout/xul/grid/reftests/reftest-stylo.list | 38 + layout/xul/grid/reftests/reftest.list | 18 + .../xul/grid/reftests/row-or-column-sizing-1.xul | 21 + .../xul/grid/reftests/row-or-column-sizing-2.xul | 21 + .../xul/grid/reftests/row-or-column-sizing-3.xul | 27 + .../xul/grid/reftests/row-or-column-sizing-4.xul | 27 + layout/xul/grid/reftests/row-sizing-1-ref.xul | 14 + layout/xul/grid/reftests/row-sizing-1.xul | 16 + .../xul/grid/reftests/scrollable-columns-ref.xhtml | 25 + layout/xul/grid/reftests/scrollable-columns.xul | 49 + layout/xul/grid/reftests/scrollable-rows-ref.xhtml | 25 + layout/xul/grid/reftests/scrollable-rows.xul | 49 + layout/xul/grid/reftests/sizing-2d-ref.xul | 12 + layout/xul/grid/reftests/sizing-2d.xul | 26 + layout/xul/grid/reftests/z-order-1-ref.xul | 30 + layout/xul/grid/reftests/z-order-1.xul | 47 + layout/xul/grid/reftests/z-order-2-ref.xul | 30 + layout/xul/grid/reftests/z-order-2.xul | 47 + layout/xul/moz.build | 107 + layout/xul/nsBox.cpp | 981 ++++ layout/xul/nsBox.h | 127 + layout/xul/nsBoxFrame.cpp | 2111 +++++++++ layout/xul/nsBoxFrame.h | 257 + layout/xul/nsBoxLayout.cpp | 94 + layout/xul/nsBoxLayout.h | 65 + layout/xul/nsBoxLayoutState.cpp | 38 + layout/xul/nsBoxLayoutState.h | 77 + layout/xul/nsButtonBoxFrame.cpp | 230 + layout/xul/nsButtonBoxFrame.h | 75 + layout/xul/nsDeckFrame.cpp | 231 + layout/xul/nsDeckFrame.h | 77 + layout/xul/nsDocElementBoxFrame.cpp | 148 + layout/xul/nsGroupBoxFrame.cpp | 311 ++ layout/xul/nsIBoxObject.idl | 40 + layout/xul/nsIBrowserBoxObject.idl | 16 + layout/xul/nsIContainerBoxObject.idl | 14 + layout/xul/nsIListBoxObject.idl | 19 + layout/xul/nsIMenuBoxObject.idl | 14 + layout/xul/nsIRootBox.h | 33 + layout/xul/nsIScrollBoxObject.idl | 12 + layout/xul/nsIScrollbarMediator.h | 91 + layout/xul/nsISliderListener.idl | 26 + layout/xul/nsImageBoxFrame.cpp | 828 ++++ layout/xul/nsImageBoxFrame.h | 163 + layout/xul/nsLeafBoxFrame.cpp | 390 ++ layout/xul/nsLeafBoxFrame.h | 95 + layout/xul/nsListBoxBodyFrame.cpp | 1535 ++++++ layout/xul/nsListBoxBodyFrame.h | 217 + layout/xul/nsListBoxLayout.cpp | 212 + layout/xul/nsListBoxLayout.h | 32 + layout/xul/nsListItemFrame.cpp | 69 + layout/xul/nsListItemFrame.h | 34 + layout/xul/nsMenuBarFrame.cpp | 434 ++ layout/xul/nsMenuBarFrame.h | 125 + layout/xul/nsMenuBarListener.cpp | 455 ++ layout/xul/nsMenuBarListener.h | 74 + layout/xul/nsMenuFrame.cpp | 1514 ++++++ layout/xul/nsMenuFrame.h | 288 ++ layout/xul/nsMenuParent.h | 69 + layout/xul/nsMenuPopupFrame.cpp | 2442 ++++++++++ layout/xul/nsMenuPopupFrame.h | 628 +++ layout/xul/nsPIBoxObject.h | 37 + layout/xul/nsPIListBoxObject.h | 30 + layout/xul/nsPopupSetFrame.cpp | 160 + layout/xul/nsPopupSetFrame.h | 64 + layout/xul/nsProgressMeterFrame.cpp | 184 + layout/xul/nsProgressMeterFrame.h | 46 + layout/xul/nsRepeatService.cpp | 86 + layout/xul/nsRepeatService.h | 60 + layout/xul/nsResizerFrame.cpp | 538 +++ layout/xul/nsResizerFrame.h | 71 + layout/xul/nsRootBoxFrame.cpp | 291 ++ layout/xul/nsScrollBoxFrame.cpp | 178 + layout/xul/nsScrollbarButtonFrame.cpp | 298 ++ layout/xul/nsScrollbarButtonFrame.h | 81 + layout/xul/nsScrollbarFrame.cpp | 311 ++ layout/xul/nsScrollbarFrame.h | 110 + layout/xul/nsSliderFrame.cpp | 1446 ++++++ layout/xul/nsSliderFrame.h | 206 + layout/xul/nsSplitterFrame.cpp | 1046 +++++ layout/xul/nsSplitterFrame.h | 83 + layout/xul/nsSprocketLayout.cpp | 1652 +++++++ layout/xul/nsSprocketLayout.h | 142 + layout/xul/nsStackFrame.cpp | 63 + layout/xul/nsStackFrame.h | 46 + layout/xul/nsStackLayout.cpp | 385 ++ layout/xul/nsStackLayout.h | 56 + layout/xul/nsTextBoxFrame.cpp | 1241 +++++ layout/xul/nsTextBoxFrame.h | 134 + layout/xul/nsTitleBarFrame.cpp | 172 + layout/xul/nsTitleBarFrame.h | 39 + layout/xul/nsXULLabelFrame.cpp | 116 + layout/xul/nsXULLabelFrame.h | 57 + layout/xul/nsXULPopupManager.cpp | 2870 ++++++++++++ layout/xul/nsXULPopupManager.h | 823 ++++ layout/xul/nsXULTooltipListener.cpp | 729 +++ layout/xul/nsXULTooltipListener.h | 102 + .../xul/reftest/image-scaling-min-height-1-ref.xul | 14 + layout/xul/reftest/image-scaling-min-height-1.xul | 14 + layout/xul/reftest/image-size-ref.xul | 115 + layout/xul/reftest/image-size.xul | 123 + layout/xul/reftest/image4x3.png | Bin 0 -> 176 bytes layout/xul/reftest/popup-explicit-size-ref.xul | 6 + layout/xul/reftest/popup-explicit-size.xul | 7 + layout/xul/reftest/reftest-stylo.list | 14 + layout/xul/reftest/reftest.list | 6 + layout/xul/reftest/textbox-multiline-noresize.xul | 5 + layout/xul/reftest/textbox-multiline-ref.xul | 5 + layout/xul/reftest/textbox-multiline-resize.xul | 5 + layout/xul/reftest/textbox-text-transform-ref.xul | 6 + layout/xul/reftest/textbox-text-transform.xul | 6 + layout/xul/test/browser.ini | 8 + layout/xul/test/browser_bug1163304.js | 35 + layout/xul/test/browser_bug685470.js | 19 + layout/xul/test/browser_bug703210.js | 33 + layout/xul/test/browser_bug706743.js | 84 + layout/xul/test/chrome.ini | 24 + layout/xul/test/mochitest.ini | 12 + layout/xul/test/test_bug1197913.xul | 68 + layout/xul/test/test_bug159346.xul | 135 + layout/xul/test/test_bug372685.xul | 49 + layout/xul/test/test_bug381167.xhtml | 49 + layout/xul/test/test_bug386386.html | 34 + layout/xul/test/test_bug393970.xul | 91 + layout/xul/test/test_bug394800.xhtml | 39 + layout/xul/test/test_bug398982-1.xul | 31 + layout/xul/test/test_bug398982-2.xul | 33 + layout/xul/test/test_bug467442.xul | 54 + layout/xul/test/test_bug477754.xul | 49 + layout/xul/test/test_bug511075.html | 121 + layout/xul/test/test_bug563416.html | 53 + layout/xul/test/test_bug703150.xul | 69 + layout/xul/test/test_bug987230.xul | 125 + layout/xul/test/test_popupReflowPos.xul | 76 + layout/xul/test/test_popupSizeTo.xul | 55 + layout/xul/test/test_popupZoom.xul | 57 + layout/xul/test/test_resizer.xul | 94 + layout/xul/test/test_resizer_incontent.xul | 42 + layout/xul/test/test_splitter.xul | 117 + layout/xul/test/test_stack.xul | 327 ++ layout/xul/test/test_submenuClose.xul | 91 + layout/xul/test/test_windowminmaxsize.xul | 245 + layout/xul/test/window_resizer.xul | 113 + layout/xul/test/window_resizer_element.xul | 188 + layout/xul/tree/TreeBoxObject.cpp | 695 +++ layout/xul/tree/TreeBoxObject.h | 128 + layout/xul/tree/crashtests/307298-1.xul | 21 + layout/xul/tree/crashtests/309732-1.xul | 30 + layout/xul/tree/crashtests/309732-2.xul | 31 + layout/xul/tree/crashtests/366583-1.xul | 43 + layout/xul/tree/crashtests/380217-1.xul | 24 + layout/xul/tree/crashtests/382444-1-inner.html | 15 + layout/xul/tree/crashtests/382444-1.html | 9 + layout/xul/tree/crashtests/391178-1.xhtml | 41 + layout/xul/tree/crashtests/391178-2.xul | 20 + layout/xul/tree/crashtests/393665-1.xul | 3 + layout/xul/tree/crashtests/399227-1.xul | 44 + layout/xul/tree/crashtests/399227-2.xul | 50 + layout/xul/tree/crashtests/399692-1.xhtml | 10 + layout/xul/tree/crashtests/399715-1.xhtml | 9 + layout/xul/tree/crashtests/409807-1.xul | 25 + layout/xul/tree/crashtests/414170-1.xul | 20 + layout/xul/tree/crashtests/430394-1.xul | 8 + layout/xul/tree/crashtests/454186-1.xul | 23 + layout/xul/tree/crashtests/479931-1.xhtml | 19 + layout/xul/tree/crashtests/509602-1-overlay.xul | 12 + layout/xul/tree/crashtests/509602-1.xul | 3 + layout/xul/tree/crashtests/585815-iframe.xul | 72 + layout/xul/tree/crashtests/585815.html | 18 + layout/xul/tree/crashtests/601427.html | 30 + layout/xul/tree/crashtests/730441-1.xul | 54 + layout/xul/tree/crashtests/730441-2.xul | 52 + layout/xul/tree/crashtests/730441-3.xul | 38 + layout/xul/tree/crashtests/crashtests.list | 24 + layout/xul/tree/moz.build | 53 + layout/xul/tree/nsITreeBoxObject.idl | 189 + layout/xul/tree/nsITreeColumns.idl | 96 + layout/xul/tree/nsITreeContentView.idl | 22 + layout/xul/tree/nsITreeSelection.idl | 130 + layout/xul/tree/nsITreeView.idl | 215 + layout/xul/tree/nsTreeBodyFrame.cpp | 4945 ++++++++++++++++++++ layout/xul/tree/nsTreeBodyFrame.h | 651 +++ layout/xul/tree/nsTreeColFrame.cpp | 201 + layout/xul/tree/nsTreeColFrame.h | 55 + layout/xul/tree/nsTreeColumns.cpp | 765 +++ layout/xul/tree/nsTreeColumns.h | 215 + layout/xul/tree/nsTreeContentView.cpp | 1372 ++++++ layout/xul/tree/nsTreeContentView.h | 101 + layout/xul/tree/nsTreeImageListener.cpp | 115 + layout/xul/tree/nsTreeImageListener.h | 66 + layout/xul/tree/nsTreeSelection.cpp | 866 ++++ layout/xul/tree/nsTreeSelection.h | 56 + layout/xul/tree/nsTreeStyleCache.cpp | 99 + layout/xul/tree/nsTreeStyleCache.h | 89 + layout/xul/tree/nsTreeUtils.cpp | 140 + layout/xul/tree/nsTreeUtils.h | 39 + 380 files changed, 54040 insertions(+) create mode 100644 layout/xul/BoxObject.cpp create mode 100644 layout/xul/BoxObject.h create mode 100644 layout/xul/ContainerBoxObject.cpp create mode 100644 layout/xul/ContainerBoxObject.h create mode 100644 layout/xul/ListBoxObject.cpp create mode 100644 layout/xul/ListBoxObject.h create mode 100644 layout/xul/MenuBoxObject.cpp create mode 100644 layout/xul/MenuBoxObject.h create mode 100644 layout/xul/PopupBoxObject.cpp create mode 100644 layout/xul/PopupBoxObject.h create mode 100644 layout/xul/ScrollBoxObject.cpp create mode 100644 layout/xul/ScrollBoxObject.h create mode 100644 layout/xul/crashtests/131008-1.xul create mode 100644 layout/xul/crashtests/137216-1.xul create mode 100644 layout/xul/crashtests/140218-1.xml create mode 100644 layout/xul/crashtests/151826-1.xul create mode 100644 layout/xul/crashtests/168724-1.xul create mode 100644 layout/xul/crashtests/189814-1.xul create mode 100644 layout/xul/crashtests/237787-1.xul create mode 100644 layout/xul/crashtests/265161-1.xul create mode 100644 layout/xul/crashtests/289410-1.xul create mode 100644 layout/xul/crashtests/290743.html create mode 100644 layout/xul/crashtests/291702-1.xul create mode 100644 layout/xul/crashtests/291702-2.xul create mode 100644 layout/xul/crashtests/291702-3.xul create mode 100644 layout/xul/crashtests/294371-1.xul create mode 100644 layout/xul/crashtests/311457-1.html create mode 100644 layout/xul/crashtests/321056-1.xhtml create mode 100644 layout/xul/crashtests/322786-1.xul create mode 100644 layout/xul/crashtests/325377.xul create mode 100644 layout/xul/crashtests/326834-1-inner.xul create mode 100644 layout/xul/crashtests/326834-1.html create mode 100644 layout/xul/crashtests/326879-1.xul create mode 100644 layout/xul/crashtests/327776-1.xul create mode 100644 layout/xul/crashtests/328135-1.xul create mode 100644 layout/xul/crashtests/329327-1.xul create mode 100644 layout/xul/crashtests/329407-1.xml create mode 100644 layout/xul/crashtests/329477-1.xhtml create mode 100644 layout/xul/crashtests/336962-1.xul create mode 100644 layout/xul/crashtests/344228-1.xul create mode 100644 layout/xul/crashtests/346083-1.xul create mode 100644 layout/xul/crashtests/346281-1.xul create mode 100644 layout/xul/crashtests/350460.xul create mode 100644 layout/xul/crashtests/360642-1.xul create mode 100644 layout/xul/crashtests/365151.xul create mode 100644 layout/xul/crashtests/366112-1.xul create mode 100644 layout/xul/crashtests/366203-1.xul create mode 100644 layout/xul/crashtests/367185-1.xhtml create mode 100644 layout/xul/crashtests/369942-1.xhtml create mode 100644 layout/xul/crashtests/374102-1.xul create mode 100644 layout/xul/crashtests/376137-1.html create mode 100644 layout/xul/crashtests/376137-2.html create mode 100644 layout/xul/crashtests/377592-1.svg create mode 100644 layout/xul/crashtests/378961.html create mode 100644 layout/xul/crashtests/381862.html create mode 100644 layout/xul/crashtests/382746-1.xul create mode 100644 layout/xul/crashtests/382899-1.xul create mode 100644 layout/xul/crashtests/383236-1.xul create mode 100644 layout/xul/crashtests/384037-1.xhtml create mode 100644 layout/xul/crashtests/384105-1-inner.xul create mode 100644 layout/xul/crashtests/384105-1.html create mode 100644 layout/xul/crashtests/384373-1.xul create mode 100644 layout/xul/crashtests/384373-2.xul create mode 100644 layout/xul/crashtests/384373.html create mode 100644 layout/xul/crashtests/384491-1.xhtml create mode 100644 layout/xul/crashtests/384871-1-inner.xul create mode 100644 layout/xul/crashtests/384871-1.html create mode 100644 layout/xul/crashtests/386642.xul create mode 100644 layout/xul/crashtests/387033-1.xhtml create mode 100644 layout/xul/crashtests/387080-1.xul create mode 100644 layout/xul/crashtests/391974-1-inner.xul create mode 100644 layout/xul/crashtests/391974-1.html create mode 100644 layout/xul/crashtests/394120-1.xhtml create mode 100644 layout/xul/crashtests/397293.xhtml create mode 100644 layout/xul/crashtests/397304-1.html create mode 100644 layout/xul/crashtests/398326-1.xhtml create mode 100644 layout/xul/crashtests/399013.xul create mode 100644 layout/xul/crashtests/400779-1.xhtml create mode 100644 layout/xul/crashtests/402912-1.xhtml create mode 100644 layout/xul/crashtests/404192.xhtml create mode 100644 layout/xul/crashtests/407152.xul create mode 100644 layout/xul/crashtests/408904-1.xul create mode 100644 layout/xul/crashtests/412479-1.xhtml create mode 100644 layout/xul/crashtests/415394-1.xhtml create mode 100644 layout/xul/crashtests/417509.xul create mode 100644 layout/xul/crashtests/420424-1.xul create mode 100644 layout/xul/crashtests/430356-1.xhtml create mode 100644 layout/xul/crashtests/431738.xhtml create mode 100644 layout/xul/crashtests/432058-1.xul create mode 100644 layout/xul/crashtests/432068-1.xul create mode 100644 layout/xul/crashtests/432068-2.xul create mode 100644 layout/xul/crashtests/433296-1.xul create mode 100644 layout/xul/crashtests/433429.xul create mode 100644 layout/xul/crashtests/434458-1.xul create mode 100644 layout/xul/crashtests/452185.html create mode 100644 layout/xul/crashtests/452185.xml create mode 100644 layout/xul/crashtests/460900-1.xul create mode 100644 layout/xul/crashtests/464149-1.xul create mode 100644 layout/xul/crashtests/464407-1.xhtml create mode 100644 layout/xul/crashtests/467080.xul create mode 100644 layout/xul/crashtests/467481-1.xul create mode 100644 layout/xul/crashtests/470063-1.html create mode 100644 layout/xul/crashtests/470272.html create mode 100644 layout/xul/crashtests/472189.xul create mode 100644 layout/xul/crashtests/475133.html create mode 100644 layout/xul/crashtests/488210-1.xhtml create mode 100644 layout/xul/crashtests/495728-1.xul create mode 100644 layout/xul/crashtests/508927-1.xul create mode 100644 layout/xul/crashtests/508927-2.xul create mode 100644 layout/xul/crashtests/514300-1.xul create mode 100644 layout/xul/crashtests/536931-1.xhtml create mode 100644 layout/xul/crashtests/538308-1.xul create mode 100644 layout/xul/crashtests/557174-1.xml create mode 100644 layout/xul/crashtests/564705-1.xul create mode 100644 layout/xul/crashtests/583957-1.html create mode 100644 layout/xul/crashtests/617089.html create mode 100644 layout/xul/crashtests/716503.html create mode 100644 layout/xul/crashtests/crashtests.list create mode 100644 layout/xul/crashtests/menulist-focused.xhtml create mode 100644 layout/xul/grid/crashtests/306911-crash.xul create mode 100644 layout/xul/grid/crashtests/306911-grid-testcases.xul create mode 100644 layout/xul/grid/crashtests/306911-grid-testcases2.xul create mode 100644 layout/xul/grid/crashtests/311710-1.xul create mode 100644 layout/xul/grid/crashtests/312784-1.xul create mode 100644 layout/xul/grid/crashtests/313173-1-inner.xul create mode 100644 layout/xul/grid/crashtests/313173-1.html create mode 100644 layout/xul/grid/crashtests/321066-1.xul create mode 100644 layout/xul/grid/crashtests/321073-1.xul create mode 100644 layout/xul/grid/crashtests/382750-1.xul create mode 100644 layout/xul/grid/crashtests/400790-1.xul create mode 100644 layout/xul/grid/crashtests/423802-crash.xul create mode 100644 layout/xul/grid/crashtests/crashtests.list create mode 100644 layout/xul/grid/examples/borderedcolumns.xul create mode 100644 layout/xul/grid/examples/borderedrowscolumns.xul create mode 100644 layout/xul/grid/examples/borderedrowscolumns2.xul create mode 100644 layout/xul/grid/examples/borderedrowscolumns3.xul create mode 100644 layout/xul/grid/examples/bordermargincolumns1.xul create mode 100644 layout/xul/grid/examples/collapsetest.xul create mode 100644 layout/xul/grid/examples/divcolumngrid.xul create mode 100644 layout/xul/grid/examples/divrowgrid.xul create mode 100644 layout/xul/grid/examples/dynamicgrid.xul create mode 100644 layout/xul/grid/examples/flexgroupgrid.xul create mode 100644 layout/xul/grid/examples/javascriptappend.xul create mode 100644 layout/xul/grid/examples/jumpygrid.xul create mode 100644 layout/xul/grid/examples/nestedrows.xul create mode 100644 layout/xul/grid/examples/rowspan.xul create mode 100644 layout/xul/grid/examples/scrollingcolumns.xul create mode 100644 layout/xul/grid/examples/scrollingrows.xul create mode 100644 layout/xul/grid/examples/splitter.xul create mode 100644 layout/xul/grid/moz.build create mode 100644 layout/xul/grid/nsGrid.cpp create mode 100644 layout/xul/grid/nsGrid.h create mode 100644 layout/xul/grid/nsGridCell.cpp create mode 100644 layout/xul/grid/nsGridCell.h create mode 100644 layout/xul/grid/nsGridLayout2.cpp create mode 100644 layout/xul/grid/nsGridLayout2.h create mode 100644 layout/xul/grid/nsGridRow.cpp create mode 100644 layout/xul/grid/nsGridRow.h create mode 100644 layout/xul/grid/nsGridRowGroupFrame.cpp create mode 100644 layout/xul/grid/nsGridRowGroupFrame.h create mode 100644 layout/xul/grid/nsGridRowGroupLayout.cpp create mode 100644 layout/xul/grid/nsGridRowGroupLayout.h create mode 100644 layout/xul/grid/nsGridRowLayout.cpp create mode 100644 layout/xul/grid/nsGridRowLayout.h create mode 100644 layout/xul/grid/nsGridRowLeafFrame.cpp create mode 100644 layout/xul/grid/nsGridRowLeafFrame.h create mode 100644 layout/xul/grid/nsGridRowLeafLayout.cpp create mode 100644 layout/xul/grid/nsGridRowLeafLayout.h create mode 100644 layout/xul/grid/nsIGridPart.h create mode 100644 layout/xul/grid/reftests/column-sizing-1-ref.xul create mode 100644 layout/xul/grid/reftests/column-sizing-1.xul create mode 100644 layout/xul/grid/reftests/not-full-basic-ref.xhtml create mode 100644 layout/xul/grid/reftests/not-full-basic.xul create mode 100644 layout/xul/grid/reftests/not-full-grid-pack-align.xul create mode 100644 layout/xul/grid/reftests/not-full-row-group-align-ref.xhtml create mode 100644 layout/xul/grid/reftests/not-full-row-group-align.xul create mode 100644 layout/xul/grid/reftests/not-full-row-group-direction-ref.xhtml create mode 100644 layout/xul/grid/reftests/not-full-row-group-direction.xul create mode 100644 layout/xul/grid/reftests/not-full-row-group-pack-ref.xhtml create mode 100644 layout/xul/grid/reftests/not-full-row-group-pack.xul create mode 100644 layout/xul/grid/reftests/not-full-row-leaf-align.xul create mode 100644 layout/xul/grid/reftests/not-full-row-leaf-direction.xul create mode 100644 layout/xul/grid/reftests/not-full-row-leaf-pack-ref.xhtml create mode 100644 layout/xul/grid/reftests/not-full-row-leaf-pack.xul create mode 100644 layout/xul/grid/reftests/reftest-stylo.list create mode 100644 layout/xul/grid/reftests/reftest.list create mode 100644 layout/xul/grid/reftests/row-or-column-sizing-1.xul create mode 100644 layout/xul/grid/reftests/row-or-column-sizing-2.xul create mode 100644 layout/xul/grid/reftests/row-or-column-sizing-3.xul create mode 100644 layout/xul/grid/reftests/row-or-column-sizing-4.xul create mode 100644 layout/xul/grid/reftests/row-sizing-1-ref.xul create mode 100644 layout/xul/grid/reftests/row-sizing-1.xul create mode 100644 layout/xul/grid/reftests/scrollable-columns-ref.xhtml create mode 100644 layout/xul/grid/reftests/scrollable-columns.xul create mode 100644 layout/xul/grid/reftests/scrollable-rows-ref.xhtml create mode 100644 layout/xul/grid/reftests/scrollable-rows.xul create mode 100644 layout/xul/grid/reftests/sizing-2d-ref.xul create mode 100644 layout/xul/grid/reftests/sizing-2d.xul create mode 100644 layout/xul/grid/reftests/z-order-1-ref.xul create mode 100644 layout/xul/grid/reftests/z-order-1.xul create mode 100644 layout/xul/grid/reftests/z-order-2-ref.xul create mode 100644 layout/xul/grid/reftests/z-order-2.xul create mode 100644 layout/xul/moz.build create mode 100644 layout/xul/nsBox.cpp create mode 100644 layout/xul/nsBox.h create mode 100644 layout/xul/nsBoxFrame.cpp create mode 100644 layout/xul/nsBoxFrame.h create mode 100644 layout/xul/nsBoxLayout.cpp create mode 100644 layout/xul/nsBoxLayout.h create mode 100644 layout/xul/nsBoxLayoutState.cpp create mode 100644 layout/xul/nsBoxLayoutState.h create mode 100644 layout/xul/nsButtonBoxFrame.cpp create mode 100644 layout/xul/nsButtonBoxFrame.h create mode 100644 layout/xul/nsDeckFrame.cpp create mode 100644 layout/xul/nsDeckFrame.h create mode 100644 layout/xul/nsDocElementBoxFrame.cpp create mode 100644 layout/xul/nsGroupBoxFrame.cpp create mode 100644 layout/xul/nsIBoxObject.idl create mode 100644 layout/xul/nsIBrowserBoxObject.idl create mode 100644 layout/xul/nsIContainerBoxObject.idl create mode 100644 layout/xul/nsIListBoxObject.idl create mode 100644 layout/xul/nsIMenuBoxObject.idl create mode 100644 layout/xul/nsIRootBox.h create mode 100644 layout/xul/nsIScrollBoxObject.idl create mode 100644 layout/xul/nsIScrollbarMediator.h create mode 100644 layout/xul/nsISliderListener.idl create mode 100644 layout/xul/nsImageBoxFrame.cpp create mode 100644 layout/xul/nsImageBoxFrame.h create mode 100644 layout/xul/nsLeafBoxFrame.cpp create mode 100644 layout/xul/nsLeafBoxFrame.h create mode 100644 layout/xul/nsListBoxBodyFrame.cpp create mode 100644 layout/xul/nsListBoxBodyFrame.h create mode 100644 layout/xul/nsListBoxLayout.cpp create mode 100644 layout/xul/nsListBoxLayout.h create mode 100644 layout/xul/nsListItemFrame.cpp create mode 100644 layout/xul/nsListItemFrame.h create mode 100644 layout/xul/nsMenuBarFrame.cpp create mode 100644 layout/xul/nsMenuBarFrame.h create mode 100644 layout/xul/nsMenuBarListener.cpp create mode 100644 layout/xul/nsMenuBarListener.h create mode 100644 layout/xul/nsMenuFrame.cpp create mode 100644 layout/xul/nsMenuFrame.h create mode 100644 layout/xul/nsMenuParent.h create mode 100644 layout/xul/nsMenuPopupFrame.cpp create mode 100644 layout/xul/nsMenuPopupFrame.h create mode 100644 layout/xul/nsPIBoxObject.h create mode 100644 layout/xul/nsPIListBoxObject.h create mode 100644 layout/xul/nsPopupSetFrame.cpp create mode 100644 layout/xul/nsPopupSetFrame.h create mode 100644 layout/xul/nsProgressMeterFrame.cpp create mode 100644 layout/xul/nsProgressMeterFrame.h create mode 100644 layout/xul/nsRepeatService.cpp create mode 100644 layout/xul/nsRepeatService.h create mode 100644 layout/xul/nsResizerFrame.cpp create mode 100644 layout/xul/nsResizerFrame.h create mode 100644 layout/xul/nsRootBoxFrame.cpp create mode 100644 layout/xul/nsScrollBoxFrame.cpp create mode 100644 layout/xul/nsScrollbarButtonFrame.cpp create mode 100644 layout/xul/nsScrollbarButtonFrame.h create mode 100644 layout/xul/nsScrollbarFrame.cpp create mode 100644 layout/xul/nsScrollbarFrame.h create mode 100644 layout/xul/nsSliderFrame.cpp create mode 100644 layout/xul/nsSliderFrame.h create mode 100644 layout/xul/nsSplitterFrame.cpp create mode 100644 layout/xul/nsSplitterFrame.h create mode 100644 layout/xul/nsSprocketLayout.cpp create mode 100644 layout/xul/nsSprocketLayout.h create mode 100644 layout/xul/nsStackFrame.cpp create mode 100644 layout/xul/nsStackFrame.h create mode 100644 layout/xul/nsStackLayout.cpp create mode 100644 layout/xul/nsStackLayout.h create mode 100644 layout/xul/nsTextBoxFrame.cpp create mode 100644 layout/xul/nsTextBoxFrame.h create mode 100644 layout/xul/nsTitleBarFrame.cpp create mode 100644 layout/xul/nsTitleBarFrame.h create mode 100644 layout/xul/nsXULLabelFrame.cpp create mode 100644 layout/xul/nsXULLabelFrame.h create mode 100644 layout/xul/nsXULPopupManager.cpp create mode 100644 layout/xul/nsXULPopupManager.h create mode 100644 layout/xul/nsXULTooltipListener.cpp create mode 100644 layout/xul/nsXULTooltipListener.h create mode 100644 layout/xul/reftest/image-scaling-min-height-1-ref.xul create mode 100644 layout/xul/reftest/image-scaling-min-height-1.xul create mode 100644 layout/xul/reftest/image-size-ref.xul create mode 100644 layout/xul/reftest/image-size.xul create mode 100644 layout/xul/reftest/image4x3.png create mode 100644 layout/xul/reftest/popup-explicit-size-ref.xul create mode 100644 layout/xul/reftest/popup-explicit-size.xul create mode 100644 layout/xul/reftest/reftest-stylo.list create mode 100644 layout/xul/reftest/reftest.list create mode 100644 layout/xul/reftest/textbox-multiline-noresize.xul create mode 100644 layout/xul/reftest/textbox-multiline-ref.xul create mode 100644 layout/xul/reftest/textbox-multiline-resize.xul create mode 100644 layout/xul/reftest/textbox-text-transform-ref.xul create mode 100644 layout/xul/reftest/textbox-text-transform.xul create mode 100644 layout/xul/test/browser.ini create mode 100644 layout/xul/test/browser_bug1163304.js create mode 100644 layout/xul/test/browser_bug685470.js create mode 100644 layout/xul/test/browser_bug703210.js create mode 100644 layout/xul/test/browser_bug706743.js create mode 100644 layout/xul/test/chrome.ini create mode 100644 layout/xul/test/mochitest.ini create mode 100644 layout/xul/test/test_bug1197913.xul create mode 100644 layout/xul/test/test_bug159346.xul create mode 100644 layout/xul/test/test_bug372685.xul create mode 100644 layout/xul/test/test_bug381167.xhtml create mode 100644 layout/xul/test/test_bug386386.html create mode 100644 layout/xul/test/test_bug393970.xul create mode 100644 layout/xul/test/test_bug394800.xhtml create mode 100644 layout/xul/test/test_bug398982-1.xul create mode 100644 layout/xul/test/test_bug398982-2.xul create mode 100644 layout/xul/test/test_bug467442.xul create mode 100644 layout/xul/test/test_bug477754.xul create mode 100644 layout/xul/test/test_bug511075.html create mode 100644 layout/xul/test/test_bug563416.html create mode 100644 layout/xul/test/test_bug703150.xul create mode 100644 layout/xul/test/test_bug987230.xul create mode 100644 layout/xul/test/test_popupReflowPos.xul create mode 100644 layout/xul/test/test_popupSizeTo.xul create mode 100644 layout/xul/test/test_popupZoom.xul create mode 100644 layout/xul/test/test_resizer.xul create mode 100644 layout/xul/test/test_resizer_incontent.xul create mode 100644 layout/xul/test/test_splitter.xul create mode 100644 layout/xul/test/test_stack.xul create mode 100644 layout/xul/test/test_submenuClose.xul create mode 100644 layout/xul/test/test_windowminmaxsize.xul create mode 100644 layout/xul/test/window_resizer.xul create mode 100644 layout/xul/test/window_resizer_element.xul create mode 100644 layout/xul/tree/TreeBoxObject.cpp create mode 100644 layout/xul/tree/TreeBoxObject.h create mode 100644 layout/xul/tree/crashtests/307298-1.xul create mode 100644 layout/xul/tree/crashtests/309732-1.xul create mode 100644 layout/xul/tree/crashtests/309732-2.xul create mode 100644 layout/xul/tree/crashtests/366583-1.xul create mode 100644 layout/xul/tree/crashtests/380217-1.xul create mode 100644 layout/xul/tree/crashtests/382444-1-inner.html create mode 100644 layout/xul/tree/crashtests/382444-1.html create mode 100644 layout/xul/tree/crashtests/391178-1.xhtml create mode 100644 layout/xul/tree/crashtests/391178-2.xul create mode 100644 layout/xul/tree/crashtests/393665-1.xul create mode 100644 layout/xul/tree/crashtests/399227-1.xul create mode 100644 layout/xul/tree/crashtests/399227-2.xul create mode 100644 layout/xul/tree/crashtests/399692-1.xhtml create mode 100644 layout/xul/tree/crashtests/399715-1.xhtml create mode 100644 layout/xul/tree/crashtests/409807-1.xul create mode 100644 layout/xul/tree/crashtests/414170-1.xul create mode 100644 layout/xul/tree/crashtests/430394-1.xul create mode 100644 layout/xul/tree/crashtests/454186-1.xul create mode 100644 layout/xul/tree/crashtests/479931-1.xhtml create mode 100644 layout/xul/tree/crashtests/509602-1-overlay.xul create mode 100644 layout/xul/tree/crashtests/509602-1.xul create mode 100644 layout/xul/tree/crashtests/585815-iframe.xul create mode 100644 layout/xul/tree/crashtests/585815.html create mode 100644 layout/xul/tree/crashtests/601427.html create mode 100644 layout/xul/tree/crashtests/730441-1.xul create mode 100644 layout/xul/tree/crashtests/730441-2.xul create mode 100644 layout/xul/tree/crashtests/730441-3.xul create mode 100644 layout/xul/tree/crashtests/crashtests.list create mode 100644 layout/xul/tree/moz.build create mode 100644 layout/xul/tree/nsITreeBoxObject.idl create mode 100644 layout/xul/tree/nsITreeColumns.idl create mode 100644 layout/xul/tree/nsITreeContentView.idl create mode 100644 layout/xul/tree/nsITreeSelection.idl create mode 100644 layout/xul/tree/nsITreeView.idl create mode 100644 layout/xul/tree/nsTreeBodyFrame.cpp create mode 100644 layout/xul/tree/nsTreeBodyFrame.h create mode 100644 layout/xul/tree/nsTreeColFrame.cpp create mode 100644 layout/xul/tree/nsTreeColFrame.h create mode 100644 layout/xul/tree/nsTreeColumns.cpp create mode 100644 layout/xul/tree/nsTreeColumns.h create mode 100644 layout/xul/tree/nsTreeContentView.cpp create mode 100644 layout/xul/tree/nsTreeContentView.h create mode 100644 layout/xul/tree/nsTreeImageListener.cpp create mode 100644 layout/xul/tree/nsTreeImageListener.h create mode 100644 layout/xul/tree/nsTreeSelection.cpp create mode 100644 layout/xul/tree/nsTreeSelection.h create mode 100644 layout/xul/tree/nsTreeStyleCache.cpp create mode 100644 layout/xul/tree/nsTreeStyleCache.h create mode 100644 layout/xul/tree/nsTreeUtils.cpp create mode 100644 layout/xul/tree/nsTreeUtils.h (limited to 'layout/xul') diff --git a/layout/xul/BoxObject.cpp b/layout/xul/BoxObject.cpp new file mode 100644 index 000000000..6636a6d62 --- /dev/null +++ b/layout/xul/BoxObject.cpp @@ -0,0 +1,614 @@ +/* -*- 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 "mozilla/dom/BoxObject.h" +#include "nsCOMPtr.h" +#include "nsIDocument.h" +#include "nsIPresShell.h" +#include "nsPresContext.h" +#include "nsIContent.h" +#include "nsContainerFrame.h" +#include "nsIDocShell.h" +#include "nsReadableUtils.h" +#include "nsDOMClassInfoID.h" +#include "nsView.h" +#ifdef MOZ_XUL +#include "nsIDOMXULElement.h" +#else +#include "nsIDOMElement.h" +#endif +#include "nsLayoutUtils.h" +#include "nsISupportsPrimitives.h" +#include "nsSupportsPrimitives.h" +#include "mozilla/dom/Element.h" +#include "nsComponentManagerUtils.h" +#include "mozilla/dom/BoxObjectBinding.h" + +// Implementation ///////////////////////////////////////////////////////////////// + +namespace mozilla { +namespace dom { + +// Static member variable initialization + +// Implement our nsISupports methods +NS_IMPL_CYCLE_COLLECTION_CLASS(BoxObject) +NS_IMPL_CYCLE_COLLECTING_ADDREF(BoxObject) +NS_IMPL_CYCLE_COLLECTING_RELEASE(BoxObject) + +// QueryInterface implementation for BoxObject +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BoxObject) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsIBoxObject) + NS_INTERFACE_MAP_ENTRY(nsPIBoxObject) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BoxObject) + // XXX jmorton: why aren't we unlinking mPropertyTable? + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BoxObject) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS + if (tmp->mPropertyTable) { + for (auto iter = tmp->mPropertyTable->Iter(); !iter.Done(); iter.Next()) { + cb.NoteXPCOMChild(iter.UserData()); + } + } +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(BoxObject) + +// Constructors/Destructors +BoxObject::BoxObject() + : mContent(nullptr) +{ +} + +BoxObject::~BoxObject() +{ +} + +NS_IMETHODIMP +BoxObject::GetElement(nsIDOMElement** aResult) +{ + if (mContent) { + return CallQueryInterface(mContent, aResult); + } + + *aResult = nullptr; + return NS_OK; +} + +// nsPIBoxObject ////////////////////////////////////////////////////////////////////////// + +nsresult +BoxObject::Init(nsIContent* aContent) +{ + mContent = aContent; + return NS_OK; +} + +void +BoxObject::Clear() +{ + mPropertyTable = nullptr; + mContent = nullptr; +} + +void +BoxObject::ClearCachedValues() +{ +} + +nsIFrame* +BoxObject::GetFrame(bool aFlushLayout) +{ + nsIPresShell* shell = GetPresShell(aFlushLayout); + if (!shell) + return nullptr; + + if (!aFlushLayout) { + // If we didn't flush layout when getting the presshell, we should at least + // flush to make sure our frame model is up to date. + // XXXbz should flush on document, no? Except people call this from + // frame code, maybe? + shell->FlushPendingNotifications(Flush_Frames); + } + + // The flush might have killed mContent. + if (!mContent) { + return nullptr; + } + + return mContent->GetPrimaryFrame(); +} + +nsIPresShell* +BoxObject::GetPresShell(bool aFlushLayout) +{ + if (!mContent) { + return nullptr; + } + + nsCOMPtr doc = mContent->GetUncomposedDoc(); + if (!doc) { + return nullptr; + } + + if (aFlushLayout) { + doc->FlushPendingNotifications(Flush_Layout); + } + + return doc->GetShell(); +} + +nsresult +BoxObject::GetOffsetRect(nsIntRect& aRect) +{ + aRect.SetRect(0, 0, 0, 0); + + if (!mContent) + return NS_ERROR_NOT_INITIALIZED; + + // Get the Frame for our content + nsIFrame* frame = GetFrame(true); + if (frame) { + // Get its origin + nsPoint origin = frame->GetPositionIgnoringScrolling(); + + // Find the frame parent whose content is the document element. + Element* docElement = mContent->GetComposedDoc()->GetRootElement(); + nsIFrame* parent = frame->GetParent(); + for (;;) { + // If we've hit the document element, break here + if (parent->GetContent() == docElement) { + break; + } + + nsIFrame* next = parent->GetParent(); + if (!next) { + NS_WARNING("We should have hit the document element..."); + origin += parent->GetPosition(); + break; + } + + // Add the parent's origin to our own to get to the + // right coordinate system + origin += next->GetPositionOfChildIgnoringScrolling(parent); + parent = next; + } + + // For the origin, add in the border for the frame + const nsStyleBorder* border = frame->StyleBorder(); + origin.x += border->GetComputedBorderWidth(NS_SIDE_LEFT); + origin.y += border->GetComputedBorderWidth(NS_SIDE_TOP); + + // And subtract out the border for the parent + const nsStyleBorder* parentBorder = parent->StyleBorder(); + origin.x -= parentBorder->GetComputedBorderWidth(NS_SIDE_LEFT); + origin.y -= parentBorder->GetComputedBorderWidth(NS_SIDE_TOP); + + aRect.x = nsPresContext::AppUnitsToIntCSSPixels(origin.x); + aRect.y = nsPresContext::AppUnitsToIntCSSPixels(origin.y); + + // Get the union of all rectangles in this and continuation frames. + // It doesn't really matter what we use as aRelativeTo here, since + // we only care about the size. Using 'parent' might make things + // a bit faster by speeding up the internal GetOffsetTo operations. + nsRect rcFrame = nsLayoutUtils::GetAllInFlowRectsUnion(frame, parent); + aRect.width = nsPresContext::AppUnitsToIntCSSPixels(rcFrame.width); + aRect.height = nsPresContext::AppUnitsToIntCSSPixels(rcFrame.height); + } + + return NS_OK; +} + +nsresult +BoxObject::GetScreenPosition(nsIntPoint& aPoint) +{ + aPoint.x = aPoint.y = 0; + + if (!mContent) + return NS_ERROR_NOT_INITIALIZED; + + nsIFrame* frame = GetFrame(true); + if (frame) { + nsIntRect rect = frame->GetScreenRect(); + aPoint.x = rect.x; + aPoint.y = rect.y; + } + + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetX(int32_t* aResult) +{ + nsIntRect rect; + GetOffsetRect(rect); + *aResult = rect.x; + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetY(int32_t* aResult) +{ + nsIntRect rect; + GetOffsetRect(rect); + *aResult = rect.y; + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetWidth(int32_t* aResult) +{ + nsIntRect rect; + GetOffsetRect(rect); + *aResult = rect.width; + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetHeight(int32_t* aResult) +{ + nsIntRect rect; + GetOffsetRect(rect); + *aResult = rect.height; + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetScreenX(int32_t *_retval) +{ + nsIntPoint position; + nsresult rv = GetScreenPosition(position); + if (NS_FAILED(rv)) return rv; + *_retval = position.x; + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetScreenY(int32_t *_retval) +{ + nsIntPoint position; + nsresult rv = GetScreenPosition(position); + if (NS_FAILED(rv)) return rv; + *_retval = position.y; + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetPropertyAsSupports(const char16_t* aPropertyName, nsISupports** aResult) +{ + NS_ENSURE_ARG(aPropertyName && *aPropertyName); + if (!mPropertyTable) { + *aResult = nullptr; + return NS_OK; + } + nsDependentString propertyName(aPropertyName); + mPropertyTable->Get(propertyName, aResult); // Addref here. + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::SetPropertyAsSupports(const char16_t* aPropertyName, nsISupports* aValue) +{ + NS_ENSURE_ARG(aPropertyName && *aPropertyName); + + if (!mPropertyTable) { + mPropertyTable = new nsInterfaceHashtable(4); + } + + nsDependentString propertyName(aPropertyName); + mPropertyTable->Put(propertyName, aValue); + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetProperty(const char16_t* aPropertyName, char16_t** aResult) +{ + nsCOMPtr data; + nsresult rv = GetPropertyAsSupports(aPropertyName,getter_AddRefs(data)); + NS_ENSURE_SUCCESS(rv, rv); + + if (!data) { + *aResult = nullptr; + return NS_OK; + } + + nsCOMPtr supportsStr = do_QueryInterface(data); + if (!supportsStr) { + return NS_ERROR_FAILURE; + } + + return supportsStr->ToString(aResult); +} + +NS_IMETHODIMP +BoxObject::SetProperty(const char16_t* aPropertyName, const char16_t* aPropertyValue) +{ + NS_ENSURE_ARG(aPropertyName && *aPropertyName); + + nsDependentString propertyName(aPropertyName); + nsDependentString propertyValue; + if (aPropertyValue) { + propertyValue.Rebind(aPropertyValue); + } else { + propertyValue.SetIsVoid(true); + } + + nsCOMPtr supportsStr(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID)); + NS_ENSURE_TRUE(supportsStr, NS_ERROR_OUT_OF_MEMORY); + supportsStr->SetData(propertyValue); + + return SetPropertyAsSupports(aPropertyName,supportsStr); +} + +NS_IMETHODIMP +BoxObject::RemoveProperty(const char16_t* aPropertyName) +{ + NS_ENSURE_ARG(aPropertyName && *aPropertyName); + + if (!mPropertyTable) return NS_OK; + + nsDependentString propertyName(aPropertyName); + mPropertyTable->Remove(propertyName); + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetParentBox(nsIDOMElement * *aParentBox) +{ + *aParentBox = nullptr; + nsIFrame* frame = GetFrame(false); + if (!frame) return NS_OK; + nsIFrame* parent = frame->GetParent(); + if (!parent) return NS_OK; + + nsCOMPtr el = do_QueryInterface(parent->GetContent()); + *aParentBox = el; + NS_IF_ADDREF(*aParentBox); + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetFirstChild(nsIDOMElement * *aFirstVisibleChild) +{ + *aFirstVisibleChild = nullptr; + nsIFrame* frame = GetFrame(false); + if (!frame) return NS_OK; + nsIFrame* firstFrame = frame->PrincipalChildList().FirstChild(); + if (!firstFrame) return NS_OK; + // get the content for the box and query to a dom element + nsCOMPtr el = do_QueryInterface(firstFrame->GetContent()); + el.swap(*aFirstVisibleChild); + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetLastChild(nsIDOMElement * *aLastVisibleChild) +{ + *aLastVisibleChild = nullptr; + nsIFrame* frame = GetFrame(false); + if (!frame) return NS_OK; + return GetPreviousSibling(frame, nullptr, aLastVisibleChild); +} + +NS_IMETHODIMP +BoxObject::GetNextSibling(nsIDOMElement **aNextOrdinalSibling) +{ + *aNextOrdinalSibling = nullptr; + nsIFrame* frame = GetFrame(false); + if (!frame) return NS_OK; + nsIFrame* nextFrame = frame->GetNextSibling(); + if (!nextFrame) return NS_OK; + // get the content for the box and query to a dom element + nsCOMPtr el = do_QueryInterface(nextFrame->GetContent()); + el.swap(*aNextOrdinalSibling); + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetPreviousSibling(nsIDOMElement **aPreviousOrdinalSibling) +{ + *aPreviousOrdinalSibling = nullptr; + nsIFrame* frame = GetFrame(false); + if (!frame) return NS_OK; + nsIFrame* parentFrame = frame->GetParent(); + if (!parentFrame) return NS_OK; + return GetPreviousSibling(parentFrame, frame, aPreviousOrdinalSibling); +} + +nsresult +BoxObject::GetPreviousSibling(nsIFrame* aParentFrame, nsIFrame* aFrame, + nsIDOMElement** aResult) +{ + *aResult = nullptr; + nsIFrame* nextFrame = aParentFrame->PrincipalChildList().FirstChild(); + nsIFrame* prevFrame = nullptr; + while (nextFrame) { + if (nextFrame == aFrame) + break; + prevFrame = nextFrame; + nextFrame = nextFrame->GetNextSibling(); + } + + if (!prevFrame) return NS_OK; + // get the content for the box and query to a dom element + nsCOMPtr el = do_QueryInterface(prevFrame->GetContent()); + el.swap(*aResult); + return NS_OK; +} + +nsIContent* +BoxObject::GetParentObject() const +{ + return mContent; +} + +JSObject* +BoxObject::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return BoxObjectBinding::Wrap(aCx, this, aGivenProto); +} + +Element* +BoxObject::GetElement() const +{ + return mContent && mContent->IsElement() ? mContent->AsElement() : nullptr; +} + +int32_t +BoxObject::X() +{ + int32_t ret = 0; + GetX(&ret); + return ret; +} + +int32_t +BoxObject::Y() +{ + int32_t ret = 0; + GetY(&ret); + return ret; +} + +int32_t +BoxObject::GetScreenX(ErrorResult& aRv) +{ + int32_t ret = 0; + aRv = GetScreenX(&ret); + return ret; +} + +int32_t +BoxObject::GetScreenY(ErrorResult& aRv) +{ + int32_t ret = 0; + aRv = GetScreenY(&ret); + return ret; +} + +int32_t +BoxObject::Width() +{ + int32_t ret = 0; + GetWidth(&ret); + return ret; +} + +int32_t +BoxObject::Height() +{ + int32_t ret = 0; + GetHeight(&ret); + return ret; +} + +already_AddRefed +BoxObject::GetPropertyAsSupports(const nsAString& propertyName) +{ + nsCOMPtr ret; + GetPropertyAsSupports(PromiseFlatString(propertyName).get(), getter_AddRefs(ret)); + return ret.forget(); +} + +void +BoxObject::SetPropertyAsSupports(const nsAString& propertyName, nsISupports* value) +{ + SetPropertyAsSupports(PromiseFlatString(propertyName).get(), value); +} + +void +BoxObject::GetProperty(const nsAString& propertyName, nsString& aRetVal, ErrorResult& aRv) +{ + nsCOMPtr data(GetPropertyAsSupports(propertyName)); + if (!data) { + return; + } + + nsCOMPtr supportsStr(do_QueryInterface(data)); + if (!supportsStr) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + supportsStr->GetData(aRetVal); +} + +void +BoxObject::SetProperty(const nsAString& propertyName, const nsAString& propertyValue) +{ + SetProperty(PromiseFlatString(propertyName).get(), PromiseFlatString(propertyValue).get()); +} + +void +BoxObject::RemoveProperty(const nsAString& propertyName) +{ + RemoveProperty(PromiseFlatString(propertyName).get()); +} + +already_AddRefed +BoxObject::GetParentBox() +{ + nsCOMPtr el; + GetParentBox(getter_AddRefs(el)); + nsCOMPtr ret(do_QueryInterface(el)); + return ret.forget(); +} + +already_AddRefed +BoxObject::GetFirstChild() +{ + nsCOMPtr el; + GetFirstChild(getter_AddRefs(el)); + nsCOMPtr ret(do_QueryInterface(el)); + return ret.forget(); +} + +already_AddRefed +BoxObject::GetLastChild() +{ + nsCOMPtr el; + GetLastChild(getter_AddRefs(el)); + nsCOMPtr ret(do_QueryInterface(el)); + return ret.forget(); +} + +already_AddRefed +BoxObject::GetNextSibling() +{ + nsCOMPtr el; + GetNextSibling(getter_AddRefs(el)); + nsCOMPtr ret(do_QueryInterface(el)); + return ret.forget(); +} + +already_AddRefed +BoxObject::GetPreviousSibling() +{ + nsCOMPtr el; + GetPreviousSibling(getter_AddRefs(el)); + nsCOMPtr ret(do_QueryInterface(el)); + return ret.forget(); +} + +} // namespace dom +} // namespace mozilla + +// Creation Routine /////////////////////////////////////////////////////////////////////// + +using namespace mozilla::dom; + +nsresult +NS_NewBoxObject(nsIBoxObject** aResult) +{ + NS_ADDREF(*aResult = new BoxObject()); + return NS_OK; +} diff --git a/layout/xul/BoxObject.h b/layout/xul/BoxObject.h new file mode 100644 index 000000000..ac3df420b --- /dev/null +++ b/layout/xul/BoxObject.h @@ -0,0 +1,91 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#ifndef mozilla_dom_BoxObject_h__ +#define mozilla_dom_BoxObject_h__ + +#include "mozilla/Attributes.h" +#include "mozilla/ErrorResult.h" +#include "nsCOMPtr.h" +#include "nsIBoxObject.h" +#include "nsPIBoxObject.h" +#include "nsPoint.h" +#include "nsAutoPtr.h" +#include "nsHashKeys.h" +#include "nsInterfaceHashtable.h" +#include "nsCycleCollectionParticipant.h" +#include "nsWrapperCache.h" +#include "nsRect.h" + +class nsIFrame; +class nsIPresShell; + +namespace mozilla { +namespace dom { + +class Element; + +class BoxObject : public nsPIBoxObject, + public nsWrapperCache +{ + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(BoxObject) + NS_DECL_NSIBOXOBJECT + +public: + BoxObject(); + + // nsPIBoxObject + virtual nsresult Init(nsIContent* aContent) override; + virtual void Clear() override; + virtual void ClearCachedValues() override; + + nsIFrame* GetFrame(bool aFlushLayout); + nsIPresShell* GetPresShell(bool aFlushLayout); + nsresult GetOffsetRect(nsIntRect& aRect); + nsresult GetScreenPosition(nsIntPoint& aPoint); + + // Given a parent frame and a child frame, find the frame whose + // next sibling is the given child frame and return its element + static nsresult GetPreviousSibling(nsIFrame* aParentFrame, nsIFrame* aFrame, + nsIDOMElement** aResult); + + // WebIDL (wraps old impls) + nsIContent* GetParentObject() const; + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + + Element* GetElement() const; + + int32_t X(); + int32_t Y(); + int32_t GetScreenX(ErrorResult& aRv); + int32_t GetScreenY(ErrorResult& aRv); + int32_t Width(); + int32_t Height(); + + already_AddRefed GetPropertyAsSupports(const nsAString& propertyName); + void SetPropertyAsSupports(const nsAString& propertyName, nsISupports* value); + void GetProperty(const nsAString& propertyName, nsString& aRetVal, ErrorResult& aRv); + void SetProperty(const nsAString& propertyName, const nsAString& propertyValue); + void RemoveProperty(const nsAString& propertyName); + + already_AddRefed GetParentBox(); + already_AddRefed GetFirstChild(); + already_AddRefed GetLastChild(); + already_AddRefed GetNextSibling(); + already_AddRefed GetPreviousSibling(); + +protected: + virtual ~BoxObject(); + + nsAutoPtr > mPropertyTable; //[OWNER] + + nsIContent* mContent; // [WEAK] +}; + +} // namespace dom +} // namespace mozilla + +#endif diff --git a/layout/xul/ContainerBoxObject.cpp b/layout/xul/ContainerBoxObject.cpp new file mode 100644 index 000000000..0464a6fec --- /dev/null +++ b/layout/xul/ContainerBoxObject.cpp @@ -0,0 +1,75 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "mozilla/dom/ContainerBoxObject.h" +#include "mozilla/dom/ContainerBoxObjectBinding.h" +#include "nsCOMPtr.h" +#include "nsIDocShell.h" +#include "nsIContent.h" +#include "nsIDocument.h" +#include "nsIFrame.h" +#include "nsSubDocumentFrame.h" + +namespace mozilla { +namespace dom { + +ContainerBoxObject::ContainerBoxObject() +{ +} + +ContainerBoxObject::~ContainerBoxObject() +{ +} + +JSObject* +ContainerBoxObject::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return ContainerBoxObjectBinding::Wrap(aCx, this, aGivenProto); +} + +already_AddRefed +ContainerBoxObject::GetDocShell() +{ + nsSubDocumentFrame *subDocFrame = do_QueryFrame(GetFrame(false)); + if (subDocFrame) { + // Ok, the frame for mContent is an nsSubDocumentFrame, it knows how + // to reach the docshell, so ask it... + nsCOMPtr ret; + subDocFrame->GetDocShell(getter_AddRefs(ret)); + return ret.forget(); + } + + if (!mContent) { + return nullptr; + } + + // No nsSubDocumentFrame available for mContent, try if there's a mapping + // between mContent's document to mContent's subdocument. + + nsIDocument *doc = mContent->GetComposedDoc(); + + if (!doc) { + return nullptr; + } + + nsIDocument *sub_doc = doc->GetSubDocumentFor(mContent); + + if (!sub_doc) { + return nullptr; + } + + nsCOMPtr result = sub_doc->GetDocShell(); + return result.forget(); +} + +} // namespace dom +} // namespace mozilla + +nsresult +NS_NewContainerBoxObject(nsIBoxObject** aResult) +{ + NS_ADDREF(*aResult = new mozilla::dom::ContainerBoxObject()); + return NS_OK; +} diff --git a/layout/xul/ContainerBoxObject.h b/layout/xul/ContainerBoxObject.h new file mode 100644 index 000000000..b3da97991 --- /dev/null +++ b/layout/xul/ContainerBoxObject.h @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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/. */ + +#ifndef mozilla_dom_ContainerBoxObject_h +#define mozilla_dom_ContainerBoxObject_h + +#include "mozilla/dom/BoxObject.h" + +namespace mozilla { +namespace dom { + +class ContainerBoxObject final : public BoxObject +{ +public: + ContainerBoxObject(); + + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + + already_AddRefed GetDocShell(); + +private: + ~ContainerBoxObject(); +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_ContainerBoxObject_h diff --git a/layout/xul/ListBoxObject.cpp b/layout/xul/ListBoxObject.cpp new file mode 100644 index 000000000..435065f5b --- /dev/null +++ b/layout/xul/ListBoxObject.cpp @@ -0,0 +1,238 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "mozilla/dom/ListBoxObject.h" +#include "nsCOMPtr.h" +#include "nsIFrame.h" +#include "nsGkAtoms.h" +#include "nsIScrollableFrame.h" +#include "nsListBoxBodyFrame.h" +#include "ChildIterator.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/ListBoxObjectBinding.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_ISUPPORTS_INHERITED(ListBoxObject, BoxObject, nsIListBoxObject, + nsPIListBoxObject) + +ListBoxObject::ListBoxObject() + : mListBoxBody(nullptr) +{ +} + +ListBoxObject::~ListBoxObject() +{ +} + +JSObject* ListBoxObject::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return ListBoxObjectBinding::Wrap(aCx, this, aGivenProto); +} + +// nsIListBoxObject +NS_IMETHODIMP +ListBoxObject::GetRowCount(int32_t *aResult) +{ + *aResult = GetRowCount(); + return NS_OK; +} + +NS_IMETHODIMP +ListBoxObject::GetItemAtIndex(int32_t index, nsIDOMElement **_retval) +{ + nsListBoxBodyFrame* body = GetListBoxBody(true); + if (body) { + return body->GetItemAtIndex(index, _retval); + } + return NS_OK; + } + +NS_IMETHODIMP +ListBoxObject::GetIndexOfItem(nsIDOMElement* aElement, int32_t *aResult) +{ + *aResult = 0; + + nsListBoxBodyFrame* body = GetListBoxBody(true); + if (body) { + return body->GetIndexOfItem(aElement, aResult); + } + return NS_OK; +} + +// ListBoxObject + +int32_t +ListBoxObject::GetRowCount() +{ + nsListBoxBodyFrame* body = GetListBoxBody(true); + if (body) { + return body->GetRowCount(); + } + return 0; +} + +int32_t +ListBoxObject::GetNumberOfVisibleRows() +{ + nsListBoxBodyFrame* body = GetListBoxBody(true); + if (body) { + return body->GetNumberOfVisibleRows(); + } + return 0; +} + +int32_t +ListBoxObject::GetIndexOfFirstVisibleRow() +{ + nsListBoxBodyFrame* body = GetListBoxBody(true); + if (body) { + return body->GetIndexOfFirstVisibleRow(); + } + return 0; +} + +void +ListBoxObject::EnsureIndexIsVisible(int32_t aRowIndex) +{ + nsListBoxBodyFrame* body = GetListBoxBody(true); + if (body) { + body->EnsureIndexIsVisible(aRowIndex); + } +} + +void +ListBoxObject::ScrollToIndex(int32_t aRowIndex) +{ + nsListBoxBodyFrame* body = GetListBoxBody(true); + if (body) { + body->ScrollToIndex(aRowIndex); + } +} + +void +ListBoxObject::ScrollByLines(int32_t aNumLines) +{ + nsListBoxBodyFrame* body = GetListBoxBody(true); + if (body) { + body->ScrollByLines(aNumLines); + } +} + +already_AddRefed +ListBoxObject::GetItemAtIndex(int32_t index) +{ + nsCOMPtr el; + GetItemAtIndex(index, getter_AddRefs(el)); + nsCOMPtr ret(do_QueryInterface(el)); + return ret.forget(); +} + +int32_t +ListBoxObject::GetIndexOfItem(Element& aElement) +{ + int32_t ret; + nsCOMPtr el(do_QueryInterface(&aElement)); + GetIndexOfItem(el, &ret); + return ret; +} + +////////////////////// + +static nsIContent* +FindBodyContent(nsIContent* aParent) +{ + if (aParent->IsXULElement(nsGkAtoms::listboxbody)) { + return aParent; + } + + mozilla::dom::FlattenedChildIterator iter(aParent); + for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) { + nsIContent* result = FindBodyContent(child); + if (result) { + return result; + } + } + + return nullptr; +} + +nsListBoxBodyFrame* +ListBoxObject::GetListBoxBody(bool aFlush) +{ + if (mListBoxBody) { + return mListBoxBody; + } + + nsIPresShell* shell = GetPresShell(false); + if (!shell) { + return nullptr; + } + + nsIFrame* frame = aFlush ? + GetFrame(false) /* does Flush_Frames */ : + mContent->GetPrimaryFrame(); + if (!frame) { + return nullptr; + } + + // Iterate over our content model children looking for the body. + nsCOMPtr content = FindBodyContent(frame->GetContent()); + + if (!content) { + return nullptr; + } + + // this frame will be a nsGFXScrollFrame + frame = content->GetPrimaryFrame(); + if (!frame) { + return nullptr; + } + + nsIScrollableFrame* scrollFrame = do_QueryFrame(frame); + if (!scrollFrame) { + return nullptr; + } + + // this frame will be the one we want + nsIFrame* yeahBaby = scrollFrame->GetScrolledFrame(); + if (!yeahBaby) { + return nullptr; + } + + // It's a frame. Refcounts are irrelevant. + nsListBoxBodyFrame* listBoxBody = do_QueryFrame(yeahBaby); + NS_ENSURE_TRUE(listBoxBody && + listBoxBody->SetBoxObject(this), + nullptr); + mListBoxBody = listBoxBody; + return mListBoxBody; +} + +void +ListBoxObject::Clear() +{ + ClearCachedValues(); + BoxObject::Clear(); +} + +void +ListBoxObject::ClearCachedValues() +{ + mListBoxBody = nullptr; +} + +} // namespace dom +} // namespace mozilla + +// Creation Routine /////////////////////////////////////////////////////////////////////// + +nsresult +NS_NewListBoxObject(nsIBoxObject** aResult) +{ + NS_ADDREF(*aResult = new mozilla::dom::ListBoxObject()); + return NS_OK; +} diff --git a/layout/xul/ListBoxObject.h b/layout/xul/ListBoxObject.h new file mode 100644 index 000000000..39cdf5c8c --- /dev/null +++ b/layout/xul/ListBoxObject.h @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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/. */ + +#ifndef mozilla_dom_ListBoxObject_h +#define mozilla_dom_ListBoxObject_h + +#include "mozilla/dom/BoxObject.h" +#include "nsPIListBoxObject.h" + +namespace mozilla { +namespace dom { + +class ListBoxObject final : public BoxObject, + public nsPIListBoxObject +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSILISTBOXOBJECT + + ListBoxObject(); + + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + + // nsPIListBoxObject + virtual nsListBoxBodyFrame* GetListBoxBody(bool aFlush) override; + + // nsPIBoxObject + virtual void Clear() override; + virtual void ClearCachedValues() override; + + // ListBoxObject.webidl + int32_t GetRowCount(); + int32_t GetNumberOfVisibleRows(); + int32_t GetIndexOfFirstVisibleRow(); + void EnsureIndexIsVisible(int32_t rowIndex); + void ScrollToIndex(int32_t rowIndex); + void ScrollByLines(int32_t numLines); + already_AddRefed GetItemAtIndex(int32_t index); + int32_t GetIndexOfItem(Element& item); + +protected: + nsListBoxBodyFrame *mListBoxBody; + +private: + ~ListBoxObject(); +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_ListBoxObject_h diff --git a/layout/xul/MenuBoxObject.cpp b/layout/xul/MenuBoxObject.cpp new file mode 100644 index 000000000..ab105fd17 --- /dev/null +++ b/layout/xul/MenuBoxObject.cpp @@ -0,0 +1,151 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "mozilla/dom/MenuBoxObject.h" +#include "mozilla/dom/MenuBoxObjectBinding.h" + +#include "mozilla/dom/KeyboardEvent.h" +#include "mozilla/dom/Element.h" +#include "nsIDOMKeyEvent.h" +#include "nsIFrame.h" +#include "nsMenuBarFrame.h" +#include "nsMenuBarListener.h" +#include "nsMenuFrame.h" +#include "nsMenuPopupFrame.h" + +namespace mozilla { +namespace dom { + +MenuBoxObject::MenuBoxObject() +{ +} + +MenuBoxObject::~MenuBoxObject() +{ +} + +JSObject* MenuBoxObject::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return MenuBoxObjectBinding::Wrap(aCx, this, aGivenProto); +} + +void MenuBoxObject::OpenMenu(bool aOpenFlag) +{ + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) { + nsIFrame* frame = GetFrame(false); + if (frame) { + if (aOpenFlag) { + nsCOMPtr content = mContent; + pm->ShowMenu(content, false, false); + } + else { + nsMenuFrame* menu = do_QueryFrame(frame); + if (menu) { + nsMenuPopupFrame* popupFrame = menu->GetPopup(); + if (popupFrame) + pm->HidePopup(popupFrame->GetContent(), false, true, false, false); + } + } + } + } +} + +already_AddRefed +MenuBoxObject::GetActiveChild() +{ + nsMenuFrame* menu = do_QueryFrame(GetFrame(false)); + if (menu) { + nsCOMPtr el; + menu->GetActiveChild(getter_AddRefs(el)); + nsCOMPtr ret(do_QueryInterface(el)); + return ret.forget(); + } + return nullptr; +} + +void MenuBoxObject::SetActiveChild(Element* arg) +{ + nsMenuFrame* menu = do_QueryFrame(GetFrame(false)); + if (menu) { + nsCOMPtr el(do_QueryInterface(arg)); + menu->SetActiveChild(el); + } +} + +bool MenuBoxObject::HandleKeyPress(KeyboardEvent& keyEvent) +{ + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (!pm) { + return false; + } + + // if event has already been handled, bail + bool eventHandled = false; + keyEvent.GetDefaultPrevented(&eventHandled); + if (eventHandled) { + return false; + } + + if (nsMenuBarListener::IsAccessKeyPressed(&keyEvent)) + return false; + + nsMenuFrame* menu = do_QueryFrame(GetFrame(false)); + if (!menu) { + return false; + } + + nsMenuPopupFrame* popupFrame = menu->GetPopup(); + if (!popupFrame) { + return false; + } + + uint32_t keyCode = keyEvent.KeyCode(); + switch (keyCode) { + case nsIDOMKeyEvent::DOM_VK_UP: + case nsIDOMKeyEvent::DOM_VK_DOWN: + case nsIDOMKeyEvent::DOM_VK_HOME: + case nsIDOMKeyEvent::DOM_VK_END: + { + nsNavigationDirection theDirection; + theDirection = NS_DIRECTION_FROM_KEY_CODE(popupFrame, keyCode); + return pm->HandleKeyboardNavigationInPopup(popupFrame, theDirection); + } + default: + return pm->HandleShortcutNavigation(&keyEvent, popupFrame); + } +} + +bool MenuBoxObject::OpenedWithKey() +{ + nsMenuFrame* menuframe = do_QueryFrame(GetFrame(false)); + if (!menuframe) { + return false; + } + + nsIFrame* frame = menuframe->GetParent(); + while (frame) { + nsMenuBarFrame* menubar = do_QueryFrame(frame); + if (menubar) { + return menubar->IsActiveByKeyboard(); + } + frame = frame->GetParent(); + } + return false; +} + +} // namespace dom +} // namespace mozilla + +// Creation Routine /////////////////////////////////////////////////////////////////////// + +using namespace mozilla::dom; + +nsresult +NS_NewMenuBoxObject(nsIBoxObject** aResult) +{ + NS_ADDREF(*aResult = new MenuBoxObject()); + return NS_OK; +} diff --git a/layout/xul/MenuBoxObject.h b/layout/xul/MenuBoxObject.h new file mode 100644 index 000000000..652e3231f --- /dev/null +++ b/layout/xul/MenuBoxObject.h @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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/. */ + +#ifndef mozilla_dom_MenuBoxObject_h +#define mozilla_dom_MenuBoxObject_h + +#include "mozilla/dom/BoxObject.h" + +namespace mozilla { +namespace dom { + +class KeyboardEvent; + +class MenuBoxObject final : public BoxObject +{ +public: + + MenuBoxObject(); + + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + + void OpenMenu(bool aOpenFlag); + already_AddRefed GetActiveChild(); + void SetActiveChild(Element* arg); + bool HandleKeyPress(KeyboardEvent& keyEvent); + bool OpenedWithKey(); + +private: + ~MenuBoxObject(); +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_MenuBoxObject_h diff --git a/layout/xul/PopupBoxObject.cpp b/layout/xul/PopupBoxObject.cpp new file mode 100644 index 000000000..00ecc943d --- /dev/null +++ b/layout/xul/PopupBoxObject.cpp @@ -0,0 +1,384 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "nsCOMPtr.h" +#include "nsIRootBox.h" +#include "nsIPresShell.h" +#include "nsIContent.h" +#include "nsNameSpaceManager.h" +#include "nsGkAtoms.h" +#include "nsMenuPopupFrame.h" +#include "nsView.h" +#include "mozilla/AppUnits.h" +#include "mozilla/dom/DOMRect.h" +#include "mozilla/dom/PopupBoxObject.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/Event.h" +#include "mozilla/dom/PopupBoxObjectBinding.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_ADDREF_INHERITED(PopupBoxObject, BoxObject) +NS_IMPL_RELEASE_INHERITED(PopupBoxObject, BoxObject) +NS_INTERFACE_MAP_BEGIN(PopupBoxObject) +NS_INTERFACE_MAP_END_INHERITING(BoxObject) + +PopupBoxObject::PopupBoxObject() +{ +} + +PopupBoxObject::~PopupBoxObject() +{ +} + +nsIContent* PopupBoxObject::GetParentObject() const +{ + return BoxObject::GetParentObject(); +} + +JSObject* PopupBoxObject::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return PopupBoxObjectBinding::Wrap(aCx, this, aGivenProto); +} + +nsPopupSetFrame* +PopupBoxObject::GetPopupSetFrame() +{ + nsIRootBox* rootBox = nsIRootBox::GetRootBox(GetPresShell(false)); + if (!rootBox) + return nullptr; + + return rootBox->GetPopupSetFrame(); +} + +void +PopupBoxObject::HidePopup(bool aCancel) +{ + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm && mContent) { + pm->HidePopup(mContent, false, true, false, aCancel); + } +} + +void +PopupBoxObject::ShowPopup(Element* aAnchorElement, + Element& aPopupElement, + int32_t aXPos, int32_t aYPos, + const nsAString& aPopupType, + const nsAString& aAnchorAlignment, + const nsAString& aPopupAlignment) +{ + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm && mContent) { + nsCOMPtr anchorContent(do_QueryInterface(aAnchorElement)); + nsAutoString popupType(aPopupType); + nsAutoString anchor(aAnchorAlignment); + nsAutoString align(aPopupAlignment); + pm->ShowPopupWithAnchorAlign(mContent, anchorContent, anchor, align, + aXPos, aYPos, + popupType.EqualsLiteral("context")); + } +} + +void +PopupBoxObject::OpenPopup(Element* aAnchorElement, + const nsAString& aPosition, + int32_t aXPos, int32_t aYPos, + bool aIsContextMenu, + bool aAttributesOverride, + Event* aTriggerEvent) +{ + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm && mContent) { + nsCOMPtr anchorContent(do_QueryInterface(aAnchorElement)); + pm->ShowPopup(mContent, anchorContent, aPosition, aXPos, aYPos, + aIsContextMenu, aAttributesOverride, false, aTriggerEvent); + } +} + +void +PopupBoxObject::OpenPopupAtScreen(int32_t aXPos, int32_t aYPos, + bool aIsContextMenu, + Event* aTriggerEvent) +{ + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm && mContent) + pm->ShowPopupAtScreen(mContent, aXPos, aYPos, aIsContextMenu, aTriggerEvent); +} + +void +PopupBoxObject::OpenPopupAtScreenRect(const nsAString& aPosition, + int32_t aXPos, int32_t aYPos, + int32_t aWidth, int32_t aHeight, + bool aIsContextMenu, + bool aAttributesOverride, + Event* aTriggerEvent) +{ + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm && mContent) { + pm->ShowPopupAtScreenRect(mContent, aPosition, + nsIntRect(aXPos, aYPos, aWidth, aHeight), + aIsContextMenu, aAttributesOverride, aTriggerEvent); + } +} + +void +PopupBoxObject::MoveTo(int32_t aLeft, int32_t aTop) +{ + nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr; + if (menuPopupFrame) { + menuPopupFrame->MoveTo(CSSIntPoint(aLeft, aTop), true); + } +} + +void +PopupBoxObject::MoveToAnchor(Element* aAnchorElement, + const nsAString& aPosition, + int32_t aXPos, int32_t aYPos, + bool aAttributesOverride) +{ + if (mContent) { + nsCOMPtr anchorContent(do_QueryInterface(aAnchorElement)); + + nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(mContent->GetPrimaryFrame()); + if (menuPopupFrame && menuPopupFrame->IsVisible()) { + menuPopupFrame->MoveToAnchor(anchorContent, aPosition, aXPos, aYPos, aAttributesOverride); + } + } +} + +void +PopupBoxObject::SizeTo(int32_t aWidth, int32_t aHeight) +{ + if (!mContent) + return; + + nsAutoString width, height; + width.AppendInt(aWidth); + height.AppendInt(aHeight); + + nsCOMPtr content = mContent; + + // We only want to pass aNotify=true to SetAttr once, but must make sure + // we pass it when a value is being changed. Thus, we check if the height + // is the same and if so, pass true when setting the width. + bool heightSame = content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::height, height, eCaseMatters); + + content->SetAttr(kNameSpaceID_None, nsGkAtoms::width, width, heightSame); + content->SetAttr(kNameSpaceID_None, nsGkAtoms::height, height, true); +} + +bool +PopupBoxObject::AutoPosition() +{ + nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr; + if (menuPopupFrame) { + return menuPopupFrame->GetAutoPosition(); + } + return true; +} + +void +PopupBoxObject::SetAutoPosition(bool aShouldAutoPosition) +{ + nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr; + if (menuPopupFrame) { + menuPopupFrame->SetAutoPosition(aShouldAutoPosition); + } +} + +void +PopupBoxObject::EnableRollup(bool aShouldRollup) +{ + // this does nothing now +} + +void +PopupBoxObject::SetConsumeRollupEvent(uint32_t aConsume) +{ + nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(false)); + if (menuPopupFrame) { + menuPopupFrame->SetConsumeRollupEvent(aConsume); + } +} + +void +PopupBoxObject::EnableKeyboardNavigator(bool aEnableKeyboardNavigator) +{ + if (!mContent) + return; + + // Use ignorekeys="true" on the popup instead of using this function. + if (aEnableKeyboardNavigator) + mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::ignorekeys, true); + else + mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::ignorekeys, + NS_LITERAL_STRING("true"), true); +} + +void +PopupBoxObject::GetPopupState(nsString& aState) +{ + // set this here in case there's no frame for the popup + aState.AssignLiteral("closed"); + + nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr; + if (menuPopupFrame) { + switch (menuPopupFrame->PopupState()) { + case ePopupShown: + aState.AssignLiteral("open"); + break; + case ePopupShowing: + case ePopupPositioning: + case ePopupOpening: + case ePopupVisible: + aState.AssignLiteral("showing"); + break; + case ePopupHiding: + case ePopupInvisible: + aState.AssignLiteral("hiding"); + break; + case ePopupClosed: + break; + default: + NS_NOTREACHED("Bad popup state"); + break; + } + } +} + +nsINode* +PopupBoxObject::GetTriggerNode() const +{ + nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr; + return nsMenuPopupFrame::GetTriggerContent(menuPopupFrame); +} + +Element* +PopupBoxObject::GetAnchorNode() const +{ + nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr; + if (!menuPopupFrame) { + return nullptr; + } + + nsIContent* anchor = menuPopupFrame->GetAnchor(); + return anchor && anchor->IsElement() ? anchor->AsElement() : nullptr; +} + +already_AddRefed +PopupBoxObject::GetOuterScreenRect() +{ + RefPtr rect = new DOMRect(mContent); + + // Return an empty rectangle if the popup is not open. + nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(false)); + if (!menuPopupFrame || !menuPopupFrame->IsOpen()) { + return rect.forget(); + } + + nsView* view = menuPopupFrame->GetView(); + if (view) { + nsIWidget* widget = view->GetWidget(); + if (widget) { + LayoutDeviceIntRect screenRect = widget->GetScreenBounds(); + + int32_t pp = menuPopupFrame->PresContext()->AppUnitsPerDevPixel(); + rect->SetLayoutRect(LayoutDeviceIntRect::ToAppUnits(screenRect, pp)); + } + } + return rect.forget(); +} + +void +PopupBoxObject::GetAlignmentPosition(nsString& positionStr) +{ + positionStr.Truncate(); + + // This needs to flush layout. + nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(true)); + if (!menuPopupFrame) + return; + + int8_t position = menuPopupFrame->GetAlignmentPosition(); + switch (position) { + case POPUPPOSITION_AFTERSTART: + positionStr.AssignLiteral("after_start"); + break; + case POPUPPOSITION_AFTEREND: + positionStr.AssignLiteral("after_end"); + break; + case POPUPPOSITION_BEFORESTART: + positionStr.AssignLiteral("before_start"); + break; + case POPUPPOSITION_BEFOREEND: + positionStr.AssignLiteral("before_end"); + break; + case POPUPPOSITION_STARTBEFORE: + positionStr.AssignLiteral("start_before"); + break; + case POPUPPOSITION_ENDBEFORE: + positionStr.AssignLiteral("end_before"); + break; + case POPUPPOSITION_STARTAFTER: + positionStr.AssignLiteral("start_after"); + break; + case POPUPPOSITION_ENDAFTER: + positionStr.AssignLiteral("end_after"); + break; + case POPUPPOSITION_OVERLAP: + positionStr.AssignLiteral("overlap"); + break; + case POPUPPOSITION_AFTERPOINTER: + positionStr.AssignLiteral("after_pointer"); + break; + case POPUPPOSITION_SELECTION: + positionStr.AssignLiteral("selection"); + break; + default: + // Leave as an empty string. + break; + } +} + +int32_t +PopupBoxObject::AlignmentOffset() +{ + nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(false)); + if (!menuPopupFrame) + return 0; + + int32_t pp = mozilla::AppUnitsPerCSSPixel(); + // Note that the offset might be along either the X or Y axis, but for the + // sake of simplicity we use a point with only the X axis set so we can + // use ToNearestPixels(). + nsPoint appOffset(menuPopupFrame->GetAlignmentOffset(), 0); + nsIntPoint popupOffset = appOffset.ToNearestPixels(pp); + return popupOffset.x; +} + +void +PopupBoxObject::SetConstraintRect(dom::DOMRectReadOnly& aRect) +{ + nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(false)); + if (menuPopupFrame) { + menuPopupFrame->SetOverrideConstraintRect( + LayoutDeviceIntRect::Truncate(aRect.Left(), aRect.Top(), aRect.Width(), aRect.Height())); + } +} + +} // namespace dom +} // namespace mozilla + +// Creation Routine /////////////////////////////////////////////////////////////////////// + +nsresult +NS_NewPopupBoxObject(nsIBoxObject** aResult) +{ + *aResult = new mozilla::dom::PopupBoxObject(); + NS_ADDREF(*aResult); + return NS_OK; +} diff --git a/layout/xul/PopupBoxObject.h b/layout/xul/PopupBoxObject.h new file mode 100644 index 000000000..a660393b5 --- /dev/null +++ b/layout/xul/PopupBoxObject.h @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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/. */ + +#ifndef mozilla_dom_PopupBoxObject_h +#define mozilla_dom_PopupBoxObject_h + +#include "mozilla/Attributes.h" +#include "mozilla/ErrorResult.h" +#include "nsCycleCollectionParticipant.h" +#include "nsWrapperCache.h" +#include "mozilla/dom/BoxObject.h" +#include "nsString.h" + +struct JSContext; +class nsPopupSetFrame; + +namespace mozilla { +namespace dom { + +class DOMRect; +class Element; +class Event; + +class PopupBoxObject final : public BoxObject +{ +public: + NS_DECL_ISUPPORTS_INHERITED + + // also in PopupBoxObject.webidl + static const uint32_t ROLLUP_DEFAULT = 0; /* widget/platform default */ + static const uint32_t ROLLUP_CONSUME = 1; /* consume the rollup event */ + static const uint32_t ROLLUP_NO_CONSUME = 2; /* don't consume the rollup event */ + + PopupBoxObject(); + + nsIContent* GetParentObject() const; + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + + void ShowPopup(Element* aAnchorElement, + Element& aPopupElement, + int32_t aXPos, + int32_t aYPos, + const nsAString& aPopupType, + const nsAString& aAnchorAlignment, + const nsAString& aPopupAlignment); + + void HidePopup(bool aCancel); + + bool AutoPosition(); + + void SetAutoPosition(bool aShouldAutoPosition); + + void EnableKeyboardNavigator(bool aEnableKeyboardNavigator); + + void EnableRollup(bool aShouldRollup); + + void SetConsumeRollupEvent(uint32_t aConsume); + + void SizeTo(int32_t aWidth, int32_t aHeight); + + void MoveTo(int32_t aLeft, int32_t aTop); + + void OpenPopup(Element* aAnchorElement, + const nsAString& aPosition, + int32_t aXPos, + int32_t aYPos, + bool aIsContextMenu, bool aAttributesOverride, + Event* aTriggerEvent); + + void OpenPopupAtScreen(int32_t aXPos, + int32_t aYPos, + bool aIsContextMenu, + Event* aTriggerEvent); + + void OpenPopupAtScreenRect(const nsAString& aPosition, + int32_t aXPos, int32_t aYPos, + int32_t aWidth, int32_t aHeight, + bool aIsContextMenu, + bool aAttributesOverride, + Event* aTriggerEvent); + + void GetPopupState(nsString& aState); + + nsINode* GetTriggerNode() const; + + Element* GetAnchorNode() const; + + already_AddRefed GetOuterScreenRect(); + + void MoveToAnchor(Element* aAnchorElement, + const nsAString& aPosition, + int32_t aXPos, + int32_t aYPos, + bool aAttributesOverride); + + void GetAlignmentPosition(nsString& positionStr); + + int32_t AlignmentOffset(); + + void SetConstraintRect(dom::DOMRectReadOnly& aRect); + +private: + ~PopupBoxObject(); + +protected: + nsPopupSetFrame* GetPopupSetFrame(); +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_PopupBoxObject_h diff --git a/layout/xul/ScrollBoxObject.cpp b/layout/xul/ScrollBoxObject.cpp new file mode 100644 index 000000000..26f7bc9bb --- /dev/null +++ b/layout/xul/ScrollBoxObject.cpp @@ -0,0 +1,384 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "mozilla/dom/ScrollBoxObject.h" +#include "mozilla/dom/ScrollBoxObjectBinding.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/ToJSValue.h" +#include "nsCOMPtr.h" +#include "nsIPresShell.h" +#include "nsIContent.h" +#include "nsIDOMElement.h" +#include "nsPresContext.h" +#include "nsBox.h" +#include "nsIScrollableFrame.h" + +namespace mozilla { +namespace dom { + +ScrollBoxObject::ScrollBoxObject() +{ +} + +ScrollBoxObject::~ScrollBoxObject() +{ +} + +JSObject* ScrollBoxObject::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return ScrollBoxObjectBinding::Wrap(aCx, this, aGivenProto); +} + +nsIScrollableFrame* ScrollBoxObject::GetScrollFrame() +{ + return do_QueryFrame(GetFrame(false)); +} + +void ScrollBoxObject::ScrollTo(int32_t x, int32_t y, ErrorResult& aRv) +{ + nsIScrollableFrame* sf = GetScrollFrame(); + if (!sf) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + sf->ScrollToCSSPixels(CSSIntPoint(x, y)); +} + +void ScrollBoxObject::ScrollBy(int32_t dx, int32_t dy, ErrorResult& aRv) +{ + CSSIntPoint pt; + GetPosition(pt, aRv); + + if (aRv.Failed()) { + return; + } + + ScrollTo(pt.x + dx, pt.y + dy, aRv); +} + +void ScrollBoxObject::ScrollByLine(int32_t dlines, ErrorResult& aRv) +{ + nsIScrollableFrame* sf = GetScrollFrame(); + if (!sf) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + sf->ScrollBy(nsIntPoint(0, dlines), nsIScrollableFrame::LINES, + nsIScrollableFrame::SMOOTH); +} + +// XUL elements have a single box child element. +// Get a pointer to that box. +// Note that now that the is just a regular box +// with 'overflow:hidden', the boxobject's frame is an nsXULScrollFrame, +// the 's box frame is the scrollframe's "scrolled frame", and +// the 's child box is a child of that. +static nsIFrame* GetScrolledBox(BoxObject* aScrollBox) { + nsIFrame* frame = aScrollBox->GetFrame(false); + if (!frame) { + return nullptr; + } + + nsIScrollableFrame* scrollFrame = do_QueryFrame(frame); + if (!scrollFrame) { + NS_WARNING("ScrollBoxObject attached to something that's not a scroll frame!"); + return nullptr; + } + + nsIFrame* scrolledFrame = scrollFrame->GetScrolledFrame(); + if (!scrolledFrame) + return nullptr; + return nsBox::GetChildXULBox(scrolledFrame); +} + +void ScrollBoxObject::ScrollByIndex(int32_t dindexes, ErrorResult& aRv) +{ + nsIScrollableFrame* sf = GetScrollFrame(); + if (!sf) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + nsIFrame* scrolledBox = GetScrolledBox(this); + if (!scrolledBox) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + nsRect rect; + + // now get the scrolled boxes first child. + nsIFrame* child = nsBox::GetChildXULBox(scrolledBox); + + bool horiz = scrolledBox->IsXULHorizontal(); + nsPoint cp = sf->GetScrollPosition(); + nscoord diff = 0; + int32_t curIndex = 0; + bool isLTR = scrolledBox->IsXULNormalDirection(); + + int32_t frameWidth = 0; + if (!isLTR && horiz) { + GetWidth(&frameWidth); + nsCOMPtr shell = GetPresShell(false); + if (!shell) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return; + } + frameWidth = nsPresContext::CSSPixelsToAppUnits(frameWidth); + } + + // first find out what index we are currently at + while(child) { + rect = child->GetRect(); + if (horiz) { + // In the left-to-right case we break from the loop when the center of + // the current child rect is greater than the scrolled position of + // the left edge of the scrollbox + // In the right-to-left case we break when the center of the current + // child rect is less than the scrolled position of the right edge of + // the scrollbox. + diff = rect.x + rect.width/2; // use the center, to avoid rounding errors + if ((isLTR && diff > cp.x) || + (!isLTR && diff < cp.x + frameWidth)) { + break; + } + } else { + diff = rect.y + rect.height/2;// use the center, to avoid rounding errors + if (diff > cp.y) { + break; + } + } + child = nsBox::GetNextXULBox(child); + curIndex++; + } + + int32_t count = 0; + + if (dindexes == 0) + return; + + if (dindexes > 0) { + while(child) { + child = nsBox::GetNextXULBox(child); + if (child) { + rect = child->GetRect(); + } + count++; + if (count >= dindexes) { + break; + } + } + + } else if (dindexes < 0) { + child = nsBox::GetChildXULBox(scrolledBox); + while(child) { + rect = child->GetRect(); + if (count >= curIndex + dindexes) { + break; + } + + count++; + child = nsBox::GetNextXULBox(child); + + } + } + + nscoord csspixel = nsPresContext::CSSPixelsToAppUnits(1); + if (horiz) { + // In the left-to-right case we scroll so that the left edge of the + // selected child is scrolled to the left edge of the scrollbox. + // In the right-to-left case we scroll so that the right edge of the + // selected child is scrolled to the right edge of the scrollbox. + + nsPoint pt(isLTR ? rect.x : rect.x + rect.width - frameWidth, + cp.y); + + // Use a destination range that ensures the left edge (or right edge, + // for RTL) will indeed be visible. Also ensure that the top edge + // is visible. + nsRect range(pt.x, pt.y, csspixel, 0); + if (isLTR) { + range.x -= csspixel; + } + sf->ScrollTo(pt, nsIScrollableFrame::INSTANT, &range); + } else { + // Use a destination range that ensures the top edge will be visible. + nsRect range(cp.x, rect.y - csspixel, 0, csspixel); + sf->ScrollTo(nsPoint(cp.x, rect.y), nsIScrollableFrame::INSTANT, &range); + } +} + +void ScrollBoxObject::ScrollToLine(int32_t line, ErrorResult& aRv) +{ + nsIScrollableFrame* sf = GetScrollFrame(); + if (!sf) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + nscoord y = sf->GetLineScrollAmount().height * line; + nsRect range(0, y - nsPresContext::CSSPixelsToAppUnits(1), + 0, nsPresContext::CSSPixelsToAppUnits(1)); + sf->ScrollTo(nsPoint(0, y), nsIScrollableFrame::INSTANT, &range); +} + +void ScrollBoxObject::ScrollToElement(Element& child, ErrorResult& aRv) +{ + nsCOMPtr shell = GetPresShell(false); + if (!shell) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return; + } + + shell->ScrollContentIntoView(&child, + nsIPresShell::ScrollAxis( + nsIPresShell::SCROLL_TOP, + nsIPresShell::SCROLL_ALWAYS), + nsIPresShell::ScrollAxis( + nsIPresShell::SCROLL_LEFT, + nsIPresShell::SCROLL_ALWAYS), + nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY | + nsIPresShell::SCROLL_OVERFLOW_HIDDEN); +} + +void ScrollBoxObject::ScrollToIndex(int32_t index, ErrorResult& aRv) +{ + aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); +} + +int32_t ScrollBoxObject::GetPositionX(ErrorResult& aRv) +{ + CSSIntPoint pt; + GetPosition(pt, aRv); + return pt.x; +} + +int32_t ScrollBoxObject::GetPositionY(ErrorResult& aRv) +{ + CSSIntPoint pt; + GetPosition(pt, aRv); + return pt.y; +} + +int32_t ScrollBoxObject::GetScrolledWidth(ErrorResult& aRv) +{ + nsRect scrollRect; + GetScrolledSize(scrollRect, aRv); + return scrollRect.width; +} + +int32_t ScrollBoxObject::GetScrolledHeight(ErrorResult& aRv) +{ + nsRect scrollRect; + GetScrolledSize(scrollRect, aRv); + return scrollRect.height; +} + +/* private helper */ +void ScrollBoxObject::GetPosition(CSSIntPoint& aPos, ErrorResult& aRv) +{ + nsIScrollableFrame* sf = GetScrollFrame(); + if (!sf) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + aPos = sf->GetScrollPositionCSSPixels(); +} + +/* private helper */ +void ScrollBoxObject::GetScrolledSize(nsRect& aRect, ErrorResult& aRv) +{ + nsIFrame* scrolledBox = GetScrolledBox(this); + if (!scrolledBox) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + aRect = scrolledBox->GetRect(); + aRect.width = nsPresContext::AppUnitsToIntCSSPixels(aRect.width); + aRect.height = nsPresContext::AppUnitsToIntCSSPixels(aRect.height); +} + +void ScrollBoxObject::GetPosition(JSContext* cx, + JS::Handle x, + JS::Handle y, + ErrorResult& aRv) +{ + CSSIntPoint pt; + GetPosition(pt, aRv); + JS::Rooted v(cx); + if (!ToJSValue(cx, pt.x, &v) || + !JS_SetProperty(cx, x, "value", v)) { + aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL); + return; + } + if (!ToJSValue(cx, pt.y, &v) || + !JS_SetProperty(cx, y, "value", v)) { + aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL); + return; + } +} + +void ScrollBoxObject::GetScrolledSize(JSContext* cx, + JS::Handle width, + JS::Handle height, + ErrorResult& aRv) +{ + nsRect rect; + GetScrolledSize(rect, aRv); + JS::Rooted v(cx); + if (!ToJSValue(cx, rect.width, &v) || + !JS_SetProperty(cx, width, "value", v)) { + aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL); + return; + } + if (!ToJSValue(cx, rect.height, &v) || + !JS_SetProperty(cx, height, "value", v)) { + aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL); + return; + } +} + +void ScrollBoxObject::EnsureElementIsVisible(Element& child, ErrorResult& aRv) +{ + nsCOMPtr shell = GetPresShell(false); + if (!shell) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return; + } + + shell->ScrollContentIntoView(&child, + nsIPresShell::ScrollAxis(), + nsIPresShell::ScrollAxis(), + nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY | + nsIPresShell::SCROLL_OVERFLOW_HIDDEN); +} + +void ScrollBoxObject::EnsureIndexIsVisible(int32_t index, ErrorResult& aRv) +{ + aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); +} + +void ScrollBoxObject::EnsureLineIsVisible(int32_t line, ErrorResult& aRv) +{ + aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); +} + +} // namespace dom +} // namespace mozilla + +// Creation Routine /////////////////////////////////////////////////////////////////////// + +using namespace mozilla::dom; + +nsresult +NS_NewScrollBoxObject(nsIBoxObject** aResult) +{ + NS_ADDREF(*aResult = new ScrollBoxObject()); + return NS_OK; +} diff --git a/layout/xul/ScrollBoxObject.h b/layout/xul/ScrollBoxObject.h new file mode 100644 index 000000000..3344bf3f0 --- /dev/null +++ b/layout/xul/ScrollBoxObject.h @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#ifndef mozilla_dom_ScrollBoxObject_h +#define mozilla_dom_ScrollBoxObject_h + +#include "mozilla/dom/BoxObject.h" +#include "Units.h" + +class nsIScrollableFrame; +struct nsRect; + +namespace mozilla { +namespace dom { + +class ScrollBoxObject final : public BoxObject +{ +public: + ScrollBoxObject(); + + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + + virtual nsIScrollableFrame* GetScrollFrame(); + + void ScrollTo(int32_t x, int32_t y, ErrorResult& aRv); + void ScrollBy(int32_t dx, int32_t dy, ErrorResult& aRv); + void ScrollByLine(int32_t dlines, ErrorResult& aRv); + void ScrollByIndex(int32_t dindexes, ErrorResult& aRv); + void ScrollToLine(int32_t line, ErrorResult& aRv); + void ScrollToElement(Element& child, ErrorResult& aRv); + void ScrollToIndex(int32_t index, ErrorResult& aRv); + int32_t GetPositionX(ErrorResult& aRv); + int32_t GetPositionY(ErrorResult& aRv); + int32_t GetScrolledWidth(ErrorResult& aRv); + int32_t GetScrolledHeight(ErrorResult& aRv); + void EnsureElementIsVisible(Element& child, ErrorResult& aRv); + void EnsureIndexIsVisible(int32_t index, ErrorResult& aRv); + void EnsureLineIsVisible(int32_t line, ErrorResult& aRv); + + // Deprecated APIs from old IDL + void GetPosition(JSContext* cx, + JS::Handle x, + JS::Handle y, + ErrorResult& aRv); + + void GetScrolledSize(JSContext* cx, + JS::Handle width, + JS::Handle height, + ErrorResult& aRv); + +protected: + void GetScrolledSize(nsRect& aRect, ErrorResult& aRv); + void GetPosition(CSSIntPoint& aPos, ErrorResult& aRv); + +private: + ~ScrollBoxObject(); +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_ScrollBoxObject_h diff --git a/layout/xul/crashtests/131008-1.xul b/layout/xul/crashtests/131008-1.xul new file mode 100644 index 000000000..d505f8696 --- /dev/null +++ b/layout/xul/crashtests/131008-1.xul @@ -0,0 +1,11 @@ + + + +
abc
+ + +
\ No newline at end of file diff --git a/layout/xul/crashtests/137216-1.xul b/layout/xul/crashtests/137216-1.xul new file mode 100644 index 000000000..a3fc043c8 --- /dev/null +++ b/layout/xul/crashtests/137216-1.xul @@ -0,0 +1,4 @@ + + + + + diff --git a/layout/xul/crashtests/326879-1.xul b/layout/xul/crashtests/326879-1.xul new file mode 100644 index 000000000..84d74c30c --- /dev/null +++ b/layout/xul/crashtests/326879-1.xul @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + diff --git a/layout/xul/crashtests/327776-1.xul b/layout/xul/crashtests/327776-1.xul new file mode 100644 index 000000000..af889493c --- /dev/null +++ b/layout/xul/crashtests/327776-1.xul @@ -0,0 +1,24 @@ + + + + + + + + + + + diff --git a/layout/xul/crashtests/328135-1.xul b/layout/xul/crashtests/328135-1.xul new file mode 100644 index 000000000..77a467909 --- /dev/null +++ b/layout/xul/crashtests/328135-1.xul @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + diff --git a/layout/xul/crashtests/329327-1.xul b/layout/xul/crashtests/329327-1.xul new file mode 100644 index 000000000..fcfed07c4 --- /dev/null +++ b/layout/xul/crashtests/329327-1.xul @@ -0,0 +1,2 @@ + + diff --git a/layout/xul/crashtests/329407-1.xml b/layout/xul/crashtests/329407-1.xml new file mode 100644 index 000000000..0d41c0185 --- /dev/null +++ b/layout/xul/crashtests/329407-1.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/layout/xul/crashtests/329477-1.xhtml b/layout/xul/crashtests/329477-1.xhtml new file mode 100644 index 000000000..fcbd3da87 --- /dev/null +++ b/layout/xul/crashtests/329477-1.xhtml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + diff --git a/layout/xul/crashtests/336962-1.xul b/layout/xul/crashtests/336962-1.xul new file mode 100644 index 000000000..5ad4ad22b --- /dev/null +++ b/layout/xul/crashtests/336962-1.xul @@ -0,0 +1,17 @@ + + + + + + + + + diff --git a/layout/xul/crashtests/344228-1.xul b/layout/xul/crashtests/344228-1.xul new file mode 100644 index 000000000..d6015707b --- /dev/null +++ b/layout/xul/crashtests/344228-1.xul @@ -0,0 +1,27 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/layout/xul/crashtests/346083-1.xul b/layout/xul/crashtests/346083-1.xul new file mode 100644 index 000000000..e04d610a4 --- /dev/null +++ b/layout/xul/crashtests/346083-1.xul @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/layout/xul/crashtests/346281-1.xul b/layout/xul/crashtests/346281-1.xul new file mode 100644 index 000000000..4ef670155 --- /dev/null +++ b/layout/xul/crashtests/346281-1.xul @@ -0,0 +1,17 @@ + + + + + + + + + diff --git a/layout/xul/crashtests/350460.xul b/layout/xul/crashtests/350460.xul new file mode 100644 index 000000000..b13de6c97 --- /dev/null +++ b/layout/xul/crashtests/350460.xul @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/layout/xul/crashtests/360642-1.xul b/layout/xul/crashtests/360642-1.xul new file mode 100644 index 000000000..5e37020a5 --- /dev/null +++ b/layout/xul/crashtests/360642-1.xul @@ -0,0 +1,9 @@ + + + + + + + diff --git a/layout/xul/crashtests/365151.xul b/layout/xul/crashtests/365151.xul new file mode 100644 index 000000000..074c8d398 --- /dev/null +++ b/layout/xul/crashtests/365151.xul @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/xul/crashtests/366112-1.xul b/layout/xul/crashtests/366112-1.xul new file mode 100644 index 000000000..4a03ea2cf --- /dev/null +++ b/layout/xul/crashtests/366112-1.xul @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/layout/xul/crashtests/366203-1.xul b/layout/xul/crashtests/366203-1.xul new file mode 100644 index 000000000..3e2b96d30 --- /dev/null +++ b/layout/xul/crashtests/366203-1.xul @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/xul/crashtests/367185-1.xhtml b/layout/xul/crashtests/367185-1.xhtml new file mode 100644 index 000000000..08fd39fa1 --- /dev/null +++ b/layout/xul/crashtests/367185-1.xhtml @@ -0,0 +1,11 @@ + + + + +Testcase bug - ASSERTION: shouldn't use unconstrained widths anymore with nested marquees + + + + + diff --git a/layout/xul/crashtests/369942-1.xhtml b/layout/xul/crashtests/369942-1.xhtml new file mode 100644 index 000000000..a05705843 --- /dev/null +++ b/layout/xul/crashtests/369942-1.xhtml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + +
Industries
+ + + + + diff --git a/layout/xul/crashtests/374102-1.xul b/layout/xul/crashtests/374102-1.xul new file mode 100644 index 000000000..7e85f0d21 --- /dev/null +++ b/layout/xul/crashtests/374102-1.xul @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/layout/xul/crashtests/376137-1.html b/layout/xul/crashtests/376137-1.html new file mode 100644 index 000000000..23b39d900 --- /dev/null +++ b/layout/xul/crashtests/376137-1.html @@ -0,0 +1,18 @@ + + + + + + + +
+
+ M + N +
+
+ + + diff --git a/layout/xul/crashtests/376137-2.html b/layout/xul/crashtests/376137-2.html new file mode 100644 index 000000000..160c61ed3 --- /dev/null +++ b/layout/xul/crashtests/376137-2.html @@ -0,0 +1,11 @@ + +Bug 376137 + + +
+

M

+

N

+
+ diff --git a/layout/xul/crashtests/377592-1.svg b/layout/xul/crashtests/377592-1.svg new file mode 100644 index 000000000..7371708f2 --- /dev/null +++ b/layout/xul/crashtests/377592-1.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + diff --git a/layout/xul/crashtests/378961.html b/layout/xul/crashtests/378961.html new file mode 100644 index 000000000..b4da857ff --- /dev/null +++ b/layout/xul/crashtests/378961.html @@ -0,0 +1,9 @@ + + +Testcase bug 378961 - Crash [@ nsSplitterFrameInner::RemoveListener] when dragging splitter and DOMAttrModified event removing window + + + + + + diff --git a/layout/xul/crashtests/381862.html b/layout/xul/crashtests/381862.html new file mode 100644 index 000000000..e26fa357e --- /dev/null +++ b/layout/xul/crashtests/381862.html @@ -0,0 +1,23 @@ + +Testcase bug - Crash [@ nsBoxFrame::BuildDisplayListForChildren] with tree stuff in iframe toggling display + + + + + + + diff --git a/layout/xul/crashtests/382746-1.xul b/layout/xul/crashtests/382746-1.xul new file mode 100644 index 000000000..9bb14f24f --- /dev/null +++ b/layout/xul/crashtests/382746-1.xul @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/layout/xul/crashtests/382899-1.xul b/layout/xul/crashtests/382899-1.xul new file mode 100644 index 000000000..7dab931f7 --- /dev/null +++ b/layout/xul/crashtests/382899-1.xul @@ -0,0 +1,9 @@ + + + + + + +x + + diff --git a/layout/xul/crashtests/383236-1.xul b/layout/xul/crashtests/383236-1.xul new file mode 100644 index 000000000..244df65f1 --- /dev/null +++ b/layout/xul/crashtests/383236-1.xul @@ -0,0 +1,5 @@ + + +x + + \ No newline at end of file diff --git a/layout/xul/crashtests/384037-1.xhtml b/layout/xul/crashtests/384037-1.xhtml new file mode 100644 index 000000000..04bac671c --- /dev/null +++ b/layout/xul/crashtests/384037-1.xhtml @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/layout/xul/crashtests/384105-1-inner.xul b/layout/xul/crashtests/384105-1-inner.xul new file mode 100644 index 000000000..4ea6e0391 --- /dev/null +++ b/layout/xul/crashtests/384105-1-inner.xul @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/layout/xul/crashtests/384105-1.html b/layout/xul/crashtests/384105-1.html new file mode 100644 index 000000000..fe468a906 --- /dev/null +++ b/layout/xul/crashtests/384105-1.html @@ -0,0 +1,9 @@ + + + + + + + diff --git a/layout/xul/crashtests/384373-1.xul b/layout/xul/crashtests/384373-1.xul new file mode 100644 index 000000000..603b53cde --- /dev/null +++ b/layout/xul/crashtests/384373-1.xul @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/layout/xul/crashtests/384373-2.xul b/layout/xul/crashtests/384373-2.xul new file mode 100644 index 000000000..1d56394e3 --- /dev/null +++ b/layout/xul/crashtests/384373-2.xul @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/layout/xul/crashtests/384373.html b/layout/xul/crashtests/384373.html new file mode 100644 index 000000000..c3bc92f16 --- /dev/null +++ b/layout/xul/crashtests/384373.html @@ -0,0 +1,23 @@ + + + + Testcase for bug 384373 + + + + + + + + + diff --git a/layout/xul/crashtests/384491-1.xhtml b/layout/xul/crashtests/384491-1.xhtml new file mode 100644 index 000000000..2eb065f8d --- /dev/null +++ b/layout/xul/crashtests/384491-1.xhtml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/layout/xul/crashtests/384871-1-inner.xul b/layout/xul/crashtests/384871-1-inner.xul new file mode 100644 index 000000000..62efdb260 --- /dev/null +++ b/layout/xul/crashtests/384871-1-inner.xul @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/layout/xul/crashtests/384871-1.html b/layout/xul/crashtests/384871-1.html new file mode 100644 index 000000000..6bb2a9e07 --- /dev/null +++ b/layout/xul/crashtests/384871-1.html @@ -0,0 +1,9 @@ + + + + + + + diff --git a/layout/xul/crashtests/386642.xul b/layout/xul/crashtests/386642.xul new file mode 100644 index 000000000..50db21a09 --- /dev/null +++ b/layout/xul/crashtests/386642.xul @@ -0,0 +1,31 @@ + + + + + + + + diff --git a/layout/xul/crashtests/387033-1.xhtml b/layout/xul/crashtests/387033-1.xhtml new file mode 100644 index 000000000..58325b1a7 --- /dev/null +++ b/layout/xul/crashtests/387033-1.xhtml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/xul/crashtests/387080-1.xul b/layout/xul/crashtests/387080-1.xul new file mode 100644 index 000000000..4eb9bd784 --- /dev/null +++ b/layout/xul/crashtests/387080-1.xul @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/layout/xul/crashtests/391974-1-inner.xul b/layout/xul/crashtests/391974-1-inner.xul new file mode 100644 index 000000000..f13aa2110 --- /dev/null +++ b/layout/xul/crashtests/391974-1-inner.xul @@ -0,0 +1,19 @@ + + + + + + + + \ No newline at end of file diff --git a/layout/xul/crashtests/391974-1.html b/layout/xul/crashtests/391974-1.html new file mode 100644 index 000000000..c72a1a73c --- /dev/null +++ b/layout/xul/crashtests/391974-1.html @@ -0,0 +1,9 @@ + + + + + + + diff --git a/layout/xul/crashtests/394120-1.xhtml b/layout/xul/crashtests/394120-1.xhtml new file mode 100644 index 000000000..9df447862 --- /dev/null +++ b/layout/xul/crashtests/394120-1.xhtml @@ -0,0 +1,19 @@ + + + + + + + + + + diff --git a/layout/xul/crashtests/397293.xhtml b/layout/xul/crashtests/397293.xhtml new file mode 100644 index 000000000..cfd181921 --- /dev/null +++ b/layout/xul/crashtests/397293.xhtml @@ -0,0 +1,21 @@ + + + + + +Foo + + + + diff --git a/layout/xul/crashtests/397304-1.html b/layout/xul/crashtests/397304-1.html new file mode 100644 index 000000000..3501f0581 --- /dev/null +++ b/layout/xul/crashtests/397304-1.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/layout/xul/crashtests/398326-1.xhtml b/layout/xul/crashtests/398326-1.xhtml new file mode 100644 index 000000000..a265ae4e0 --- /dev/null +++ b/layout/xul/crashtests/398326-1.xhtml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/layout/xul/crashtests/399013.xul b/layout/xul/crashtests/399013.xul new file mode 100644 index 000000000..a2349aff8 --- /dev/null +++ b/layout/xul/crashtests/399013.xul @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/layout/xul/crashtests/400779-1.xhtml b/layout/xul/crashtests/400779-1.xhtml new file mode 100644 index 000000000..c0f5d493c --- /dev/null +++ b/layout/xul/crashtests/400779-1.xhtml @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/layout/xul/crashtests/402912-1.xhtml b/layout/xul/crashtests/402912-1.xhtml new file mode 100644 index 000000000..b2cb98dc5 --- /dev/null +++ b/layout/xul/crashtests/402912-1.xhtml @@ -0,0 +1,5 @@ + + + + + diff --git a/layout/xul/crashtests/404192.xhtml b/layout/xul/crashtests/404192.xhtml new file mode 100644 index 000000000..4ad5af348 --- /dev/null +++ b/layout/xul/crashtests/404192.xhtml @@ -0,0 +1,12 @@ + + + + + diff --git a/layout/xul/crashtests/407152.xul b/layout/xul/crashtests/407152.xul new file mode 100644 index 000000000..0a5adf69e --- /dev/null +++ b/layout/xul/crashtests/407152.xul @@ -0,0 +1,7 @@ + + + + + + diff --git a/layout/xul/grid/crashtests/321066-1.xul b/layout/xul/grid/crashtests/321066-1.xul new file mode 100644 index 000000000..789c2582c --- /dev/null +++ b/layout/xul/grid/crashtests/321066-1.xul @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/layout/xul/grid/crashtests/321073-1.xul b/layout/xul/grid/crashtests/321073-1.xul new file mode 100644 index 000000000..b92098b62 --- /dev/null +++ b/layout/xul/grid/crashtests/321073-1.xul @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/layout/xul/grid/crashtests/382750-1.xul b/layout/xul/grid/crashtests/382750-1.xul new file mode 100644 index 000000000..7a9da73ec --- /dev/null +++ b/layout/xul/grid/crashtests/382750-1.xul @@ -0,0 +1,5 @@ + + + + + diff --git a/layout/xul/grid/crashtests/400790-1.xul b/layout/xul/grid/crashtests/400790-1.xul new file mode 100644 index 000000000..4de709428 --- /dev/null +++ b/layout/xul/grid/crashtests/400790-1.xul @@ -0,0 +1,20 @@ + + + + + + + diff --git a/layout/xul/grid/crashtests/423802-crash.xul b/layout/xul/grid/crashtests/423802-crash.xul new file mode 100644 index 000000000..0ae4eab8f --- /dev/null +++ b/layout/xul/grid/crashtests/423802-crash.xul @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/layout/xul/grid/crashtests/crashtests.list b/layout/xul/grid/crashtests/crashtests.list new file mode 100644 index 000000000..afe8e1002 --- /dev/null +++ b/layout/xul/grid/crashtests/crashtests.list @@ -0,0 +1,11 @@ +load 306911-crash.xul +load 306911-grid-testcases.xul +load 306911-grid-testcases2.xul +load 311710-1.xul +load 312784-1.xul +load 313173-1.html +load 321066-1.xul +load 321073-1.xul +load 382750-1.xul +load 400790-1.xul +load 423802-crash.xul diff --git a/layout/xul/grid/examples/borderedcolumns.xul b/layout/xul/grid/examples/borderedcolumns.xul new file mode 100644 index 000000000..15ee06911 --- /dev/null +++ b/layout/xul/grid/examples/borderedcolumns.xul @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/xul/grid/examples/borderedrowscolumns.xul b/layout/xul/grid/examples/borderedrowscolumns.xul new file mode 100644 index 000000000..94d3d8d99 --- /dev/null +++ b/layout/xul/grid/examples/borderedrowscolumns.xul @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/xul/grid/examples/borderedrowscolumns2.xul b/layout/xul/grid/examples/borderedrowscolumns2.xul new file mode 100644 index 000000000..96b6ca9e5 --- /dev/null +++ b/layout/xul/grid/examples/borderedrowscolumns2.xul @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/xul/grid/examples/borderedrowscolumns3.xul b/layout/xul/grid/examples/borderedrowscolumns3.xul new file mode 100644 index 000000000..30a6fcc1b --- /dev/null +++ b/layout/xul/grid/examples/borderedrowscolumns3.xul @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/xul/grid/examples/bordermargincolumns1.xul b/layout/xul/grid/examples/bordermargincolumns1.xul new file mode 100644 index 000000000..009f932a8 --- /dev/null +++ b/layout/xul/grid/examples/bordermargincolumns1.xul @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/xul/grid/examples/collapsetest.xul b/layout/xul/grid/examples/collapsetest.xul new file mode 100644 index 000000000..5e1a042f6 --- /dev/null +++ b/layout/xul/grid/examples/collapsetest.xul @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/xul/test/test_popupSizeTo.xul b/layout/xul/test/test_popupSizeTo.xul new file mode 100644 index 000000000..a135e1980 --- /dev/null +++ b/layout/xul/test/test_popupSizeTo.xul @@ -0,0 +1,55 @@ + + + + + + + + + + + + + diff --git a/layout/xul/test/test_popupZoom.xul b/layout/xul/test/test_popupZoom.xul new file mode 100644 index 000000000..b8e15ba1d --- /dev/null +++ b/layout/xul/test/test_popupZoom.xul @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + diff --git a/layout/xul/test/test_resizer.xul b/layout/xul/test/test_resizer.xul new file mode 100644 index 000000000..2ba971d05 --- /dev/null +++ b/layout/xul/test/test_resizer.xul @@ -0,0 +1,94 @@ + + + + + + + + + + + + + diff --git a/layout/xul/test/test_resizer_incontent.xul b/layout/xul/test/test_resizer_incontent.xul new file mode 100644 index 000000000..068bd5bb1 --- /dev/null +++ b/layout/xul/test/test_resizer_incontent.xul @@ -0,0 +1,42 @@ + + + + + + + + + + diff --git a/layout/xul/test/test_splitter.xul b/layout/xul/test/test_splitter.xul new file mode 100644 index 000000000..697ad4be8 --- /dev/null +++ b/layout/xul/test/test_splitter.xul @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/xul/test/test_stack.xul b/layout/xul/test/test_stack.xul new file mode 100644 index 000000000..781a6f298 --- /dev/null +++ b/layout/xul/test/test_stack.xul @@ -0,0 +1,327 @@ + + + + + + + diff --git a/layout/xul/test/test_submenuClose.xul b/layout/xul/test/test_submenuClose.xul new file mode 100644 index 000000000..907736d99 --- /dev/null +++ b/layout/xul/test/test_submenuClose.xul @@ -0,0 +1,91 @@ + + + + + + + diff --git a/layout/xul/test/test_windowminmaxsize.xul b/layout/xul/test/test_windowminmaxsize.xul new file mode 100644 index 000000000..5909039cf --- /dev/null +++ b/layout/xul/test/test_windowminmaxsize.xul @@ -0,0 +1,245 @@ + + + + + + + + + +

+

+ +
+
+ + +
diff --git a/layout/xul/test/window_resizer.xul b/layout/xul/test/window_resizer.xul new file mode 100644 index 000000000..4e349d125 --- /dev/null +++ b/layout/xul/test/window_resizer.xul @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/xul/test/window_resizer_element.xul b/layout/xul/test/window_resizer_element.xul new file mode 100644 index 000000000..b0c58d1a1 --- /dev/null +++ b/layout/xul/test/window_resizer_element.xul @@ -0,0 +1,188 @@ + + + + + + + + + + + + One + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/xul/tree/TreeBoxObject.cpp b/layout/xul/tree/TreeBoxObject.cpp new file mode 100644 index 000000000..2265d9ee5 --- /dev/null +++ b/layout/xul/tree/TreeBoxObject.cpp @@ -0,0 +1,695 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "mozilla/dom/TreeBoxObject.h" +#include "nsCOMPtr.h" +#include "nsIDOMXULElement.h" +#include "nsIScriptableRegion.h" +#include "nsIXULTemplateBuilder.h" +#include "nsTreeContentView.h" +#include "nsITreeSelection.h" +#include "ChildIterator.h" +#include "nsContentUtils.h" +#include "nsError.h" +#include "nsTreeBodyFrame.h" +#include "mozilla/dom/TreeBoxObjectBinding.h" +#include "nsITreeColumns.h" +#include "mozilla/dom/DOMRect.h" +#include "mozilla/dom/BindingUtils.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/ToJSValue.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_INHERITED(TreeBoxObject, BoxObject, + mView) + +NS_IMPL_ADDREF_INHERITED(TreeBoxObject, BoxObject) +NS_IMPL_RELEASE_INHERITED(TreeBoxObject, BoxObject) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TreeBoxObject) + NS_INTERFACE_MAP_ENTRY(nsITreeBoxObject) +NS_INTERFACE_MAP_END_INHERITING(BoxObject) + +void +TreeBoxObject::Clear() +{ + ClearCachedValues(); + + // Drop the view's ref to us. + if (mView) { + nsCOMPtr sel; + mView->GetSelection(getter_AddRefs(sel)); + if (sel) + sel->SetTree(nullptr); + mView->SetTree(nullptr); // Break the circular ref between the view and us. + } + mView = nullptr; + + BoxObject::Clear(); +} + + +TreeBoxObject::TreeBoxObject() + : mTreeBody(nullptr) +{ +} + +TreeBoxObject::~TreeBoxObject() +{ +} + +static nsIContent* FindBodyElement(nsIContent* aParent) +{ + mozilla::dom::FlattenedChildIterator iter(aParent); + for (nsIContent* content = iter.GetNextChild(); content; content = iter.GetNextChild()) { + mozilla::dom::NodeInfo *ni = content->NodeInfo(); + if (ni->Equals(nsGkAtoms::treechildren, kNameSpaceID_XUL)) { + return content; + } else if (ni->Equals(nsGkAtoms::tree, kNameSpaceID_XUL)) { + // There are nesting tree elements. Only the innermost should + // find the treechilren. + return nullptr; + } else if (content->IsElement() && + !ni->Equals(nsGkAtoms::_template, kNameSpaceID_XUL)) { + nsIContent* result = FindBodyElement(content); + if (result) + return result; + } + } + + return nullptr; +} + +nsTreeBodyFrame* +TreeBoxObject::GetTreeBodyFrame(bool aFlushLayout) +{ + // Make sure our frames are up to date, and layout as needed. We + // have to do this before checking for our cached mTreeBody, since + // it might go away on style flush, and in any case if aFlushLayout + // is true we need to make sure to flush no matter what. + // XXXbz except that flushing style when we were not asked to flush + // layout here breaks things. See bug 585123. + nsIFrame* frame = nullptr; + if (aFlushLayout) { + frame = GetFrame(aFlushLayout); + if (!frame) + return nullptr; + } + + if (mTreeBody) { + // Have one cached already. + return mTreeBody; + } + + if (!aFlushLayout) { + frame = GetFrame(aFlushLayout); + if (!frame) + return nullptr; + } + + // Iterate over our content model children looking for the body. + nsCOMPtr content = FindBodyElement(frame->GetContent()); + if (!content) + return nullptr; + + frame = content->GetPrimaryFrame(); + if (!frame) + return nullptr; + + // Make sure that the treebodyframe has a pointer to |this|. + nsTreeBodyFrame *treeBody = do_QueryFrame(frame); + NS_ENSURE_TRUE(treeBody && treeBody->GetTreeBoxObject() == this, nullptr); + + mTreeBody = treeBody; + return mTreeBody; +} + +NS_IMETHODIMP +TreeBoxObject::GetView(nsITreeView * *aView) +{ + if (!mTreeBody) { + if (!GetTreeBodyFrame()) { + // Don't return an uninitialised view + *aView = nullptr; + return NS_OK; + } + + if (mView) + // Our new frame needs to initialise itself + return mTreeBody->GetView(aView); + } + if (!mView) { + nsCOMPtr xulele = do_QueryInterface(mContent); + if (xulele) { + // See if there is a XUL tree builder associated with the element + nsCOMPtr builder; + xulele->GetBuilder(getter_AddRefs(builder)); + mView = do_QueryInterface(builder); + + if (!mView) { + // No tree builder, create a tree content view. + nsresult rv = NS_NewTreeContentView(getter_AddRefs(mView)); + NS_ENSURE_SUCCESS(rv, rv); + } + + // Initialise the frame and view + mTreeBody->SetView(mView); + } + } + NS_IF_ADDREF(*aView = mView); + return NS_OK; +} + +already_AddRefed +TreeBoxObject::GetView() { + nsCOMPtr view; + GetView(getter_AddRefs(view)); + return view.forget(); +} + +static bool +CanTrustView(nsISupports* aValue) +{ + // Untrusted content is only allowed to specify known-good views + if (nsContentUtils::IsCallerChrome()) + return true; + nsCOMPtr nativeTreeView = do_QueryInterface(aValue); + if (!nativeTreeView || NS_FAILED(nativeTreeView->EnsureNative())) { + // XXX ERRMSG need a good error here for developers + return false; + } + return true; +} + +NS_IMETHODIMP TreeBoxObject::SetView(nsITreeView * aView) +{ + if (!CanTrustView(aView)) + return NS_ERROR_DOM_SECURITY_ERR; + + mView = aView; + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + body->SetView(aView); + + return NS_OK; +} + +void TreeBoxObject::SetView(nsITreeView* aView, ErrorResult& aRv) +{ + aRv = SetView(aView); +} + +bool TreeBoxObject::Focused() +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->GetFocused(); + return false; +} + +NS_IMETHODIMP TreeBoxObject::GetFocused(bool* aFocused) +{ + *aFocused = Focused(); + return NS_OK; +} + +NS_IMETHODIMP TreeBoxObject::SetFocused(bool aFocused) +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->SetFocused(aFocused); + return NS_OK; +} + +NS_IMETHODIMP TreeBoxObject::GetTreeBody(nsIDOMElement** aElement) +{ + *aElement = nullptr; + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->GetTreeBody(aElement); + return NS_OK; +} + +already_AddRefed +TreeBoxObject::GetTreeBody() +{ + nsCOMPtr el; + GetTreeBody(getter_AddRefs(el)); + nsCOMPtr ret(do_QueryInterface(el)); + return ret.forget(); +} + +already_AddRefed +TreeBoxObject::GetColumns() +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->Columns(); + return nullptr; +} + +NS_IMETHODIMP TreeBoxObject::GetColumns(nsITreeColumns** aColumns) +{ + *aColumns = GetColumns().take(); + return NS_OK; +} + +int32_t TreeBoxObject::RowHeight() +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->RowHeight(); + return 0; +} + +int32_t TreeBoxObject::RowWidth() +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->RowWidth(); + return 0; +} + +NS_IMETHODIMP TreeBoxObject::GetRowHeight(int32_t* aRowHeight) +{ + *aRowHeight = RowHeight(); + return NS_OK; +} + +NS_IMETHODIMP TreeBoxObject::GetRowWidth(int32_t *aRowWidth) +{ + *aRowWidth = RowWidth(); + return NS_OK; +} + +int32_t TreeBoxObject::GetFirstVisibleRow() +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->FirstVisibleRow(); + return 0; +} + +NS_IMETHODIMP TreeBoxObject::GetFirstVisibleRow(int32_t *aFirstVisibleRow) +{ + *aFirstVisibleRow = GetFirstVisibleRow(); + return NS_OK; +} + +int32_t TreeBoxObject::GetLastVisibleRow() +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->LastVisibleRow(); + return 0; +} + +NS_IMETHODIMP TreeBoxObject::GetLastVisibleRow(int32_t *aLastVisibleRow) +{ + *aLastVisibleRow = GetLastVisibleRow(); + return NS_OK; +} + +int32_t TreeBoxObject::HorizontalPosition() +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->GetHorizontalPosition(); + return 0; +} + +NS_IMETHODIMP TreeBoxObject::GetHorizontalPosition(int32_t *aHorizontalPosition) +{ + *aHorizontalPosition = HorizontalPosition(); + return NS_OK; +} + +int32_t TreeBoxObject::GetPageLength() +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->PageLength(); + return 0; +} + +NS_IMETHODIMP TreeBoxObject::GetPageLength(int32_t *aPageLength) +{ + *aPageLength = GetPageLength(); + return NS_OK; +} + +NS_IMETHODIMP TreeBoxObject::GetSelectionRegion(nsIScriptableRegion **aRegion) +{ + *aRegion = nullptr; + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->GetSelectionRegion(aRegion); + return NS_OK; +} + +already_AddRefed +TreeBoxObject::SelectionRegion() +{ + nsCOMPtr region; + GetSelectionRegion(getter_AddRefs(region)); + return region.forget(); +} + +NS_IMETHODIMP +TreeBoxObject::EnsureRowIsVisible(int32_t aRow) +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->EnsureRowIsVisible(aRow); + return NS_OK; +} + +NS_IMETHODIMP +TreeBoxObject::EnsureCellIsVisible(int32_t aRow, nsITreeColumn* aCol) +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->EnsureCellIsVisible(aRow, aCol); + return NS_OK; +} + +NS_IMETHODIMP +TreeBoxObject::ScrollToRow(int32_t aRow) +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(true); + if (body) + return body->ScrollToRow(aRow); + return NS_OK; +} + +NS_IMETHODIMP +TreeBoxObject::ScrollByLines(int32_t aNumLines) +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->ScrollByLines(aNumLines); + return NS_OK; +} + +NS_IMETHODIMP +TreeBoxObject::ScrollByPages(int32_t aNumPages) +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->ScrollByPages(aNumPages); + return NS_OK; +} + +NS_IMETHODIMP +TreeBoxObject::ScrollToCell(int32_t aRow, nsITreeColumn* aCol) +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->ScrollToCell(aRow, aCol); + return NS_OK; +} + +NS_IMETHODIMP +TreeBoxObject::ScrollToColumn(nsITreeColumn* aCol) +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->ScrollToColumn(aCol); + return NS_OK; +} + +NS_IMETHODIMP +TreeBoxObject::ScrollToHorizontalPosition(int32_t aHorizontalPosition) +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->ScrollToHorizontalPosition(aHorizontalPosition); + return NS_OK; +} + +NS_IMETHODIMP TreeBoxObject::Invalidate() +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->Invalidate(); + return NS_OK; +} + +NS_IMETHODIMP +TreeBoxObject::InvalidateColumn(nsITreeColumn* aCol) +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->InvalidateColumn(aCol); + return NS_OK; +} + +NS_IMETHODIMP +TreeBoxObject::InvalidateRow(int32_t aIndex) +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->InvalidateRow(aIndex); + return NS_OK; +} + +NS_IMETHODIMP +TreeBoxObject::InvalidateCell(int32_t aRow, nsITreeColumn* aCol) +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->InvalidateCell(aRow, aCol); + return NS_OK; +} + +NS_IMETHODIMP +TreeBoxObject::InvalidateRange(int32_t aStart, int32_t aEnd) +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->InvalidateRange(aStart, aEnd); + return NS_OK; +} + +NS_IMETHODIMP +TreeBoxObject::InvalidateColumnRange(int32_t aStart, int32_t aEnd, nsITreeColumn* aCol) +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->InvalidateColumnRange(aStart, aEnd, aCol); + return NS_OK; +} + +NS_IMETHODIMP +TreeBoxObject::GetRowAt(int32_t x, int32_t y, int32_t *aRow) +{ + *aRow = 0; + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->GetRowAt(x, y, aRow); + return NS_OK; +} + +int32_t +TreeBoxObject::GetRowAt(int32_t x, int32_t y) +{ + int32_t row; + GetRowAt(x, y, &row); + return row; +} + +NS_IMETHODIMP +TreeBoxObject::GetCellAt(int32_t aX, int32_t aY, int32_t *aRow, + nsITreeColumn** aCol, nsAString& aChildElt) +{ + *aRow = 0; + *aCol = nullptr; + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) { + nsAutoCString element; + nsresult retval = body->GetCellAt(aX, aY, aRow, aCol, element); + CopyUTF8toUTF16(element, aChildElt); + return retval; + } + return NS_OK; +} + +void +TreeBoxObject::GetCellAt(int32_t x, int32_t y, TreeCellInfo& aRetVal, ErrorResult& aRv) +{ + nsCOMPtr col; + GetCellAt(x, y, &aRetVal.mRow, getter_AddRefs(col), aRetVal.mChildElt); + aRetVal.mCol = col.forget().downcast(); +} + +void +TreeBoxObject::GetCellAt(JSContext* cx, + int32_t x, int32_t y, + JS::Handle rowOut, + JS::Handle colOut, + JS::Handle childEltOut, + ErrorResult& aRv) +{ + int32_t row; + nsITreeColumn* col; + nsAutoString childElt; + GetCellAt(x, y, &row, &col, childElt); + + JS::Rooted v(cx); + + if (!ToJSValue(cx, row, &v) || + !JS_SetProperty(cx, rowOut, "value", v)) { + aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL); + return; + } + if (!dom::WrapObject(cx, col, &v) || + !JS_SetProperty(cx, colOut, "value", v)) { + aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL); + return; + } + if (!ToJSValue(cx, childElt, &v) || + !JS_SetProperty(cx, childEltOut, "value", v)) { + aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL); + return; + } +} + +NS_IMETHODIMP +TreeBoxObject::GetCoordsForCellItem(int32_t aRow, nsITreeColumn* aCol, const nsAString& aElement, + int32_t *aX, int32_t *aY, int32_t *aWidth, int32_t *aHeight) +{ + *aX = *aY = *aWidth = *aHeight = 0; + nsTreeBodyFrame* body = GetTreeBodyFrame(); + NS_ConvertUTF16toUTF8 element(aElement); + if (body) + return body->GetCoordsForCellItem(aRow, aCol, element, aX, aY, aWidth, aHeight); + return NS_OK; +} + +already_AddRefed +TreeBoxObject::GetCoordsForCellItem(int32_t row, nsTreeColumn& col, const nsAString& element, ErrorResult& aRv) +{ + int32_t x, y, w, h; + GetCoordsForCellItem(row, &col, element, &x, &y, &w, &h); + RefPtr rect = new DOMRect(mContent, x, y, w, h); + return rect.forget(); +} + +void +TreeBoxObject::GetCoordsForCellItem(JSContext* cx, + int32_t row, + nsTreeColumn& col, + const nsAString& element, + JS::Handle xOut, + JS::Handle yOut, + JS::Handle widthOut, + JS::Handle heightOut, + ErrorResult& aRv) +{ + int32_t x, y, w, h; + GetCoordsForCellItem(row, &col, element, &x, &y, &w, &h); + JS::Rooted v(cx, JS::Int32Value(x)); + if (!JS_SetProperty(cx, xOut, "value", v)) { + aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL); + return; + } + v.setInt32(y); + if (!JS_SetProperty(cx, yOut, "value", v)) { + aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL); + return; + } + v.setInt32(w); + if (!JS_SetProperty(cx, widthOut, "value", v)) { + aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL); + return; + } + v.setInt32(h); + if (!JS_SetProperty(cx, heightOut, "value", v)) { + aRv.Throw(NS_ERROR_XPC_CANT_SET_OUT_VAL); + return; + } +} + +NS_IMETHODIMP +TreeBoxObject::IsCellCropped(int32_t aRow, nsITreeColumn* aCol, bool *aIsCropped) +{ + *aIsCropped = false; + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->IsCellCropped(aRow, aCol, aIsCropped); + return NS_OK; +} + +bool +TreeBoxObject::IsCellCropped(int32_t row, nsITreeColumn* col, ErrorResult& aRv) +{ + bool ret; + aRv = IsCellCropped(row, col, &ret); + return ret; +} + +NS_IMETHODIMP +TreeBoxObject::RowCountChanged(int32_t aIndex, int32_t aDelta) +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->RowCountChanged(aIndex, aDelta); + return NS_OK; +} + +NS_IMETHODIMP +TreeBoxObject::BeginUpdateBatch() +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->BeginUpdateBatch(); + return NS_OK; +} + +NS_IMETHODIMP +TreeBoxObject::EndUpdateBatch() +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->EndUpdateBatch(); + return NS_OK; +} + +NS_IMETHODIMP +TreeBoxObject::ClearStyleAndImageCaches() +{ + nsTreeBodyFrame* body = GetTreeBodyFrame(); + if (body) + return body->ClearStyleAndImageCaches(); + return NS_OK; +} + +void +TreeBoxObject::ClearCachedValues() +{ + mTreeBody = nullptr; +} + +JSObject* +TreeBoxObject::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return TreeBoxObjectBinding::Wrap(aCx, this, aGivenProto); +} + +} // namespace dom +} // namespace mozilla + +// Creation Routine /////////////////////////////////////////////////////////////////////// + +using namespace mozilla::dom; + +nsresult +NS_NewTreeBoxObject(nsIBoxObject** aResult) +{ + NS_ADDREF(*aResult = new TreeBoxObject()); + return NS_OK; +} diff --git a/layout/xul/tree/TreeBoxObject.h b/layout/xul/tree/TreeBoxObject.h new file mode 100644 index 000000000..d997b5a63 --- /dev/null +++ b/layout/xul/tree/TreeBoxObject.h @@ -0,0 +1,128 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#ifndef mozilla_dom_TreeBoxObject_h +#define mozilla_dom_TreeBoxObject_h + +#include "mozilla/dom/BoxObject.h" +#include "nsITreeView.h" +#include "nsITreeBoxObject.h" + +class nsTreeBodyFrame; +class nsTreeColumn; +class nsTreeColumns; + +namespace mozilla { +namespace dom { + +struct TreeCellInfo; +class DOMRect; + +class TreeBoxObject final : public BoxObject, + public nsITreeBoxObject +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TreeBoxObject, BoxObject) + NS_DECL_NSITREEBOXOBJECT + + TreeBoxObject(); + + nsTreeBodyFrame* GetTreeBodyFrame(bool aFlushLayout = false); + nsTreeBodyFrame* GetCachedTreeBodyFrame() { return mTreeBody; } + + //NS_PIBOXOBJECT interfaces + virtual void Clear() override; + virtual void ClearCachedValues() override; + + // WebIDL + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + + already_AddRefed GetColumns(); + + already_AddRefed GetView(); + + void SetView(nsITreeView* arg, ErrorResult& aRv); + + bool Focused(); + + already_AddRefed GetTreeBody(); + + int32_t RowHeight(); + + int32_t RowWidth(); + + int32_t HorizontalPosition(); + + already_AddRefed SelectionRegion(); + + int32_t GetFirstVisibleRow(); + + int32_t GetLastVisibleRow(); + + int32_t GetPageLength(); + + int32_t GetRowAt(int32_t x, int32_t y); + + void GetCellAt(int32_t x, int32_t y, TreeCellInfo& aRetVal, ErrorResult& aRv); + + already_AddRefed GetCoordsForCellItem(int32_t row, + nsTreeColumn& col, + const nsAString& element, + ErrorResult& aRv); + + bool IsCellCropped(int32_t row, nsITreeColumn* col, ErrorResult& aRv); + + // Deprecated APIs from old IDL + void GetCellAt(JSContext* cx, + int32_t x, int32_t y, + JS::Handle rowOut, + JS::Handle colOut, + JS::Handle childEltOut, + ErrorResult& aRv); + + void GetCoordsForCellItem(JSContext* cx, + int32_t row, + nsTreeColumn& col, + const nsAString& element, + JS::Handle xOut, + JS::Handle yOut, + JS::Handle widthOut, + JS::Handle heightOut, + ErrorResult& aRv); + + // Same signature (except for nsresult return type) as the XPIDL impls + // void Invalidate(); + // void BeginUpdateBatch(); + // void EndUpdateBatch(); + // void ClearStyleAndImageCaches(); + // void SetFocused(bool arg); + // void EnsureRowIsVisible(int32_t index); + // void EnsureCellIsVisible(int32_t row, nsITreeColumn* col); + // void ScrollToRow(int32_t index); + // void ScrollByLines(int32_t numLines); + // void ScrollByPages(int32_t numPages); + // void ScrollToCell(int32_t row, nsITreeColumn* col); + // void ScrollToColumn(nsITreeColumn* col); + // void ScrollToHorizontalPosition(int32_t horizontalPosition); + // void InvalidateColumn(nsITreeColumn* col); + // void InvalidateRow(int32_t index); + // void InvalidateCell(int32_t row, nsITreeColumn* col); + // void InvalidateRange(int32_t startIndex, int32_t endIndex); + // void InvalidateColumnRange(int32_t startIndex, int32_t endIndex, nsITreeColumn* col); + // void RowCountChanged(int32_t index, int32_t count); + +protected: + nsTreeBodyFrame* mTreeBody; + nsCOMPtr mView; + +private: + ~TreeBoxObject(); +}; + +} // namespace dom +} // namespace mozilla + +#endif diff --git a/layout/xul/tree/crashtests/307298-1.xul b/layout/xul/tree/crashtests/307298-1.xul new file mode 100644 index 000000000..57396755a --- /dev/null +++ b/layout/xul/tree/crashtests/307298-1.xul @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/xul/tree/crashtests/309732-1.xul b/layout/xul/tree/crashtests/309732-1.xul new file mode 100644 index 000000000..df955e14e --- /dev/null +++ b/layout/xul/tree/crashtests/309732-1.xul @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/xul/tree/crashtests/309732-2.xul b/layout/xul/tree/crashtests/309732-2.xul new file mode 100644 index 000000000..9c65e7379 --- /dev/null +++ b/layout/xul/tree/crashtests/309732-2.xul @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/xul/tree/crashtests/366583-1.xul b/layout/xul/tree/crashtests/366583-1.xul new file mode 100644 index 000000000..db37b444e --- /dev/null +++ b/layout/xul/tree/crashtests/366583-1.xul @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/xul/tree/crashtests/380217-1.xul b/layout/xul/tree/crashtests/380217-1.xul new file mode 100644 index 000000000..b834f7e1f --- /dev/null +++ b/layout/xul/tree/crashtests/380217-1.xul @@ -0,0 +1,24 @@ + + + + + +* { position: fixed; } + + + + + + + + + + + + + + + + diff --git a/layout/xul/tree/crashtests/382444-1-inner.html b/layout/xul/tree/crashtests/382444-1-inner.html new file mode 100644 index 000000000..d59a2e787 --- /dev/null +++ b/layout/xul/tree/crashtests/382444-1-inner.html @@ -0,0 +1,15 @@ + + +Testcase bug - Crash [@ nsINodeInfo::Equals] with underflow event, tree stuff and removing window + + + + + + + diff --git a/layout/xul/tree/crashtests/382444-1.html b/layout/xul/tree/crashtests/382444-1.html new file mode 100644 index 000000000..8926cf16d --- /dev/null +++ b/layout/xul/tree/crashtests/382444-1.html @@ -0,0 +1,9 @@ + + + + + + + diff --git a/layout/xul/tree/crashtests/391178-1.xhtml b/layout/xul/tree/crashtests/391178-1.xhtml new file mode 100644 index 000000000..0f4b16cd9 --- /dev/null +++ b/layout/xul/tree/crashtests/391178-1.xhtml @@ -0,0 +1,41 @@ + + + + + + + +
+
+ + + diff --git a/layout/xul/tree/crashtests/391178-2.xul b/layout/xul/tree/crashtests/391178-2.xul new file mode 100644 index 000000000..491fbe77b --- /dev/null +++ b/layout/xul/tree/crashtests/391178-2.xul @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + diff --git a/layout/xul/tree/crashtests/393665-1.xul b/layout/xul/tree/crashtests/393665-1.xul new file mode 100644 index 000000000..6fb5ec0c9 --- /dev/null +++ b/layout/xul/tree/crashtests/393665-1.xul @@ -0,0 +1,3 @@ + + + diff --git a/layout/xul/tree/crashtests/399227-1.xul b/layout/xul/tree/crashtests/399227-1.xul new file mode 100644 index 000000000..bfc381892 --- /dev/null +++ b/layout/xul/tree/crashtests/399227-1.xul @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/xul/tree/crashtests/399227-2.xul b/layout/xul/tree/crashtests/399227-2.xul new file mode 100644 index 000000000..55665ec47 --- /dev/null +++ b/layout/xul/tree/crashtests/399227-2.xul @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/xul/tree/crashtests/399692-1.xhtml b/layout/xul/tree/crashtests/399692-1.xhtml new file mode 100644 index 000000000..97eec2674 --- /dev/null +++ b/layout/xul/tree/crashtests/399692-1.xhtml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/layout/xul/tree/crashtests/399715-1.xhtml b/layout/xul/tree/crashtests/399715-1.xhtml new file mode 100644 index 000000000..ea0a20cfa --- /dev/null +++ b/layout/xul/tree/crashtests/399715-1.xhtml @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/layout/xul/tree/crashtests/409807-1.xul b/layout/xul/tree/crashtests/409807-1.xul new file mode 100644 index 000000000..a3af3da41 --- /dev/null +++ b/layout/xul/tree/crashtests/409807-1.xul @@ -0,0 +1,25 @@ + + + + + + + diff --git a/layout/xul/tree/crashtests/414170-1.xul b/layout/xul/tree/crashtests/414170-1.xul new file mode 100644 index 000000000..f3bc1d134 --- /dev/null +++ b/layout/xul/tree/crashtests/414170-1.xul @@ -0,0 +1,20 @@ + + + + + + + + + + + diff --git a/layout/xul/tree/crashtests/430394-1.xul b/layout/xul/tree/crashtests/430394-1.xul new file mode 100644 index 000000000..63f4c3780 --- /dev/null +++ b/layout/xul/tree/crashtests/430394-1.xul @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/layout/xul/tree/crashtests/454186-1.xul b/layout/xul/tree/crashtests/454186-1.xul new file mode 100644 index 000000000..edf266e43 --- /dev/null +++ b/layout/xul/tree/crashtests/454186-1.xul @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/xul/tree/crashtests/479931-1.xhtml b/layout/xul/tree/crashtests/479931-1.xhtml new file mode 100644 index 000000000..458a19250 --- /dev/null +++ b/layout/xul/tree/crashtests/479931-1.xhtml @@ -0,0 +1,19 @@ + + + + + + +
+ + + diff --git a/layout/xul/tree/crashtests/509602-1-overlay.xul b/layout/xul/tree/crashtests/509602-1-overlay.xul new file mode 100644 index 000000000..f5cecd40e --- /dev/null +++ b/layout/xul/tree/crashtests/509602-1-overlay.xul @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/layout/xul/tree/crashtests/509602-1.xul b/layout/xul/tree/crashtests/509602-1.xul new file mode 100644 index 000000000..a1cdcf1cc --- /dev/null +++ b/layout/xul/tree/crashtests/509602-1.xul @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/layout/xul/tree/crashtests/585815-iframe.xul b/layout/xul/tree/crashtests/585815-iframe.xul new file mode 100644 index 000000000..ce9d29448 --- /dev/null +++ b/layout/xul/tree/crashtests/585815-iframe.xul @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/xul/tree/crashtests/585815.html b/layout/xul/tree/crashtests/585815.html new file mode 100644 index 000000000..0b8b01827 --- /dev/null +++ b/layout/xul/tree/crashtests/585815.html @@ -0,0 +1,18 @@ + + + + Testcase for bug 585815 + + + + + + + + + diff --git a/layout/xul/tree/crashtests/601427.html b/layout/xul/tree/crashtests/601427.html new file mode 100644 index 000000000..cd9574eef --- /dev/null +++ b/layout/xul/tree/crashtests/601427.html @@ -0,0 +1,30 @@ + + + + diff --git a/layout/xul/tree/crashtests/730441-1.xul b/layout/xul/tree/crashtests/730441-1.xul new file mode 100644 index 000000000..a31c3b63f --- /dev/null +++ b/layout/xul/tree/crashtests/730441-1.xul @@ -0,0 +1,54 @@ + + + + + + + + + + + + + diff --git a/layout/xul/tree/crashtests/730441-2.xul b/layout/xul/tree/crashtests/730441-2.xul new file mode 100644 index 000000000..02b3a307e --- /dev/null +++ b/layout/xul/tree/crashtests/730441-2.xul @@ -0,0 +1,52 @@ + + + + + + + + + + + + diff --git a/layout/xul/tree/crashtests/730441-3.xul b/layout/xul/tree/crashtests/730441-3.xul new file mode 100644 index 000000000..2cf74d1b9 --- /dev/null +++ b/layout/xul/tree/crashtests/730441-3.xul @@ -0,0 +1,38 @@ + + + + + + + + diff --git a/layout/xul/tree/crashtests/crashtests.list b/layout/xul/tree/crashtests/crashtests.list new file mode 100644 index 000000000..d462e9550 --- /dev/null +++ b/layout/xul/tree/crashtests/crashtests.list @@ -0,0 +1,24 @@ +load 307298-1.xul +load 309732-1.xul +load 309732-2.xul +load 366583-1.xul +load 380217-1.xul +load 382444-1.html +load 391178-1.xhtml +load 391178-2.xul +load 393665-1.xul +load 399227-1.xul +load 399227-2.xul +load 399692-1.xhtml +load 399715-1.xhtml +load 409807-1.xul +load 414170-1.xul +load 430394-1.xul +load 454186-1.xul +load 479931-1.xhtml +load 509602-1.xul +load 585815.html +load 601427.html +load 730441-1.xul +load 730441-2.xul +load 730441-3.xul diff --git a/layout/xul/tree/moz.build b/layout/xul/tree/moz.build new file mode 100644 index 000000000..ccac5bde9 --- /dev/null +++ b/layout/xul/tree/moz.build @@ -0,0 +1,53 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +with Files('**'): + BUG_COMPONENT = ('Core', 'XP Toolkit/Widgets: XUL') + +XPIDL_SOURCES += [ + 'nsITreeBoxObject.idl', + 'nsITreeColumns.idl', + 'nsITreeContentView.idl', + 'nsITreeSelection.idl', + 'nsITreeView.idl', +] + +XPIDL_MODULE = 'layout_xul_tree' + +EXPORTS += [ + 'nsTreeColFrame.h', + 'nsTreeColumns.h', + 'nsTreeUtils.h', +] + +EXPORTS.mozilla.dom += [ + 'TreeBoxObject.h' +] + +UNIFIED_SOURCES += [ + 'nsTreeBodyFrame.cpp', + 'nsTreeColFrame.cpp', + 'nsTreeColumns.cpp', + 'nsTreeContentView.cpp', + 'nsTreeImageListener.cpp', + 'nsTreeSelection.cpp', + 'nsTreeStyleCache.cpp', + 'nsTreeUtils.cpp', + 'TreeBoxObject.cpp', +] + +FINAL_LIBRARY = 'xul' +LOCAL_INCLUDES += [ + '..', + '../../base', + '../../forms', + '../../generic', + '../../style', + '/dom/base', +] + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wno-error=shadow'] diff --git a/layout/xul/tree/nsITreeBoxObject.idl b/layout/xul/tree/nsITreeBoxObject.idl new file mode 100644 index 000000000..638c8a41c --- /dev/null +++ b/layout/xul/tree/nsITreeBoxObject.idl @@ -0,0 +1,189 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "nsISupports.idl" + +interface nsIDOMElement; +interface nsITreeView; +interface nsITreeSelection; +interface nsITreeColumn; +interface nsITreeColumns; +interface nsIScriptableRegion; + +[scriptable, uuid(f3da0c5e-51f5-45f0-b2cd-6be3ab6847ae)] +interface nsITreeBoxObject : nsISupports +{ + /** + * Obtain the columns. + */ + readonly attribute nsITreeColumns columns; + + /** + * The view that backs the tree and that supplies it with its data. + * It is dynamically settable, either using a view attribute on the + * tree tag or by setting this attribute to a new value. + */ + attribute nsITreeView view; + + /** + * Whether or not we are currently focused. + */ + attribute boolean focused; + + /** + * Obtain the treebody content node + */ + readonly attribute nsIDOMElement treeBody; + + /** + * Obtain the height of a row. + */ + readonly attribute long rowHeight; + + /** + * Obtain the width of a row. + */ + readonly attribute long rowWidth; + + /** + * Get the pixel position of the horizontal scrollbar. + */ + readonly attribute long horizontalPosition; + + /** + * Return the region for the visible parts of the selection, in device pixels. + */ + readonly attribute nsIScriptableRegion selectionRegion; + + /** + * Get the index of the first visible row. + */ + long getFirstVisibleRow(); + + /** + * Get the index of the last visible row. + */ + long getLastVisibleRow(); + + /** + * Gets the number of possible visible rows. + */ + long getPageLength(); + + /** + * Ensures that a row at a given index is visible. + */ + void ensureRowIsVisible(in long index); + + /** + * Ensures that a given cell in the tree is visible. + */ + void ensureCellIsVisible(in long row, in nsITreeColumn col); + + /** + * Scrolls such that the row at index is at the top of the visible view. + */ + void scrollToRow(in long index); + + /** + * Scroll the tree up or down by numLines lines. Positive + * values move down in the tree. Prevents scrolling off the + * end of the tree. + */ + void scrollByLines(in long numLines); + + /** + * Scroll the tree up or down by numPages pages. A page + * is considered to be the amount displayed by the tree. + * Positive values move down in the tree. Prevents scrolling + * off the end of the tree. + */ + void scrollByPages(in long numPages); + + /** + * Scrolls such that a given cell is visible (if possible) + * at the top left corner of the visible view. + */ + void scrollToCell(in long row, in nsITreeColumn col); + + /** + * Scrolls horizontally so that the specified column is + * at the left of the view (if possible). + */ + void scrollToColumn(in nsITreeColumn col); + + /** + * Scroll to a specific horizontal pixel position. + */ + void scrollToHorizontalPosition(in long horizontalPosition); + + /** + * Invalidation methods for fine-grained painting control. + */ + void invalidate(); + void invalidateColumn(in nsITreeColumn col); + void invalidateRow(in long index); + void invalidateCell(in long row, in nsITreeColumn col); + void invalidateRange(in long startIndex, in long endIndex); + void invalidateColumnRange(in long startIndex, in long endIndex, + in nsITreeColumn col); + + /** + * A hit test that can tell you what row the mouse is over. + * returns -1 for invalid mouse coordinates. + * + * The coordinate system is the client coordinate system for the + * document this boxObject lives in, and the units are CSS pixels. + */ + long getRowAt(in long x, in long y); + + /** + * A hit test that can tell you what cell the mouse is over. Row is the row index + * hit, returns -1 for invalid mouse coordinates. ColID is the column hit. + * ChildElt is the pseudoelement hit: this can have values of + * "cell", "twisty", "image", and "text". + * + * The coordinate system is the client coordinate system for the + * document this boxObject lives in, and the units are CSS pixels. + */ + void getCellAt(in long x, in long y, out long row, out nsITreeColumn col, out AString childElt); + + /** + * Find the coordinates of an element within a specific cell. + */ + void getCoordsForCellItem(in long row, in nsITreeColumn col, in AString element, + out long x, out long y, out long width, out long height); + + /** + * Determine if the text of a cell is being cropped or not. + */ + boolean isCellCropped(in long row, in nsITreeColumn col); + + /** + * The view is responsible for calling these notification methods when + * rows are added or removed. Index is the position at which the new + * rows were added or at which rows were removed. For + * non-contiguous additions/removals, this method should be called multiple times. + */ + void rowCountChanged(in long index, in long count); + + /** + * Notify the tree that the view is about to perform a batch + * update, that is, add, remove or invalidate several rows at once. + * This must be followed by calling endUpdateBatch(), otherwise the tree + * will get out of sync. + */ + void beginUpdateBatch(); + + /** + * Notify the tree that the view has completed a batch update. + */ + void endUpdateBatch(); + + /** + * Called on a theme switch to flush out the tree's style and image caches. + */ + void clearStyleAndImageCaches(); +}; diff --git a/layout/xul/tree/nsITreeColumns.idl b/layout/xul/tree/nsITreeColumns.idl new file mode 100644 index 000000000..a601f3776 --- /dev/null +++ b/layout/xul/tree/nsITreeColumns.idl @@ -0,0 +1,96 @@ +/* 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 "nsISupports.idl" + +interface nsITreeColumns; +interface nsIDOMElement; +interface nsIAtom; + +[scriptable, uuid(ae835ecf-6b32-4660-9b43-8a270df56e02)] +interface nsITreeColumn : nsISupports +{ + readonly attribute nsIDOMElement element; + + readonly attribute nsITreeColumns columns; + + readonly attribute long x; + readonly attribute long width; + + readonly attribute AString id; + [noscript] void getIdConst([shared] out wstring idConst); + [noscript] readonly attribute nsIAtom atom; + + readonly attribute long index; + + readonly attribute boolean primary; + readonly attribute boolean cycler; + readonly attribute boolean editable; + readonly attribute boolean selectable; + + const short TYPE_TEXT = 1; + const short TYPE_CHECKBOX = 2; + const short TYPE_PROGRESSMETER = 3; + const short TYPE_PASSWORD = 4; + readonly attribute short type; + + nsITreeColumn getNext(); + nsITreeColumn getPrevious(); + + void invalidate(); +}; + +interface nsITreeBoxObject; + +[scriptable, uuid(f8a8d6b4-6788-438d-9009-7142798767ab)] +interface nsITreeColumns : nsISupports +{ + /** + * The tree widget for these columns. + */ + readonly attribute nsITreeBoxObject tree; + + /** + * The number of columns. + */ + readonly attribute long count; + + /** + * An alias for count (for the benefit of scripts which treat this as an + * array). + */ + readonly attribute long length; + + /** + * Get the first/last column. + */ + nsITreeColumn getFirstColumn(); + nsITreeColumn getLastColumn(); + + /** + * Attribute based column getters. + */ + nsITreeColumn getPrimaryColumn(); + nsITreeColumn getSortedColumn(); + nsITreeColumn getKeyColumn(); + + /** + * Get the column for the given element. + */ + nsITreeColumn getColumnFor(in nsIDOMElement element); + + /** + * Parametric column getters. + */ + nsITreeColumn getNamedColumn(in AString id); + nsITreeColumn getColumnAt(in long index); + + /** + * This method is called whenever a treecol is added or removed and + * the column cache needs to be rebuilt. + */ + void invalidateColumns(); + + void restoreNaturalOrder(); +}; diff --git a/layout/xul/tree/nsITreeContentView.idl b/layout/xul/tree/nsITreeContentView.idl new file mode 100644 index 000000000..0b5d178aa --- /dev/null +++ b/layout/xul/tree/nsITreeContentView.idl @@ -0,0 +1,22 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "nsISupports.idl" + +interface nsIDOMElement; + +[scriptable, uuid(5ef62896-0c0a-41f1-bb3c-44a60f5dfdab)] +interface nsITreeContentView : nsISupports +{ + /** + * Retrieve the content item associated with the specified index. + */ + nsIDOMElement getItemAtIndex(in long index); + + /** + * Retrieve the index associated with the specified content item. + */ + long getIndexOfItem(in nsIDOMElement item); +}; diff --git a/layout/xul/tree/nsITreeSelection.idl b/layout/xul/tree/nsITreeSelection.idl new file mode 100644 index 000000000..6cc137f1e --- /dev/null +++ b/layout/xul/tree/nsITreeSelection.idl @@ -0,0 +1,130 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +interface nsITreeBoxObject; +interface nsITreeColumn; + +#include "nsISupports.idl" + +[scriptable, uuid(ab6fe746-300b-4ab4-abb9-1c0e3977874c)] +interface nsITreeSelection : nsISupports +{ + /** + * The tree widget for this selection. + */ + attribute nsITreeBoxObject tree; + + /** + * This attribute is a boolean indicating single selection. + */ + readonly attribute boolean single; + + /** + * The number of rows currently selected in this tree. + */ + readonly attribute long count; + + /** + * Indicates whether or not the row at the specified index is + * part of the selection. + */ + boolean isSelected(in long index); + + /** + * Deselect all rows and select the row at the specified index. + */ + void select(in long index); + + /** + * Perform a timed select. + */ + void timedSelect(in long index, in long delay); + + /** + * Toggle the selection state of the row at the specified index. + */ + void toggleSelect(in long index); + + /** + * Select the range specified by the indices. If augment is true, + * then we add the range to the selection without clearing out anything + * else. If augment is false, everything is cleared except for the specified range. + */ + void rangedSelect(in long startIndex, in long endIndex, in boolean augment); + + /** + * Clears the range. + */ + void clearRange(in long startIndex, in long endIndex); + + /** + * Clears the selection. + */ + void clearSelection(); + + /** + * Inverts the selection. + */ + void invertSelection(); + + /** + * Selects all rows. + */ + void selectAll(); + + /** + * Iterate the selection using these methods. + */ + long getRangeCount(); + void getRangeAt(in long i, out long min, out long max); + + /** + * Can be used to invalidate the selection. + */ + void invalidateSelection(); + + /** + * Called when the row count changes to adjust selection indices. + */ + void adjustSelection(in long index, in long count); + + /** + * This attribute is a boolean indicating whether or not the + * "select" event should fire when the selection is changed using + * one of our methods. A view can use this to temporarily suppress + * the selection while manipulating all of the indices, e.g., on + * a sort. + * Note: setting this attribute to false will fire a select event. + */ + attribute boolean selectEventsSuppressed; + + /** + * The current item (the one that gets a focus rect in addition to being + * selected). + */ + attribute long currentIndex; + + /** + * The current column. + */ + attribute nsITreeColumn currentColumn; + + /** + * The selection "pivot". This is the first item the user selected as + * part of a ranged select. + */ + readonly attribute long shiftSelectPivot; +}; + +/** + * The following interface is not scriptable and MUST NEVER BE MADE scriptable. + * Native treeselections implement it, and we use this to check whether a + * treeselection is native (and therefore suitable for use by untrusted content). + */ +[uuid(1bd59678-5cb3-4316-b246-31a91b19aabe)] +interface nsINativeTreeSelection : nsITreeSelection +{ + [noscript] void ensureNative(); +}; diff --git a/layout/xul/tree/nsITreeView.idl b/layout/xul/tree/nsITreeView.idl new file mode 100644 index 000000000..66920f81b --- /dev/null +++ b/layout/xul/tree/nsITreeView.idl @@ -0,0 +1,215 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "nsISupports.idl" + +interface nsITreeBoxObject; +interface nsITreeSelection; +interface nsITreeColumn; +interface nsIDOMDataTransfer; + +[scriptable, uuid(091116f0-0bdc-4b32-b9c8-c8d5a37cb088)] +interface nsITreeView : nsISupports +{ + /** + * The total number of rows in the tree (including the offscreen rows). + */ + readonly attribute long rowCount; + + /** + * The selection for this view. + */ + attribute nsITreeSelection selection; + + /** + * A whitespace delimited list of properties. For each property X the view + * gives back will cause the pseudoclasses ::-moz-tree-cell(x), + * ::-moz-tree-row(x), ::-moz-tree-twisty(x), ::-moz-tree-image(x), + * ::-moz-tree-cell-text(x). to be matched on the pseudoelement + * ::moz-tree-row. + */ + AString getRowProperties(in long index); + + /** + * A whitespace delimited list of properties for a given cell. Each + * property, x, that the view gives back will cause the pseudoclasses + * ::-moz-tree-cell(x), ::-moz-tree-row(x), ::-moz-tree-twisty(x), + * ::-moz-tree-image(x), ::-moz-tree-cell-text(x). to be matched on the + * cell. + */ + AString getCellProperties(in long row, in nsITreeColumn col); + + /** + * Called to get properties to paint a column background. For shading the sort + * column, etc. + */ + AString getColumnProperties(in nsITreeColumn col); + + /** + * Methods that can be used to test whether or not a twisty should be drawn, + * and if so, whether an open or closed twisty should be used. + */ + boolean isContainer(in long index); + boolean isContainerOpen(in long index); + boolean isContainerEmpty(in long index); + + /** + * isSeparator is used to determine if the row at index is a separator. + * A value of true will result in the tree drawing a horizontal separator. + * The tree uses the ::moz-tree-separator pseudoclass to draw the separator. + */ + boolean isSeparator(in long index); + + /** + * Specifies if there is currently a sort on any column. Used mostly by dragdrop + * to affect drop feedback. + */ + boolean isSorted(); + + const short DROP_BEFORE = -1; + const short DROP_ON = 0; + const short DROP_AFTER = 1; + /** + * Methods used by the drag feedback code to determine if a drag is allowable at + * the current location. To get the behavior where drops are only allowed on + * items, such as the mailNews folder pane, always return false when + * the orientation is not DROP_ON. + */ + boolean canDrop(in long index, in long orientation, in nsIDOMDataTransfer dataTransfer); + + /** + * Called when the user drops something on this view. The |orientation| param + * specifies before/on/after the given |row|. + */ + void drop(in long row, in long orientation, in nsIDOMDataTransfer dataTransfer); + + /** + * Methods used by the tree to draw thread lines in the tree. + * getParentIndex is used to obtain the index of a parent row. + * If there is no parent row, getParentIndex returns -1. + */ + long getParentIndex(in long rowIndex); + + /** + * hasNextSibling is used to determine if the row at rowIndex has a nextSibling + * that occurs *after* the index specified by afterIndex. Code that is forced + * to march down the view looking at levels can optimize the march by starting + * at afterIndex+1. + */ + boolean hasNextSibling(in long rowIndex, in long afterIndex); + + /** + * The level is an integer value that represents + * the level of indentation. It is multiplied by the width specified in the + * :moz-tree-indentation pseudoelement to compute the exact indendation. + */ + long getLevel(in long index); + + /** + * The image path for a given cell. For defining an icon for a cell. + * If the empty string is returned, the :moz-tree-image pseudoelement + * will be used. + */ + AString getImageSrc(in long row, in nsITreeColumn col); + + /** + * The progress mode for a given cell. This method is only called for + * columns of type |progressmeter|. + */ + const short PROGRESS_NORMAL = 1; + const short PROGRESS_UNDETERMINED = 2; + const short PROGRESS_NONE = 3; + long getProgressMode(in long row, in nsITreeColumn col); + + /** + * The value for a given cell. This method is only called for columns + * of type other than |text|. + */ + AString getCellValue(in long row, in nsITreeColumn col); + + /** + * The text for a given cell. If a column consists only of an image, then + * the empty string is returned. + */ + AString getCellText(in long row, in nsITreeColumn col); + + /** + * Called during initialization to link the view to the front end box object. + */ + void setTree(in nsITreeBoxObject tree); + + /** + * Called on the view when an item is opened or closed. + */ + void toggleOpenState(in long index); + + /** + * Called on the view when a header is clicked. + */ + void cycleHeader(in nsITreeColumn col); + + /** + * Should be called from a XUL onselect handler whenever the selection changes. + */ + void selectionChanged(); + + /** + * Called on the view when a cell in a non-selectable cycling column (e.g., unread/flag/etc.) is clicked. + */ + void cycleCell(in long row, in nsITreeColumn col); + + /** + * isEditable is called to ask the view if the cell contents are editable. + * A value of true will result in the tree popping up a text field when + * the user tries to inline edit the cell. + */ + boolean isEditable(in long row, in nsITreeColumn col); + + /** + * isSelectable is called to ask the view if the cell is selectable. + * This method is only called if the selection style is |cell| or |text|. + * XXXvarga shouldn't this be called isCellSelectable? + */ + boolean isSelectable(in long row, in nsITreeColumn col); + + /** + * setCellValue is called when the value of the cell has been set by the user. + * This method is only called for columns of type other than |text|. + */ + void setCellValue(in long row, in nsITreeColumn col, in AString value); + + /** + * setCellText is called when the contents of the cell have been edited by the user. + */ + void setCellText(in long row, in nsITreeColumn col, in AString value); + + /** + * A command API that can be used to invoke commands on the selection. The tree + * will automatically invoke this method when certain keys are pressed. For example, + * when the DEL key is pressed, performAction will be called with the "delete" string. + */ + void performAction(in wstring action); + + /** + * A command API that can be used to invoke commands on a specific row. + */ + void performActionOnRow(in wstring action, in long row); + + /** + * A command API that can be used to invoke commands on a specific cell. + */ + void performActionOnCell(in wstring action, in long row, in nsITreeColumn col); +}; + +/** + * The following interface is not scriptable and MUST NEVER BE MADE scriptable. + * Native treeviews implement it, and we use this to check whether a treeview + * is native (and therefore suitable for use by untrusted content). + */ +[uuid(46c90265-6553-41ae-8d39-7022e7d09145)] +interface nsINativeTreeView : nsITreeView +{ + [noscript] void ensureNative(); +}; diff --git a/layout/xul/tree/nsTreeBodyFrame.cpp b/layout/xul/tree/nsTreeBodyFrame.cpp new file mode 100644 index 000000000..deba04a36 --- /dev/null +++ b/layout/xul/tree/nsTreeBodyFrame.cpp @@ -0,0 +1,4945 @@ +/* -*- 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 "mozilla/AsyncEventDispatcher.h" +#include "mozilla/ContentEvents.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/EventDispatcher.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/gfx/PathHelpers.h" +#include "mozilla/Likely.h" +#include "mozilla/MathAlgorithms.h" +#include "mozilla/MouseEvents.h" +#include "mozilla/TextEditRules.h" + +#include "gfxUtils.h" +#include "nsAlgorithm.h" +#include "nsCOMPtr.h" +#include "nsFontMetrics.h" +#include "nsPresContext.h" +#include "nsNameSpaceManager.h" + +#include "nsTreeBodyFrame.h" +#include "nsTreeSelection.h" +#include "nsTreeImageListener.h" + +#include "nsGkAtoms.h" +#include "nsCSSAnonBoxes.h" + +#include "nsIContent.h" +#include "nsStyleContext.h" +#include "nsIBoxObject.h" +#include "nsIDOMCustomEvent.h" +#include "nsIDOMMouseEvent.h" +#include "nsIDOMElement.h" +#include "nsIDOMNodeList.h" +#include "nsIDOMDocument.h" +#include "nsIDOMXULElement.h" +#include "nsIDocument.h" +#include "mozilla/css/StyleRule.h" +#include "nsCSSRendering.h" +#include "nsIXULTemplateBuilder.h" +#include "nsXPIDLString.h" +#include "nsContainerFrame.h" +#include "nsView.h" +#include "nsViewManager.h" +#include "nsVariant.h" +#include "nsWidgetsCID.h" +#include "nsBoxFrame.h" +#include "nsIURL.h" +#include "nsBoxLayoutState.h" +#include "nsTreeContentView.h" +#include "nsTreeUtils.h" +#include "nsThemeConstants.h" +#include "nsITheme.h" +#include "imgIRequest.h" +#include "imgIContainer.h" +#include "imgILoader.h" +#include "mozilla/dom/NodeInfo.h" +#include "nsContentUtils.h" +#include "nsLayoutUtils.h" +#include "nsIScrollableFrame.h" +#include "nsDisplayList.h" +#include "mozilla/dom/TreeBoxObject.h" +#include "nsRenderingContext.h" +#include "nsIScriptableRegion.h" +#include +#include "ScrollbarActivity.h" + +#ifdef ACCESSIBILITY +#include "nsAccessibilityService.h" +#include "nsIWritablePropertyBag2.h" +#endif +#include "nsBidiUtils.h" + +using namespace mozilla; +using namespace mozilla::gfx; +using namespace mozilla::image; +using namespace mozilla::layout; + +// Function that cancels all the image requests in our cache. +void +nsTreeBodyFrame::CancelImageRequests() +{ + for (auto iter = mImageCache.Iter(); !iter.Done(); iter.Next()) { + // If our imgIRequest object was registered with the refresh driver + // then we need to deregister it. + nsTreeImageCacheEntry entry = iter.UserData(); + nsLayoutUtils::DeregisterImageRequest(PresContext(), entry.request, + nullptr); + entry.request->CancelAndForgetObserver(NS_BINDING_ABORTED); + } +} + +// +// NS_NewTreeFrame +// +// Creates a new tree frame +// +nsIFrame* +NS_NewTreeBodyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) +{ + return new (aPresShell) nsTreeBodyFrame(aContext); +} + +NS_IMPL_FRAMEARENA_HELPERS(nsTreeBodyFrame) + +NS_QUERYFRAME_HEAD(nsTreeBodyFrame) + NS_QUERYFRAME_ENTRY(nsIScrollbarMediator) + NS_QUERYFRAME_ENTRY(nsTreeBodyFrame) +NS_QUERYFRAME_TAIL_INHERITING(nsLeafBoxFrame) + +// Constructor +nsTreeBodyFrame::nsTreeBodyFrame(nsStyleContext* aContext) +:nsLeafBoxFrame(aContext), + mSlots(nullptr), + mImageCache(), + mTopRowIndex(0), + mPageLength(0), + mHorzPosition(0), + mOriginalHorzWidth(-1), + mHorzWidth(0), + mAdjustWidth(0), + mRowHeight(0), + mIndentation(0), + mStringWidth(-1), + mUpdateBatchNest(0), + mRowCount(0), + mMouseOverRow(-1), + mFocused(false), + mHasFixedRowCount(false), + mVerticalOverflow(false), + mHorizontalOverflow(false), + mReflowCallbackPosted(false), + mCheckingOverflow(false) +{ + mColumns = new nsTreeColumns(this); +} + +// Destructor +nsTreeBodyFrame::~nsTreeBodyFrame() +{ + CancelImageRequests(); + DetachImageListeners(); + delete mSlots; +} + +static void +GetBorderPadding(nsStyleContext* aContext, nsMargin& aMargin) +{ + aMargin.SizeTo(0, 0, 0, 0); + aContext->StylePadding()->GetPadding(aMargin); + aMargin += aContext->StyleBorder()->GetComputedBorder(); +} + +static void +AdjustForBorderPadding(nsStyleContext* aContext, nsRect& aRect) +{ + nsMargin borderPadding(0, 0, 0, 0); + GetBorderPadding(aContext, borderPadding); + aRect.Deflate(borderPadding); +} + +void +nsTreeBodyFrame::Init(nsIContent* aContent, + nsContainerFrame* aParent, + nsIFrame* aPrevInFlow) +{ + nsLeafBoxFrame::Init(aContent, aParent, aPrevInFlow); + + mIndentation = GetIndentation(); + mRowHeight = GetRowHeight(); + + EnsureBoxObject(); + + if (LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0) { + mScrollbarActivity = new ScrollbarActivity( + static_cast(this)); + } +} + +nsSize +nsTreeBodyFrame::GetXULMinSize(nsBoxLayoutState& aBoxLayoutState) +{ + EnsureView(); + + nsIContent* baseElement = GetBaseElement(); + + nsSize min(0,0); + int32_t desiredRows; + if (MOZ_UNLIKELY(!baseElement)) { + desiredRows = 0; + } + else if (baseElement->IsHTMLElement(nsGkAtoms::select)) { + min.width = CalcMaxRowWidth(); + nsAutoString size; + baseElement->GetAttr(kNameSpaceID_None, nsGkAtoms::size, size); + if (!size.IsEmpty()) { + nsresult err; + desiredRows = size.ToInteger(&err); + mHasFixedRowCount = true; + mPageLength = desiredRows; + } + else { + desiredRows = 1; + } + } + else { + // tree + nsAutoString rows; + baseElement->GetAttr(kNameSpaceID_None, nsGkAtoms::rows, rows); + if (!rows.IsEmpty()) { + nsresult err; + desiredRows = rows.ToInteger(&err); + mPageLength = desiredRows; + } + else { + desiredRows = 0; + } + } + + min.height = mRowHeight * desiredRows; + + AddBorderAndPadding(min); + bool widthSet, heightSet; + nsIFrame::AddXULMinSize(aBoxLayoutState, this, min, widthSet, heightSet); + + return min; +} + +nscoord +nsTreeBodyFrame::CalcMaxRowWidth() +{ + if (mStringWidth != -1) + return mStringWidth; + + if (!mView) + return 0; + + nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow); + nsMargin rowMargin(0,0,0,0); + GetBorderPadding(rowContext, rowMargin); + + nscoord rowWidth; + nsTreeColumn* col; + + nsRenderingContext rc( + PresContext()->PresShell()->CreateReferenceRenderingContext()); + + for (int32_t row = 0; row < mRowCount; ++row) { + rowWidth = 0; + + for (col = mColumns->GetFirstColumn(); col; col = col->GetNext()) { + nscoord desiredWidth, currentWidth; + nsresult rv = GetCellWidth(row, col, &rc, desiredWidth, currentWidth); + if (NS_FAILED(rv)) { + NS_NOTREACHED("invalid column"); + continue; + } + rowWidth += desiredWidth; + } + + if (rowWidth > mStringWidth) + mStringWidth = rowWidth; + } + + mStringWidth += rowMargin.left + rowMargin.right; + return mStringWidth; +} + +void +nsTreeBodyFrame::DestroyFrom(nsIFrame* aDestructRoot) +{ + if (mScrollbarActivity) { + mScrollbarActivity->Destroy(); + mScrollbarActivity = nullptr; + } + + mScrollEvent.Revoke(); + // Make sure we cancel any posted callbacks. + if (mReflowCallbackPosted) { + PresContext()->PresShell()->CancelReflowCallback(this); + mReflowCallbackPosted = false; + } + + if (mColumns) + mColumns->SetTree(nullptr); + + // Save off our info into the box object. + nsCOMPtr box(do_QueryInterface(mTreeBoxObject)); + if (box) { + if (mTopRowIndex > 0) { + nsAutoString topRowStr; topRowStr.AssignLiteral("topRow"); + nsAutoString topRow; + topRow.AppendInt(mTopRowIndex); + box->SetProperty(topRowStr.get(), topRow.get()); + } + + // Always null out the cached tree body frame. + box->ClearCachedValues(); + + mTreeBoxObject = nullptr; // Drop our ref here. + } + + if (mView) { + nsCOMPtr sel; + mView->GetSelection(getter_AddRefs(sel)); + if (sel) + sel->SetTree(nullptr); + mView->SetTree(nullptr); + mView = nullptr; + } + + nsLeafBoxFrame::DestroyFrom(aDestructRoot); +} + +void +nsTreeBodyFrame::EnsureBoxObject() +{ + if (!mTreeBoxObject) { + nsIContent* parent = GetBaseElement(); + if (parent) { + nsIDocument* nsDoc = parent->GetComposedDoc(); + if (!nsDoc) // there may be no document, if we're called from Destroy() + return; + ErrorResult ignored; + nsCOMPtr box = + nsDoc->GetBoxObjectFor(parent->AsElement(), ignored); + // Ensure that we got a native box object. + nsCOMPtr pBox = do_QueryInterface(box); + if (pBox) { + nsCOMPtr realTreeBoxObject = do_QueryInterface(pBox); + if (realTreeBoxObject) { + nsTreeBodyFrame* innerTreeBoxObject = + static_cast(realTreeBoxObject.get()) + ->GetCachedTreeBodyFrame(); + ENSURE_TRUE(!innerTreeBoxObject || innerTreeBoxObject == this); + mTreeBoxObject = realTreeBoxObject; + } + } + } + } +} + +void +nsTreeBodyFrame::EnsureView() +{ + if (!mView) { + if (PresContext()->PresShell()->IsReflowLocked()) { + if (!mReflowCallbackPosted) { + mReflowCallbackPosted = true; + PresContext()->PresShell()->PostReflowCallback(this); + } + return; + } + nsCOMPtr box = do_QueryInterface(mTreeBoxObject); + if (box) { + nsWeakFrame weakFrame(this); + nsCOMPtr treeView; + mTreeBoxObject->GetView(getter_AddRefs(treeView)); + if (treeView && weakFrame.IsAlive()) { + nsXPIDLString rowStr; + box->GetProperty(u"topRow", getter_Copies(rowStr)); + nsAutoString rowStr2(rowStr); + nsresult error; + int32_t rowIndex = rowStr2.ToInteger(&error); + + // Set our view. + SetView(treeView); + ENSURE_TRUE(weakFrame.IsAlive()); + + // Scroll to the given row. + // XXX is this optimal if we haven't laid out yet? + ScrollToRow(rowIndex); + ENSURE_TRUE(weakFrame.IsAlive()); + + // Clear out the property info for the top row, but we always keep the + // view current. + box->RemoveProperty(u"topRow"); + } + } + } +} + +void +nsTreeBodyFrame::ManageReflowCallback(const nsRect& aRect, nscoord aHorzWidth) +{ + if (!mReflowCallbackPosted && + (!aRect.IsEqualEdges(mRect) || mHorzWidth != aHorzWidth)) { + PresContext()->PresShell()->PostReflowCallback(this); + mReflowCallbackPosted = true; + mOriginalHorzWidth = mHorzWidth; + } + else if (mReflowCallbackPosted && + mHorzWidth != aHorzWidth && mOriginalHorzWidth == aHorzWidth) { + PresContext()->PresShell()->CancelReflowCallback(this); + mReflowCallbackPosted = false; + mOriginalHorzWidth = -1; + } +} + +void +nsTreeBodyFrame::SetXULBounds(nsBoxLayoutState& aBoxLayoutState, const nsRect& aRect, + bool aRemoveOverflowArea) +{ + nscoord horzWidth = CalcHorzWidth(GetScrollParts()); + ManageReflowCallback(aRect, horzWidth); + mHorzWidth = horzWidth; + + nsLeafBoxFrame::SetXULBounds(aBoxLayoutState, aRect, aRemoveOverflowArea); +} + + +bool +nsTreeBodyFrame::ReflowFinished() +{ + if (!mView) { + nsWeakFrame weakFrame(this); + EnsureView(); + NS_ENSURE_TRUE(weakFrame.IsAlive(), false); + } + if (mView) { + CalcInnerBox(); + ScrollParts parts = GetScrollParts(); + mHorzWidth = CalcHorzWidth(parts); + if (!mHasFixedRowCount) { + mPageLength = mInnerBox.height / mRowHeight; + } + + int32_t lastPageTopRow = std::max(0, mRowCount - mPageLength); + if (mTopRowIndex > lastPageTopRow) + ScrollToRowInternal(parts, lastPageTopRow); + + nsIContent *treeContent = GetBaseElement(); + if (treeContent && + treeContent->AttrValueIs(kNameSpaceID_None, + nsGkAtoms::keepcurrentinview, + nsGkAtoms::_true, eCaseMatters)) { + // make sure that the current selected item is still + // visible after the tree changes size. + nsCOMPtr sel; + mView->GetSelection(getter_AddRefs(sel)); + if (sel) { + int32_t currentIndex; + sel->GetCurrentIndex(¤tIndex); + if (currentIndex != -1) + EnsureRowIsVisibleInternal(parts, currentIndex); + } + } + + if (!FullScrollbarsUpdate(false)) { + return false; + } + } + + mReflowCallbackPosted = false; + return false; +} + +void +nsTreeBodyFrame::ReflowCallbackCanceled() +{ + mReflowCallbackPosted = false; +} + +nsresult +nsTreeBodyFrame::GetView(nsITreeView * *aView) +{ + *aView = nullptr; + nsWeakFrame weakFrame(this); + EnsureView(); + NS_ENSURE_STATE(weakFrame.IsAlive()); + NS_IF_ADDREF(*aView = mView); + return NS_OK; +} + +nsresult +nsTreeBodyFrame::SetView(nsITreeView * aView) +{ + // First clear out the old view. + if (mView) { + nsCOMPtr sel; + mView->GetSelection(getter_AddRefs(sel)); + if (sel) + sel->SetTree(nullptr); + mView->SetTree(nullptr); + + // Only reset the top row index and delete the columns if we had an old non-null view. + mTopRowIndex = 0; + } + + // Tree, meet the view. + mView = aView; + + // Changing the view causes us to refetch our data. This will + // necessarily entail a full invalidation of the tree. + Invalidate(); + + nsIContent *treeContent = GetBaseElement(); + if (treeContent) { +#ifdef ACCESSIBILITY + nsAccessibilityService* accService = nsIPresShell::AccService(); + if (accService) + accService->TreeViewChanged(PresContext()->GetPresShell(), treeContent, mView); +#endif + FireDOMEvent(NS_LITERAL_STRING("TreeViewChanged"), treeContent); + } + + if (mView) { + // Give the view a new empty selection object to play with, but only if it + // doesn't have one already. + nsCOMPtr sel; + mView->GetSelection(getter_AddRefs(sel)); + if (sel) { + sel->SetTree(mTreeBoxObject); + } + else { + NS_NewTreeSelection(mTreeBoxObject, getter_AddRefs(sel)); + mView->SetSelection(sel); + } + + // View, meet the tree. + nsWeakFrame weakFrame(this); + mView->SetTree(mTreeBoxObject); + NS_ENSURE_STATE(weakFrame.IsAlive()); + mView->GetRowCount(&mRowCount); + + if (!PresContext()->PresShell()->IsReflowLocked()) { + // The scrollbar will need to be updated. + FullScrollbarsUpdate(false); + } else if (!mReflowCallbackPosted) { + mReflowCallbackPosted = true; + PresContext()->PresShell()->PostReflowCallback(this); + } + } + + return NS_OK; +} + +nsresult +nsTreeBodyFrame::SetFocused(bool aFocused) +{ + if (mFocused != aFocused) { + mFocused = aFocused; + if (mView) { + nsCOMPtr sel; + mView->GetSelection(getter_AddRefs(sel)); + if (sel) + sel->InvalidateSelection(); + } + } + return NS_OK; +} + +nsresult +nsTreeBodyFrame::GetTreeBody(nsIDOMElement** aElement) +{ + //NS_ASSERTION(mContent, "no content, see bug #104878"); + if (!mContent) + return NS_ERROR_NULL_POINTER; + + return mContent->QueryInterface(NS_GET_IID(nsIDOMElement), (void**)aElement); +} + +int32_t +nsTreeBodyFrame::RowHeight() const +{ + return nsPresContext::AppUnitsToIntCSSPixels(mRowHeight); +} + +int32_t +nsTreeBodyFrame::RowWidth() +{ + return nsPresContext::AppUnitsToIntCSSPixels(CalcHorzWidth(GetScrollParts())); +} + +int32_t +nsTreeBodyFrame::GetHorizontalPosition() const +{ + return nsPresContext::AppUnitsToIntCSSPixels(mHorzPosition); +} + +nsresult +nsTreeBodyFrame::GetSelectionRegion(nsIScriptableRegion **aRegion) +{ + *aRegion = nullptr; + + nsCOMPtr selection; + mView->GetSelection(getter_AddRefs(selection)); + NS_ENSURE_TRUE(selection, NS_OK); + + nsCOMPtr region = do_CreateInstance("@mozilla.org/gfx/region;1"); + NS_ENSURE_TRUE(region, NS_ERROR_FAILURE); + region->Init(); + + RefPtr presContext = PresContext(); + nsIntRect rect = mRect.ToOutsidePixels(presContext->AppUnitsPerCSSPixel()); + + nsIFrame* rootFrame = presContext->PresShell()->GetRootFrame(); + nsPoint origin = GetOffsetTo(rootFrame); + + // iterate through the visible rows and add the selected ones to the + // drag region + int32_t x = nsPresContext::AppUnitsToIntCSSPixels(origin.x); + int32_t y = nsPresContext::AppUnitsToIntCSSPixels(origin.y); + int32_t top = y; + int32_t end = LastVisibleRow(); + int32_t rowHeight = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight); + for (int32_t i = mTopRowIndex; i <= end; i++) { + bool isSelected; + selection->IsSelected(i, &isSelected); + if (isSelected) + region->UnionRect(x, y, rect.width, rowHeight); + y += rowHeight; + } + + // clip to the tree boundary in case one row extends past it + region->IntersectRect(x, top, rect.width, rect.height); + + region.forget(aRegion); + return NS_OK; +} + +nsresult +nsTreeBodyFrame::Invalidate() +{ + if (mUpdateBatchNest) + return NS_OK; + + InvalidateFrame(); + + return NS_OK; +} + +nsresult +nsTreeBodyFrame::InvalidateColumn(nsITreeColumn* aCol) +{ + if (mUpdateBatchNest) + return NS_OK; + + RefPtr col = GetColumnImpl(aCol); + if (!col) + return NS_ERROR_INVALID_ARG; + +#ifdef ACCESSIBILITY + if (nsIPresShell::IsAccessibilityActive()) + FireInvalidateEvent(-1, -1, aCol, aCol); +#endif + + nsRect columnRect; + nsresult rv = col->GetRect(this, mInnerBox.y, mInnerBox.height, &columnRect); + NS_ENSURE_SUCCESS(rv, rv); + + // When false then column is out of view + if (OffsetForHorzScroll(columnRect, true)) + InvalidateFrameWithRect(columnRect); + + return NS_OK; +} + +nsresult +nsTreeBodyFrame::InvalidateRow(int32_t aIndex) +{ + if (mUpdateBatchNest) + return NS_OK; + +#ifdef ACCESSIBILITY + if (nsIPresShell::IsAccessibilityActive()) + FireInvalidateEvent(aIndex, aIndex, nullptr, nullptr); +#endif + + aIndex -= mTopRowIndex; + if (aIndex < 0 || aIndex > mPageLength) + return NS_OK; + + nsRect rowRect(mInnerBox.x, mInnerBox.y+mRowHeight*aIndex, mInnerBox.width, mRowHeight); + InvalidateFrameWithRect(rowRect); + + return NS_OK; +} + +nsresult +nsTreeBodyFrame::InvalidateCell(int32_t aIndex, nsITreeColumn* aCol) +{ + if (mUpdateBatchNest) + return NS_OK; + +#ifdef ACCESSIBILITY + if (nsIPresShell::IsAccessibilityActive()) + FireInvalidateEvent(aIndex, aIndex, aCol, aCol); +#endif + + aIndex -= mTopRowIndex; + if (aIndex < 0 || aIndex > mPageLength) + return NS_OK; + + RefPtr col = GetColumnImpl(aCol); + if (!col) + return NS_ERROR_INVALID_ARG; + + nsRect cellRect; + nsresult rv = col->GetRect(this, mInnerBox.y+mRowHeight*aIndex, mRowHeight, + &cellRect); + NS_ENSURE_SUCCESS(rv, rv); + + if (OffsetForHorzScroll(cellRect, true)) + InvalidateFrameWithRect(cellRect); + + return NS_OK; +} + +nsresult +nsTreeBodyFrame::InvalidateRange(int32_t aStart, int32_t aEnd) +{ + if (mUpdateBatchNest) + return NS_OK; + + if (aStart == aEnd) + return InvalidateRow(aStart); + + int32_t last = LastVisibleRow(); + if (aStart > aEnd || aEnd < mTopRowIndex || aStart > last) + return NS_OK; + + if (aStart < mTopRowIndex) + aStart = mTopRowIndex; + + if (aEnd > last) + aEnd = last; + +#ifdef ACCESSIBILITY + if (nsIPresShell::IsAccessibilityActive()) { + int32_t end = + mRowCount > 0 ? ((mRowCount <= aEnd) ? mRowCount - 1 : aEnd) : 0; + FireInvalidateEvent(aStart, end, nullptr, nullptr); + } +#endif + + nsRect rangeRect(mInnerBox.x, mInnerBox.y+mRowHeight*(aStart-mTopRowIndex), mInnerBox.width, mRowHeight*(aEnd-aStart+1)); + InvalidateFrameWithRect(rangeRect); + + return NS_OK; +} + +nsresult +nsTreeBodyFrame::InvalidateColumnRange(int32_t aStart, int32_t aEnd, nsITreeColumn* aCol) +{ + if (mUpdateBatchNest) + return NS_OK; + + RefPtr col = GetColumnImpl(aCol); + if (!col) + return NS_ERROR_INVALID_ARG; + + if (aStart == aEnd) + return InvalidateCell(aStart, col); + + int32_t last = LastVisibleRow(); + if (aStart > aEnd || aEnd < mTopRowIndex || aStart > last) + return NS_OK; + + if (aStart < mTopRowIndex) + aStart = mTopRowIndex; + + if (aEnd > last) + aEnd = last; + +#ifdef ACCESSIBILITY + if (nsIPresShell::IsAccessibilityActive()) { + int32_t end = + mRowCount > 0 ? ((mRowCount <= aEnd) ? mRowCount - 1 : aEnd) : 0; + FireInvalidateEvent(aStart, end, aCol, aCol); + } +#endif + + nsRect rangeRect; + nsresult rv = col->GetRect(this, + mInnerBox.y+mRowHeight*(aStart-mTopRowIndex), + mRowHeight*(aEnd-aStart+1), + &rangeRect); + NS_ENSURE_SUCCESS(rv, rv); + + InvalidateFrameWithRect(rangeRect); + + return NS_OK; +} + +static void +FindScrollParts(nsIFrame* aCurrFrame, nsTreeBodyFrame::ScrollParts* aResult) +{ + if (!aResult->mColumnsScrollFrame) { + nsIScrollableFrame* f = do_QueryFrame(aCurrFrame); + if (f) { + aResult->mColumnsFrame = aCurrFrame; + aResult->mColumnsScrollFrame = f; + } + } + + nsScrollbarFrame *sf = do_QueryFrame(aCurrFrame); + if (sf) { + if (!aCurrFrame->IsXULHorizontal()) { + if (!aResult->mVScrollbar) { + aResult->mVScrollbar = sf; + } + } else { + if (!aResult->mHScrollbar) { + aResult->mHScrollbar = sf; + } + } + // don't bother searching inside a scrollbar + return; + } + + nsIFrame* child = aCurrFrame->PrincipalChildList().FirstChild(); + while (child && + !child->GetContent()->IsRootOfNativeAnonymousSubtree() && + (!aResult->mVScrollbar || !aResult->mHScrollbar || + !aResult->mColumnsScrollFrame)) { + FindScrollParts(child, aResult); + child = child->GetNextSibling(); + } +} + +nsTreeBodyFrame::ScrollParts nsTreeBodyFrame::GetScrollParts() +{ + ScrollParts result = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; + nsIContent* baseElement = GetBaseElement(); + nsIFrame* treeFrame = + baseElement ? baseElement->GetPrimaryFrame() : nullptr; + if (treeFrame) { + // The way we do this, searching through the entire frame subtree, is pretty + // dumb! We should know where these frames are. + FindScrollParts(treeFrame, &result); + if (result.mHScrollbar) { + result.mHScrollbar->SetScrollbarMediatorContent(GetContent()); + nsIFrame* f = do_QueryFrame(result.mHScrollbar); + result.mHScrollbarContent = f->GetContent(); + } + if (result.mVScrollbar) { + result.mVScrollbar->SetScrollbarMediatorContent(GetContent()); + nsIFrame* f = do_QueryFrame(result.mVScrollbar); + result.mVScrollbarContent = f->GetContent(); + } + } + return result; +} + +void +nsTreeBodyFrame::UpdateScrollbars(const ScrollParts& aParts) +{ + nscoord rowHeightAsPixels = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight); + + nsWeakFrame weakFrame(this); + + if (aParts.mVScrollbar) { + nsAutoString curPos; + curPos.AppendInt(mTopRowIndex*rowHeightAsPixels); + aParts.mVScrollbarContent-> + SetAttr(kNameSpaceID_None, nsGkAtoms::curpos, curPos, true); + // 'this' might be deleted here + } + + if (weakFrame.IsAlive() && aParts.mHScrollbar) { + nsAutoString curPos; + curPos.AppendInt(mHorzPosition); + aParts.mHScrollbarContent-> + SetAttr(kNameSpaceID_None, nsGkAtoms::curpos, curPos, true); + // 'this' might be deleted here + } + + if (weakFrame.IsAlive() && mScrollbarActivity) { + mScrollbarActivity->ActivityOccurred(); + } +} + +void +nsTreeBodyFrame::CheckOverflow(const ScrollParts& aParts) +{ + bool verticalOverflowChanged = false; + bool horizontalOverflowChanged = false; + + if (!mVerticalOverflow && mRowCount > mPageLength) { + mVerticalOverflow = true; + verticalOverflowChanged = true; + } + else if (mVerticalOverflow && mRowCount <= mPageLength) { + mVerticalOverflow = false; + verticalOverflowChanged = true; + } + + if (aParts.mColumnsFrame) { + nsRect bounds = aParts.mColumnsFrame->GetRect(); + if (bounds.width != 0) { + /* Ignore overflows that are less than half a pixel. Yes these happen + all over the place when flex boxes are compressed real small. + Probably a result of a rounding errors somewhere in the layout code. */ + bounds.width += nsPresContext::CSSPixelsToAppUnits(0.5f); + if (!mHorizontalOverflow && bounds.width < mHorzWidth) { + mHorizontalOverflow = true; + horizontalOverflowChanged = true; + } else if (mHorizontalOverflow && bounds.width >= mHorzWidth) { + mHorizontalOverflow = false; + horizontalOverflowChanged = true; + } + } + } + + nsWeakFrame weakFrame(this); + + RefPtr presContext = PresContext(); + nsCOMPtr presShell = presContext->GetPresShell(); + nsCOMPtr content = mContent; + + if (verticalOverflowChanged) { + InternalScrollPortEvent event(true, + mVerticalOverflow ? eScrollPortOverflow : eScrollPortUnderflow, + nullptr); + event.mOrient = InternalScrollPortEvent::eVertical; + EventDispatcher::Dispatch(content, presContext, &event); + } + + if (horizontalOverflowChanged) { + InternalScrollPortEvent event(true, + mHorizontalOverflow ? eScrollPortOverflow : eScrollPortUnderflow, + nullptr); + event.mOrient = InternalScrollPortEvent::eHorizontal; + EventDispatcher::Dispatch(content, presContext, &event); + } + + // The synchronous event dispatch above can trigger reflow notifications. + // Flush those explicitly now, so that we can guard against potential infinite + // recursion. See bug 905909. + if (!weakFrame.IsAlive()) { + return; + } + NS_ASSERTION(!mCheckingOverflow, "mCheckingOverflow should not already be set"); + // Don't use AutoRestore since we want to not touch mCheckingOverflow if we fail + // the weakFrame.IsAlive() check below + mCheckingOverflow = true; + presShell->FlushPendingNotifications(Flush_Layout); + if (!weakFrame.IsAlive()) { + return; + } + mCheckingOverflow = false; +} + +void +nsTreeBodyFrame::InvalidateScrollbars(const ScrollParts& aParts, nsWeakFrame& aWeakColumnsFrame) +{ + if (mUpdateBatchNest || !mView) + return; + nsWeakFrame weakFrame(this); + + if (aParts.mVScrollbar) { + // Do Vertical Scrollbar + nsAutoString maxposStr; + + nscoord rowHeightAsPixels = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight); + + int32_t size = rowHeightAsPixels * (mRowCount > mPageLength ? mRowCount - mPageLength : 0); + maxposStr.AppendInt(size); + aParts.mVScrollbarContent-> + SetAttr(kNameSpaceID_None, nsGkAtoms::maxpos, maxposStr, true); + ENSURE_TRUE(weakFrame.IsAlive()); + + // Also set our page increment and decrement. + nscoord pageincrement = mPageLength*rowHeightAsPixels; + nsAutoString pageStr; + pageStr.AppendInt(pageincrement); + aParts.mVScrollbarContent-> + SetAttr(kNameSpaceID_None, nsGkAtoms::pageincrement, pageStr, true); + ENSURE_TRUE(weakFrame.IsAlive()); + } + + if (aParts.mHScrollbar && aParts.mColumnsFrame && aWeakColumnsFrame.IsAlive()) { + // And now Horizontal scrollbar + nsRect bounds = aParts.mColumnsFrame->GetRect(); + nsAutoString maxposStr; + + maxposStr.AppendInt(mHorzWidth > bounds.width ? mHorzWidth - bounds.width : 0); + aParts.mHScrollbarContent-> + SetAttr(kNameSpaceID_None, nsGkAtoms::maxpos, maxposStr, true); + ENSURE_TRUE(weakFrame.IsAlive()); + + nsAutoString pageStr; + pageStr.AppendInt(bounds.width); + aParts.mHScrollbarContent-> + SetAttr(kNameSpaceID_None, nsGkAtoms::pageincrement, pageStr, true); + ENSURE_TRUE(weakFrame.IsAlive()); + + pageStr.Truncate(); + pageStr.AppendInt(nsPresContext::CSSPixelsToAppUnits(16)); + aParts.mHScrollbarContent-> + SetAttr(kNameSpaceID_None, nsGkAtoms::increment, pageStr, true); + } + + if (weakFrame.IsAlive() && mScrollbarActivity) { + mScrollbarActivity->ActivityOccurred(); + } +} + +// Takes client x/y in pixels, converts them to appunits, and converts into +// values relative to this nsTreeBodyFrame frame. +nsPoint +nsTreeBodyFrame::AdjustClientCoordsToBoxCoordSpace(int32_t aX, int32_t aY) +{ + nsPoint point(nsPresContext::CSSPixelsToAppUnits(aX), + nsPresContext::CSSPixelsToAppUnits(aY)); + + nsPresContext* presContext = PresContext(); + point -= GetOffsetTo(presContext->GetPresShell()->GetRootFrame()); + + // Adjust by the inner box coords, so that we're in the inner box's + // coordinate space. + point -= mInnerBox.TopLeft(); + return point; +} // AdjustClientCoordsToBoxCoordSpace + +nsresult +nsTreeBodyFrame::GetRowAt(int32_t aX, int32_t aY, int32_t* _retval) +{ + if (!mView) + return NS_OK; + + nsPoint point = AdjustClientCoordsToBoxCoordSpace(aX, aY); + + // Check if the coordinates are above our visible space. + if (point.y < 0) { + *_retval = -1; + return NS_OK; + } + + *_retval = GetRowAt(point.x, point.y); + + return NS_OK; +} + +nsresult +nsTreeBodyFrame::GetCellAt(int32_t aX, int32_t aY, int32_t* aRow, nsITreeColumn** aCol, + nsACString& aChildElt) +{ + if (!mView) + return NS_OK; + + nsPoint point = AdjustClientCoordsToBoxCoordSpace(aX, aY); + + // Check if the coordinates are above our visible space. + if (point.y < 0) { + *aRow = -1; + return NS_OK; + } + + nsTreeColumn* col; + nsIAtom* child; + GetCellAt(point.x, point.y, aRow, &col, &child); + + if (col) { + NS_ADDREF(*aCol = col); + if (child == nsCSSAnonBoxes::moztreecell) + aChildElt.AssignLiteral("cell"); + else if (child == nsCSSAnonBoxes::moztreetwisty) + aChildElt.AssignLiteral("twisty"); + else if (child == nsCSSAnonBoxes::moztreeimage) + aChildElt.AssignLiteral("image"); + else if (child == nsCSSAnonBoxes::moztreecelltext) + aChildElt.AssignLiteral("text"); + } + + return NS_OK; +} + + +// +// GetCoordsForCellItem +// +// Find the x/y location and width/height (all in PIXELS) of the given object +// in the given column. +// +// XXX IMPORTANT XXX: +// Hyatt says in the bug for this, that the following needs to be done: +// (1) You need to deal with overflow when computing cell rects. See other column +// iteration examples... if you don't deal with this, you'll mistakenly extend the +// cell into the scrollbar's rect. +// +// (2) You are adjusting the cell rect by the *row" border padding. That's +// wrong. You need to first adjust a row rect by its border/padding, and then the +// cell rect fits inside the adjusted row rect. It also can have border/padding +// as well as margins. The vertical direction isn't that important, but you need +// to get the horizontal direction right. +// +// (3) GetImageSize() does not include margins (but it does include border/padding). +// You need to make sure to add in the image's margins as well. +// +nsresult +nsTreeBodyFrame::GetCoordsForCellItem(int32_t aRow, nsITreeColumn* aCol, const nsACString& aElement, + int32_t *aX, int32_t *aY, int32_t *aWidth, int32_t *aHeight) +{ + *aX = 0; + *aY = 0; + *aWidth = 0; + *aHeight = 0; + + bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; + nscoord currX = mInnerBox.x - mHorzPosition; + + // The Rect for the requested item. + nsRect theRect; + + nsPresContext* presContext = PresContext(); + + for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol; currCol = currCol->GetNext()) { + + // The Rect for the current cell. + nscoord colWidth; +#ifdef DEBUG + nsresult rv = +#endif + currCol->GetWidthInTwips(this, &colWidth); + NS_ASSERTION(NS_SUCCEEDED(rv), "invalid column"); + + nsRect cellRect(currX, mInnerBox.y + mRowHeight * (aRow - mTopRowIndex), + colWidth, mRowHeight); + + // Check the ID of the current column to see if it matches. If it doesn't + // increment the current X value and continue to the next column. + if (currCol != aCol) { + currX += cellRect.width; + continue; + } + // Now obtain the properties for our cell. + PrefillPropertyArray(aRow, currCol); + + nsAutoString properties; + mView->GetCellProperties(aRow, currCol, properties); + nsTreeUtils::TokenizeProperties(properties, mScratchArray); + + nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow); + + // We don't want to consider any of the decorations that may be present + // on the current row, so we have to deflate the rect by the border and + // padding and offset its left and top coordinates appropriately. + AdjustForBorderPadding(rowContext, cellRect); + + nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell); + + NS_NAMED_LITERAL_CSTRING(cell, "cell"); + if (currCol->IsCycler() || cell.Equals(aElement)) { + // If the current Column is a Cycler, then the Rect is just the cell - the margins. + // Similarly, if we're just being asked for the cell rect, provide it. + + theRect = cellRect; + nsMargin cellMargin; + cellContext->StyleMargin()->GetMargin(cellMargin); + theRect.Deflate(cellMargin); + break; + } + + // Since we're not looking for the cell, and since the cell isn't a cycler, + // we're looking for some subcomponent, and now we need to subtract the + // borders and padding of the cell from cellRect so this does not + // interfere with our computations. + AdjustForBorderPadding(cellContext, cellRect); + + nsRenderingContext rc( + presContext->PresShell()->CreateReferenceRenderingContext()); + + // Now we'll start making our way across the cell, starting at the edge of + // the cell and proceeding until we hit the right edge. |cellX| is the + // working X value that we will increment as we crawl from left to right. + nscoord cellX = cellRect.x; + nscoord remainWidth = cellRect.width; + + if (currCol->IsPrimary()) { + // If the current Column is a Primary, then we need to take into account the indentation + // and possibly a twisty. + + // The amount of indentation is the indentation width (|mIndentation|) by the level. + int32_t level; + mView->GetLevel(aRow, &level); + if (!isRTL) + cellX += mIndentation * level; + remainWidth -= mIndentation * level; + + // Find the twisty rect by computing its size. + nsRect imageRect; + nsRect twistyRect(cellRect); + nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty); + GetTwistyRect(aRow, currCol, imageRect, twistyRect, presContext, + twistyContext); + + if (NS_LITERAL_CSTRING("twisty").Equals(aElement)) { + // If we're looking for the twisty Rect, just return the size + theRect = twistyRect; + break; + } + + // Now we need to add in the margins of the twisty element, so that we + // can find the offset of the next element in the cell. + nsMargin twistyMargin; + twistyContext->StyleMargin()->GetMargin(twistyMargin); + twistyRect.Inflate(twistyMargin); + + // Adjust our working X value with the twisty width (image size, margins, + // borders, padding. + if (!isRTL) + cellX += twistyRect.width; + } + + // Cell Image + nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage); + + nsRect imageSize = GetImageSize(aRow, currCol, false, imageContext); + if (NS_LITERAL_CSTRING("image").Equals(aElement)) { + theRect = imageSize; + theRect.x = cellX; + theRect.y = cellRect.y; + break; + } + + // Add in the margins of the cell image. + nsMargin imageMargin; + imageContext->StyleMargin()->GetMargin(imageMargin); + imageSize.Inflate(imageMargin); + + // Increment cellX by the image width + if (!isRTL) + cellX += imageSize.width; + + // Cell Text + nsAutoString cellText; + mView->GetCellText(aRow, currCol, cellText); + // We're going to measure this text so we need to ensure bidi is enabled if + // necessary + CheckTextForBidi(cellText); + + // Create a scratch rect to represent the text rectangle, with the current + // X and Y coords, and a guess at the width and height. The width is the + // remaining width we have left to traverse in the cell, which will be the + // widest possible value for the text rect, and the row height. + nsRect textRect(cellX, cellRect.y, remainWidth, cellRect.height); + + // Measure the width of the text. If the width of the text is greater than + // the remaining width available, then we just assume that the text has + // been cropped and use the remaining rect as the text Rect. Otherwise, + // we add in borders and padding to the text dimension and give that back. + nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext); + + RefPtr fm = + nsLayoutUtils::GetFontMetricsForStyleContext(textContext); + nscoord height = fm->MaxHeight(); + + nsMargin textMargin; + textContext->StyleMargin()->GetMargin(textMargin); + textRect.Deflate(textMargin); + + // Center the text. XXX Obey vertical-align style prop? + if (height < textRect.height) { + textRect.y += (textRect.height - height) / 2; + textRect.height = height; + } + + nsMargin bp(0,0,0,0); + GetBorderPadding(textContext, bp); + textRect.height += bp.top + bp.bottom; + + AdjustForCellText(cellText, aRow, currCol, rc, *fm, textRect); + + theRect = textRect; + } + + if (isRTL) + theRect.x = mInnerBox.width - theRect.x - theRect.width; + + *aX = nsPresContext::AppUnitsToIntCSSPixels(theRect.x); + *aY = nsPresContext::AppUnitsToIntCSSPixels(theRect.y); + *aWidth = nsPresContext::AppUnitsToIntCSSPixels(theRect.width); + *aHeight = nsPresContext::AppUnitsToIntCSSPixels(theRect.height); + + return NS_OK; +} + +int32_t +nsTreeBodyFrame::GetRowAt(int32_t aX, int32_t aY) +{ + // Now just mod by our total inner box height and add to our top row index. + int32_t row = (aY/mRowHeight)+mTopRowIndex; + + // Check if the coordinates are below our visible space (or within our visible + // space but below any row). + if (row > mTopRowIndex + mPageLength || row >= mRowCount) + return -1; + + return row; +} + +void +nsTreeBodyFrame::CheckTextForBidi(nsAutoString& aText) +{ + // We could check to see whether the prescontext already has bidi enabled, + // but usually it won't, so it's probably faster to avoid the call to + // GetPresContext() when it's not needed. + if (HasRTLChars(aText)) { + PresContext()->SetBidiEnabled(); + } +} + +void +nsTreeBodyFrame::AdjustForCellText(nsAutoString& aText, + int32_t aRowIndex, nsTreeColumn* aColumn, + nsRenderingContext& aRenderingContext, + nsFontMetrics& aFontMetrics, + nsRect& aTextRect) +{ + NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed"); + + DrawTarget* drawTarget = aRenderingContext.GetDrawTarget(); + + nscoord maxWidth = aTextRect.width; + bool widthIsGreater = nsLayoutUtils::StringWidthIsGreaterThan(aText, + aFontMetrics, + drawTarget, + maxWidth); + + if (aColumn->Overflow()) { + DebugOnly rv; + nsTreeColumn* nextColumn = aColumn->GetNext(); + while (nextColumn && widthIsGreater) { + while (nextColumn) { + nscoord width; + rv = nextColumn->GetWidthInTwips(this, &width); + NS_ASSERTION(NS_SUCCEEDED(rv), "nextColumn is invalid"); + + if (width != 0) + break; + + nextColumn = nextColumn->GetNext(); + } + + if (nextColumn) { + nsAutoString nextText; + mView->GetCellText(aRowIndex, nextColumn, nextText); + // We don't measure or draw this text so no need to check it for + // bidi-ness + + if (nextText.Length() == 0) { + nscoord width; + rv = nextColumn->GetWidthInTwips(this, &width); + NS_ASSERTION(NS_SUCCEEDED(rv), "nextColumn is invalid"); + + maxWidth += width; + widthIsGreater = nsLayoutUtils::StringWidthIsGreaterThan(aText, + aFontMetrics, + drawTarget, + maxWidth); + + nextColumn = nextColumn->GetNext(); + } + else { + nextColumn = nullptr; + } + } + } + } + + nscoord width; + if (widthIsGreater) { + // See if the width is even smaller than the ellipsis + // If so, clear the text completely. + const nsDependentString& kEllipsis = nsContentUtils::GetLocalizedEllipsis(); + aFontMetrics.SetTextRunRTL(false); + nscoord ellipsisWidth = + nsLayoutUtils::AppUnitWidthOfString(kEllipsis, aFontMetrics, drawTarget); + + width = maxWidth; + if (ellipsisWidth > width) + aText.SetLength(0); + else if (ellipsisWidth == width) + aText.Assign(kEllipsis); + else { + // We will be drawing an ellipsis, thank you very much. + // Subtract out the required width of the ellipsis. + // This is the total remaining width we have to play with. + width -= ellipsisWidth; + + // Now we crop. + switch (aColumn->GetCropStyle()) { + default: + case 0: { + // Crop right. + nscoord cwidth; + nscoord twidth = 0; + uint32_t length = aText.Length(); + uint32_t i; + for (i = 0; i < length; ++i) { + char16_t ch = aText[i]; + // XXX this is horrible and doesn't handle clusters + cwidth = nsLayoutUtils::AppUnitWidthOfString(ch, aFontMetrics, + drawTarget); + if (twidth + cwidth > width) + break; + twidth += cwidth; + } + aText.Truncate(i); + aText.Append(kEllipsis); + } + break; + + case 2: { + // Crop left. + nscoord cwidth; + nscoord twidth = 0; + int32_t length = aText.Length(); + int32_t i; + for (i=length-1; i >= 0; --i) { + char16_t ch = aText[i]; + cwidth = nsLayoutUtils::AppUnitWidthOfString(ch, aFontMetrics, + drawTarget); + if (twidth + cwidth > width) + break; + twidth += cwidth; + } + + nsAutoString copy; + aText.Right(copy, length-1-i); + aText.Assign(kEllipsis); + aText += copy; + } + break; + + case 1: + { + // Crop center. + nsAutoString leftStr, rightStr; + nscoord cwidth, twidth = 0; + int32_t length = aText.Length(); + int32_t rightPos = length - 1; + for (int32_t leftPos = 0; leftPos < rightPos; ++leftPos) { + char16_t ch = aText[leftPos]; + cwidth = nsLayoutUtils::AppUnitWidthOfString(ch, aFontMetrics, + drawTarget); + twidth += cwidth; + if (twidth > width) + break; + leftStr.Append(ch); + + ch = aText[rightPos]; + cwidth = nsLayoutUtils::AppUnitWidthOfString(ch, aFontMetrics, + drawTarget); + twidth += cwidth; + if (twidth > width) + break; + rightStr.Insert(ch, 0); + --rightPos; + } + aText = leftStr; + aText.Append(kEllipsis); + aText += rightStr; + } + break; + } + } + } + + width = nsLayoutUtils::AppUnitWidthOfStringBidi(aText, this, aFontMetrics, + aRenderingContext); + + switch (aColumn->GetTextAlignment()) { + case NS_STYLE_TEXT_ALIGN_RIGHT: { + aTextRect.x += aTextRect.width - width; + } + break; + case NS_STYLE_TEXT_ALIGN_CENTER: { + aTextRect.x += (aTextRect.width - width) / 2; + } + break; + } + + aTextRect.width = width; +} + +nsIAtom* +nsTreeBodyFrame::GetItemWithinCellAt(nscoord aX, const nsRect& aCellRect, + int32_t aRowIndex, + nsTreeColumn* aColumn) +{ + NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed"); + + // Obtain the properties for our cell. + PrefillPropertyArray(aRowIndex, aColumn); + nsAutoString properties; + mView->GetCellProperties(aRowIndex, aColumn, properties); + nsTreeUtils::TokenizeProperties(properties, mScratchArray); + + // Resolve style for the cell. + nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell); + + // Obtain the margins for the cell and then deflate our rect by that + // amount. The cell is assumed to be contained within the deflated rect. + nsRect cellRect(aCellRect); + nsMargin cellMargin; + cellContext->StyleMargin()->GetMargin(cellMargin); + cellRect.Deflate(cellMargin); + + // Adjust the rect for its border and padding. + AdjustForBorderPadding(cellContext, cellRect); + + if (aX < cellRect.x || aX >= cellRect.x + cellRect.width) { + // The user clicked within the cell's margins/borders/padding. This constitutes a click on the cell. + return nsCSSAnonBoxes::moztreecell; + } + + nscoord currX = cellRect.x; + nscoord remainingWidth = cellRect.width; + + // Handle right alignment hit testing. + bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; + + nsPresContext* presContext = PresContext(); + nsRenderingContext rc( + presContext->PresShell()->CreateReferenceRenderingContext()); + + if (aColumn->IsPrimary()) { + // If we're the primary column, we have indentation and a twisty. + int32_t level; + mView->GetLevel(aRowIndex, &level); + + if (!isRTL) + currX += mIndentation*level; + remainingWidth -= mIndentation*level; + + if ((isRTL && aX > currX + remainingWidth) || + (!isRTL && aX < currX)) { + // The user clicked within the indentation. + return nsCSSAnonBoxes::moztreecell; + } + + // Always leave space for the twisty. + nsRect twistyRect(currX, cellRect.y, remainingWidth, cellRect.height); + bool hasTwisty = false; + bool isContainer = false; + mView->IsContainer(aRowIndex, &isContainer); + if (isContainer) { + bool isContainerEmpty = false; + mView->IsContainerEmpty(aRowIndex, &isContainerEmpty); + if (!isContainerEmpty) + hasTwisty = true; + } + + // Resolve style for the twisty. + nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty); + + nsRect imageSize; + GetTwistyRect(aRowIndex, aColumn, imageSize, twistyRect, presContext, + twistyContext); + + // We will treat a click as hitting the twisty if it happens on the margins, borders, padding, + // or content of the twisty object. By allowing a "slop" into the margin, we make it a little + // bit easier for a user to hit the twisty. (We don't want to be too picky here.) + nsMargin twistyMargin; + twistyContext->StyleMargin()->GetMargin(twistyMargin); + twistyRect.Inflate(twistyMargin); + if (isRTL) + twistyRect.x = currX + remainingWidth - twistyRect.width; + + // Now we test to see if aX is actually within the twistyRect. If it is, and if the item should + // have a twisty, then we return "twisty". If it is within the rect but we shouldn't have a twisty, + // then we return "cell". + if (aX >= twistyRect.x && aX < twistyRect.x + twistyRect.width) { + if (hasTwisty) + return nsCSSAnonBoxes::moztreetwisty; + else + return nsCSSAnonBoxes::moztreecell; + } + + if (!isRTL) + currX += twistyRect.width; + remainingWidth -= twistyRect.width; + } + + // Now test to see if the user hit the icon for the cell. + nsRect iconRect(currX, cellRect.y, remainingWidth, cellRect.height); + + // Resolve style for the image. + nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage); + + nsRect iconSize = GetImageSize(aRowIndex, aColumn, false, imageContext); + nsMargin imageMargin; + imageContext->StyleMargin()->GetMargin(imageMargin); + iconSize.Inflate(imageMargin); + iconRect.width = iconSize.width; + if (isRTL) + iconRect.x = currX + remainingWidth - iconRect.width; + + if (aX >= iconRect.x && aX < iconRect.x + iconRect.width) { + // The user clicked on the image. + return nsCSSAnonBoxes::moztreeimage; + } + + if (!isRTL) + currX += iconRect.width; + remainingWidth -= iconRect.width; + + nsAutoString cellText; + mView->GetCellText(aRowIndex, aColumn, cellText); + // We're going to measure this text so we need to ensure bidi is enabled if + // necessary + CheckTextForBidi(cellText); + + nsRect textRect(currX, cellRect.y, remainingWidth, cellRect.height); + + nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext); + + nsMargin textMargin; + textContext->StyleMargin()->GetMargin(textMargin); + textRect.Deflate(textMargin); + + AdjustForBorderPadding(textContext, textRect); + + RefPtr fm = + nsLayoutUtils::GetFontMetricsForStyleContext(textContext); + AdjustForCellText(cellText, aRowIndex, aColumn, rc, *fm, textRect); + + if (aX >= textRect.x && aX < textRect.x + textRect.width) + return nsCSSAnonBoxes::moztreecelltext; + else + return nsCSSAnonBoxes::moztreecell; +} + +void +nsTreeBodyFrame::GetCellAt(nscoord aX, nscoord aY, int32_t* aRow, + nsTreeColumn** aCol, nsIAtom** aChildElt) +{ + *aCol = nullptr; + *aChildElt = nullptr; + + *aRow = GetRowAt(aX, aY); + if (*aRow < 0) + return; + + // Determine the column hit. + for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol; + currCol = currCol->GetNext()) { + nsRect cellRect; + nsresult rv = currCol->GetRect(this, + mInnerBox.y + + mRowHeight * (*aRow - mTopRowIndex), + mRowHeight, + &cellRect); + if (NS_FAILED(rv)) { + NS_NOTREACHED("column has no frame"); + continue; + } + + if (!OffsetForHorzScroll(cellRect, false)) + continue; + + if (aX >= cellRect.x && aX < cellRect.x + cellRect.width) { + // We know the column hit now. + *aCol = currCol; + + if (currCol->IsCycler()) + // Cyclers contain only images. Fill this in immediately and return. + *aChildElt = nsCSSAnonBoxes::moztreeimage; + else + *aChildElt = GetItemWithinCellAt(aX, cellRect, *aRow, currCol); + break; + } + } +} + +nsresult +nsTreeBodyFrame::GetCellWidth(int32_t aRow, nsTreeColumn* aCol, + nsRenderingContext* aRenderingContext, + nscoord& aDesiredSize, nscoord& aCurrentSize) +{ + NS_PRECONDITION(aCol, "aCol must not be null"); + NS_PRECONDITION(aRenderingContext, "aRenderingContext must not be null"); + + // The rect for the current cell. + nscoord colWidth; + nsresult rv = aCol->GetWidthInTwips(this, &colWidth); + NS_ENSURE_SUCCESS(rv, rv); + + nsRect cellRect(0, 0, colWidth, mRowHeight); + + int32_t overflow = cellRect.x+cellRect.width-(mInnerBox.x+mInnerBox.width); + if (overflow > 0) + cellRect.width -= overflow; + + // Adjust borders and padding for the cell. + nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell); + nsMargin bp(0,0,0,0); + GetBorderPadding(cellContext, bp); + + aCurrentSize = cellRect.width; + aDesiredSize = bp.left + bp.right; + + if (aCol->IsPrimary()) { + // If the current Column is a Primary, then we need to take into account + // the indentation and possibly a twisty. + + // The amount of indentation is the indentation width (|mIndentation|) by the level. + int32_t level; + mView->GetLevel(aRow, &level); + aDesiredSize += mIndentation * level; + + // Find the twisty rect by computing its size. + nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty); + + nsRect imageSize; + nsRect twistyRect(cellRect); + GetTwistyRect(aRow, aCol, imageSize, twistyRect, PresContext(), + twistyContext); + + // Add in the margins of the twisty element. + nsMargin twistyMargin; + twistyContext->StyleMargin()->GetMargin(twistyMargin); + twistyRect.Inflate(twistyMargin); + + aDesiredSize += twistyRect.width; + } + + nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage); + + // Account for the width of the cell image. + nsRect imageSize = GetImageSize(aRow, aCol, false, imageContext); + // Add in the margins of the cell image. + nsMargin imageMargin; + imageContext->StyleMargin()->GetMargin(imageMargin); + imageSize.Inflate(imageMargin); + + aDesiredSize += imageSize.width; + + // Get the cell text. + nsAutoString cellText; + mView->GetCellText(aRow, aCol, cellText); + // We're going to measure this text so we need to ensure bidi is enabled if + // necessary + CheckTextForBidi(cellText); + + nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext); + + // Get the borders and padding for the text. + GetBorderPadding(textContext, bp); + + RefPtr fm = + nsLayoutUtils::GetFontMetricsForStyleContext(textContext); + // Get the width of the text itself + nscoord width = nsLayoutUtils::AppUnitWidthOfStringBidi(cellText, this, *fm, + *aRenderingContext); + nscoord totalTextWidth = width + bp.left + bp.right; + aDesiredSize += totalTextWidth; + return NS_OK; +} + +nsresult +nsTreeBodyFrame::IsCellCropped(int32_t aRow, nsITreeColumn* aCol, bool *_retval) +{ + nscoord currentSize, desiredSize; + nsresult rv; + + RefPtr col = GetColumnImpl(aCol); + if (!col) + return NS_ERROR_INVALID_ARG; + + nsRenderingContext rc( + PresContext()->PresShell()->CreateReferenceRenderingContext()); + + rv = GetCellWidth(aRow, col, &rc, desiredSize, currentSize); + NS_ENSURE_SUCCESS(rv, rv); + + *_retval = desiredSize > currentSize; + + return NS_OK; +} + +void +nsTreeBodyFrame::MarkDirtyIfSelect() +{ + nsIContent* baseElement = GetBaseElement(); + + if (baseElement && baseElement->IsHTMLElement(nsGkAtoms::select)) { + // If we are an intrinsically sized select widget, we may need to + // resize, if the widest item was removed or a new item was added. + // XXX optimize this more + + mStringWidth = -1; + PresContext()->PresShell()->FrameNeedsReflow(this, + nsIPresShell::eTreeChange, + NS_FRAME_IS_DIRTY); + } +} + +nsresult +nsTreeBodyFrame::CreateTimer(const LookAndFeel::IntID aID, + nsTimerCallbackFunc aFunc, int32_t aType, + nsITimer** aTimer) +{ + // Get the delay from the look and feel service. + int32_t delay = LookAndFeel::GetInt(aID, 0); + + nsCOMPtr timer; + + // Create a new timer only if the delay is greater than zero. + // Zero value means that this feature is completely disabled. + if (delay > 0) { + timer = do_CreateInstance("@mozilla.org/timer;1"); + if (timer) + timer->InitWithFuncCallback(aFunc, this, delay, aType); + } + + NS_IF_ADDREF(*aTimer = timer); + + return NS_OK; +} + +nsresult +nsTreeBodyFrame::RowCountChanged(int32_t aIndex, int32_t aCount) +{ + if (aCount == 0 || !mView) + return NS_OK; // Nothing to do. + +#ifdef ACCESSIBILITY + if (nsIPresShell::IsAccessibilityActive()) + FireRowCountChangedEvent(aIndex, aCount); +#endif + + // Adjust our selection. + nsCOMPtr sel; + mView->GetSelection(getter_AddRefs(sel)); + if (sel) + sel->AdjustSelection(aIndex, aCount); + + if (mUpdateBatchNest) + return NS_OK; + + mRowCount += aCount; +#ifdef DEBUG + int32_t rowCount = mRowCount; + mView->GetRowCount(&rowCount); + NS_ASSERTION(rowCount == mRowCount, "row count did not change by the amount suggested, check caller"); +#endif + + int32_t count = Abs(aCount); + int32_t last = LastVisibleRow(); + if (aIndex >= mTopRowIndex && aIndex <= last) + InvalidateRange(aIndex, last); + + ScrollParts parts = GetScrollParts(); + + if (mTopRowIndex == 0) { + // Just update the scrollbar and return. + if (FullScrollbarsUpdate(false)) { + MarkDirtyIfSelect(); + } + return NS_OK; + } + + bool needsInvalidation = false; + // Adjust our top row index. + if (aCount > 0) { + if (mTopRowIndex > aIndex) { + // Rows came in above us. Augment the top row index. + mTopRowIndex += aCount; + } + } + else if (aCount < 0) { + if (mTopRowIndex > aIndex+count-1) { + // No need to invalidate. The remove happened + // completely above us (offscreen). + mTopRowIndex -= count; + } + else if (mTopRowIndex >= aIndex) { + // This is a full-blown invalidate. + if (mTopRowIndex + mPageLength > mRowCount - 1) { + mTopRowIndex = std::max(0, mRowCount - 1 - mPageLength); + } + needsInvalidation = true; + } + } + + if (FullScrollbarsUpdate(needsInvalidation)) { + MarkDirtyIfSelect(); + } + return NS_OK; +} + +nsresult +nsTreeBodyFrame::BeginUpdateBatch() +{ + ++mUpdateBatchNest; + + return NS_OK; +} + +nsresult +nsTreeBodyFrame::EndUpdateBatch() +{ + NS_ASSERTION(mUpdateBatchNest > 0, "badly nested update batch"); + + if (--mUpdateBatchNest == 0) { + if (mView) { + Invalidate(); + int32_t countBeforeUpdate = mRowCount; + mView->GetRowCount(&mRowCount); + if (countBeforeUpdate != mRowCount) { + if (mTopRowIndex + mPageLength > mRowCount - 1) { + mTopRowIndex = std::max(0, mRowCount - 1 - mPageLength); + } + FullScrollbarsUpdate(false); + } + } + } + + return NS_OK; +} + +void +nsTreeBodyFrame::PrefillPropertyArray(int32_t aRowIndex, nsTreeColumn* aCol) +{ + NS_PRECONDITION(!aCol || aCol->GetFrame(), "invalid column passed"); + mScratchArray.Clear(); + + // focus + if (mFocused) + mScratchArray.AppendElement(nsGkAtoms::focus); + + // sort + bool sorted = false; + mView->IsSorted(&sorted); + if (sorted) + mScratchArray.AppendElement(nsGkAtoms::sorted); + + // drag session + if (mSlots && mSlots->mIsDragging) + mScratchArray.AppendElement(nsGkAtoms::dragSession); + + if (aRowIndex != -1) { + if (aRowIndex == mMouseOverRow) + mScratchArray.AppendElement(nsGkAtoms::hover); + + nsCOMPtr selection; + mView->GetSelection(getter_AddRefs(selection)); + + if (selection) { + // selected + bool isSelected; + selection->IsSelected(aRowIndex, &isSelected); + if (isSelected) + mScratchArray.AppendElement(nsGkAtoms::selected); + + // current + int32_t currentIndex; + selection->GetCurrentIndex(¤tIndex); + if (aRowIndex == currentIndex) + mScratchArray.AppendElement(nsGkAtoms::current); + + // active + if (aCol) { + nsCOMPtr currentColumn; + selection->GetCurrentColumn(getter_AddRefs(currentColumn)); + if (aCol == currentColumn) + mScratchArray.AppendElement(nsGkAtoms::active); + } + } + + // container or leaf + bool isContainer = false; + mView->IsContainer(aRowIndex, &isContainer); + if (isContainer) { + mScratchArray.AppendElement(nsGkAtoms::container); + + // open or closed + bool isOpen = false; + mView->IsContainerOpen(aRowIndex, &isOpen); + if (isOpen) + mScratchArray.AppendElement(nsGkAtoms::open); + else + mScratchArray.AppendElement(nsGkAtoms::closed); + } + else { + mScratchArray.AppendElement(nsGkAtoms::leaf); + } + + // drop orientation + if (mSlots && mSlots->mDropAllowed && mSlots->mDropRow == aRowIndex) { + if (mSlots->mDropOrient == nsITreeView::DROP_BEFORE) + mScratchArray.AppendElement(nsGkAtoms::dropBefore); + else if (mSlots->mDropOrient == nsITreeView::DROP_ON) + mScratchArray.AppendElement(nsGkAtoms::dropOn); + else if (mSlots->mDropOrient == nsITreeView::DROP_AFTER) + mScratchArray.AppendElement(nsGkAtoms::dropAfter); + } + + // odd or even + if (aRowIndex % 2) + mScratchArray.AppendElement(nsGkAtoms::odd); + else + mScratchArray.AppendElement(nsGkAtoms::even); + + nsIContent* baseContent = GetBaseElement(); + if (baseContent && baseContent->HasAttr(kNameSpaceID_None, nsGkAtoms::editing)) + mScratchArray.AppendElement(nsGkAtoms::editing); + + // multiple columns + if (mColumns->GetColumnAt(1)) + mScratchArray.AppendElement(nsGkAtoms::multicol); + } + + if (aCol) { + mScratchArray.AppendElement(aCol->GetAtom()); + + if (aCol->IsPrimary()) + mScratchArray.AppendElement(nsGkAtoms::primary); + + if (aCol->GetType() == nsITreeColumn::TYPE_CHECKBOX) { + mScratchArray.AppendElement(nsGkAtoms::checkbox); + + if (aRowIndex != -1) { + nsAutoString value; + mView->GetCellValue(aRowIndex, aCol, value); + if (value.EqualsLiteral("true")) + mScratchArray.AppendElement(nsGkAtoms::checked); + } + } + else if (aCol->GetType() == nsITreeColumn::TYPE_PROGRESSMETER) { + mScratchArray.AppendElement(nsGkAtoms::progressmeter); + + if (aRowIndex != -1) { + int32_t state; + mView->GetProgressMode(aRowIndex, aCol, &state); + if (state == nsITreeView::PROGRESS_NORMAL) + mScratchArray.AppendElement(nsGkAtoms::progressNormal); + else if (state == nsITreeView::PROGRESS_UNDETERMINED) + mScratchArray.AppendElement(nsGkAtoms::progressUndetermined); + } + } + + // Read special properties from attributes on the column content node + if (aCol->mContent->AttrValueIs(kNameSpaceID_None, + nsGkAtoms::insertbefore, + nsGkAtoms::_true, eCaseMatters)) + mScratchArray.AppendElement(nsGkAtoms::insertbefore); + if (aCol->mContent->AttrValueIs(kNameSpaceID_None, + nsGkAtoms::insertafter, + nsGkAtoms::_true, eCaseMatters)) + mScratchArray.AppendElement(nsGkAtoms::insertafter); + } +} + +nsITheme* +nsTreeBodyFrame::GetTwistyRect(int32_t aRowIndex, + nsTreeColumn* aColumn, + nsRect& aImageRect, + nsRect& aTwistyRect, + nsPresContext* aPresContext, + nsStyleContext* aTwistyContext) +{ + // The twisty rect extends all the way to the end of the cell. This is incorrect. We need to + // determine the twisty rect's true width. This is done by examining the style context for + // a width first. If it has one, we use that. If it doesn't, we use the image's natural width. + // If the image hasn't loaded and if no width is specified, then we just bail. If there is + // a -moz-appearance involved, adjust the rect by the minimum widget size provided by + // the theme implementation. + aImageRect = GetImageSize(aRowIndex, aColumn, true, aTwistyContext); + if (aImageRect.height > aTwistyRect.height) + aImageRect.height = aTwistyRect.height; + if (aImageRect.width > aTwistyRect.width) + aImageRect.width = aTwistyRect.width; + else + aTwistyRect.width = aImageRect.width; + + bool useTheme = false; + nsITheme *theme = nullptr; + const nsStyleDisplay* twistyDisplayData = aTwistyContext->StyleDisplay(); + if (twistyDisplayData->mAppearance) { + theme = aPresContext->GetTheme(); + if (theme && theme->ThemeSupportsWidget(aPresContext, nullptr, twistyDisplayData->mAppearance)) + useTheme = true; + } + + if (useTheme) { + LayoutDeviceIntSize minTwistySizePx; + bool canOverride = true; + theme->GetMinimumWidgetSize(aPresContext, this, twistyDisplayData->mAppearance, + &minTwistySizePx, &canOverride); + + // GMWS() returns size in pixels, we need to convert it back to app units + nsSize minTwistySize; + minTwistySize.width = aPresContext->DevPixelsToAppUnits(minTwistySizePx.width); + minTwistySize.height = aPresContext->DevPixelsToAppUnits(minTwistySizePx.height); + + if (aTwistyRect.width < minTwistySize.width || !canOverride) + aTwistyRect.width = minTwistySize.width; + } + + return useTheme ? theme : nullptr; +} + +nsresult +nsTreeBodyFrame::GetImage(int32_t aRowIndex, nsTreeColumn* aCol, bool aUseContext, + nsStyleContext* aStyleContext, bool& aAllowImageRegions, imgIContainer** aResult) +{ + *aResult = nullptr; + + nsAutoString imageSrc; + mView->GetImageSrc(aRowIndex, aCol, imageSrc); + RefPtr styleRequest; + if (!aUseContext && !imageSrc.IsEmpty()) { + aAllowImageRegions = false; + } + else { + // Obtain the URL from the style context. + aAllowImageRegions = true; + styleRequest = aStyleContext->StyleList()->GetListStyleImage(); + if (!styleRequest) + return NS_OK; + nsCOMPtr uri; + styleRequest->GetURI(getter_AddRefs(uri)); + nsAutoCString spec; + nsresult rv = uri->GetSpec(spec); + NS_ENSURE_SUCCESS(rv, rv); + CopyUTF8toUTF16(spec, imageSrc); + } + + // Look the image up in our cache. + nsTreeImageCacheEntry entry; + if (mImageCache.Get(imageSrc, &entry)) { + // Find out if the image has loaded. + uint32_t status; + imgIRequest *imgReq = entry.request; + imgReq->GetImageStatus(&status); + imgReq->GetImage(aResult); // We hand back the image here. The GetImage call addrefs *aResult. + bool animated = true; // Assuming animated is the safe option + + // We can only call GetAnimated if we're decoded + if (*aResult && (status & imgIRequest::STATUS_DECODE_COMPLETE)) + (*aResult)->GetAnimated(&animated); + + if ((!(status & imgIRequest::STATUS_LOAD_COMPLETE)) || animated) { + // We either aren't done loading, or we're animating. Add our row as a listener for invalidations. + nsCOMPtr obs; + imgReq->GetNotificationObserver(getter_AddRefs(obs)); + + if (obs) { + static_cast (obs.get())->AddCell(aRowIndex, aCol); + } + + return NS_OK; + } + } + + if (!*aResult) { + // Create a new nsTreeImageListener object and pass it our row and column + // information. + nsTreeImageListener* listener = new nsTreeImageListener(this); + if (!listener) + return NS_ERROR_OUT_OF_MEMORY; + + if (!mCreatedListeners.PutEntry(listener)) { + return NS_ERROR_FAILURE; + } + + listener->AddCell(aRowIndex, aCol); + nsCOMPtr imgNotificationObserver = listener; + + RefPtr imageRequest; + if (styleRequest) { + styleRequest->Clone(imgNotificationObserver, getter_AddRefs(imageRequest)); + } else { + nsIDocument* doc = mContent->GetComposedDoc(); + if (!doc) + // The page is currently being torn down. Why bother. + return NS_ERROR_FAILURE; + + nsCOMPtr baseURI = mContent->GetBaseURI(); + + nsCOMPtr srcURI; + nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(srcURI), + imageSrc, + doc, + baseURI); + if (!srcURI) + return NS_ERROR_FAILURE; + + // XXXbz what's the origin principal for this stuff that comes from our + // view? I guess we should assume that it's the node's principal... + nsresult rv = nsContentUtils::LoadImage(srcURI, + mContent, + doc, + mContent->NodePrincipal(), + doc->GetDocumentURI(), + doc->GetReferrerPolicy(), + imgNotificationObserver, + nsIRequest::LOAD_NORMAL, + EmptyString(), + getter_AddRefs(imageRequest)); + NS_ENSURE_SUCCESS(rv, rv); + } + listener->UnsuppressInvalidation(); + + if (!imageRequest) + return NS_ERROR_FAILURE; + + // We don't want discarding/decode-on-draw for xul images + imageRequest->StartDecoding(); + imageRequest->LockImage(); + + // In a case it was already cached. + imageRequest->GetImage(aResult); + nsTreeImageCacheEntry cacheEntry(imageRequest, imgNotificationObserver); + mImageCache.Put(imageSrc, cacheEntry); + } + return NS_OK; +} + +nsRect nsTreeBodyFrame::GetImageSize(int32_t aRowIndex, nsTreeColumn* aCol, bool aUseContext, + nsStyleContext* aStyleContext) +{ + // XXX We should respond to visibility rules for collapsed vs. hidden. + + // This method returns the width of the twisty INCLUDING borders and padding. + // It first checks the style context for a width. If none is found, it tries to + // use the default image width for the twisty. If no image is found, it defaults + // to border+padding. + nsRect r(0,0,0,0); + nsMargin bp(0,0,0,0); + GetBorderPadding(aStyleContext, bp); + r.Inflate(bp); + + // Now r contains our border+padding info. We now need to get our width and + // height. + bool needWidth = false; + bool needHeight = false; + + // We have to load image even though we already have a size. + // Don't change this, otherwise things start to go crazy. + bool useImageRegion = true; + nsCOMPtr image; + GetImage(aRowIndex, aCol, aUseContext, aStyleContext, useImageRegion, getter_AddRefs(image)); + + const nsStylePosition* myPosition = aStyleContext->StylePosition(); + const nsStyleList* myList = aStyleContext->StyleList(); + + if (useImageRegion) { + r.x += myList->mImageRegion.x; + r.y += myList->mImageRegion.y; + } + + if (myPosition->mWidth.GetUnit() == eStyleUnit_Coord) { + int32_t val = myPosition->mWidth.GetCoordValue(); + r.width += val; + } + else if (useImageRegion && myList->mImageRegion.width > 0) + r.width += myList->mImageRegion.width; + else + needWidth = true; + + if (myPosition->mHeight.GetUnit() == eStyleUnit_Coord) { + int32_t val = myPosition->mHeight.GetCoordValue(); + r.height += val; + } + else if (useImageRegion && myList->mImageRegion.height > 0) + r.height += myList->mImageRegion.height; + else + needHeight = true; + + if (image) { + if (needWidth || needHeight) { + // Get the natural image size. + + if (needWidth) { + // Get the size from the image. + nscoord width; + image->GetWidth(&width); + r.width += nsPresContext::CSSPixelsToAppUnits(width); + } + + if (needHeight) { + nscoord height; + image->GetHeight(&height); + r.height += nsPresContext::CSSPixelsToAppUnits(height); + } + } + } + + return r; +} + +// GetImageDestSize returns the destination size of the image. +// The width and height do not include borders and padding. +// The width and height have not been adjusted to fit in the row height +// or cell width. +// The width and height reflect the destination size specified in CSS, +// or the image region specified in CSS, or the natural size of the +// image. +// If only the destination width has been specified in CSS, the height is +// calculated to maintain the aspect ratio of the image. +// If only the destination height has been specified in CSS, the width is +// calculated to maintain the aspect ratio of the image. +nsSize +nsTreeBodyFrame::GetImageDestSize(nsStyleContext* aStyleContext, + bool useImageRegion, + imgIContainer* image) +{ + nsSize size(0,0); + + // We need to get the width and height. + bool needWidth = false; + bool needHeight = false; + + // Get the style position to see if the CSS has specified the + // destination width/height. + const nsStylePosition* myPosition = aStyleContext->StylePosition(); + + if (myPosition->mWidth.GetUnit() == eStyleUnit_Coord) { + // CSS has specified the destination width. + size.width = myPosition->mWidth.GetCoordValue(); + } + else { + // We'll need to get the width of the image/region. + needWidth = true; + } + + if (myPosition->mHeight.GetUnit() == eStyleUnit_Coord) { + // CSS has specified the destination height. + size.height = myPosition->mHeight.GetCoordValue(); + } + else { + // We'll need to get the height of the image/region. + needHeight = true; + } + + if (needWidth || needHeight) { + // We need to get the size of the image/region. + nsSize imageSize(0,0); + + const nsStyleList* myList = aStyleContext->StyleList(); + + if (useImageRegion && myList->mImageRegion.width > 0) { + // CSS has specified an image region. + // Use the width of the image region. + imageSize.width = myList->mImageRegion.width; + } + else if (image) { + nscoord width; + image->GetWidth(&width); + imageSize.width = nsPresContext::CSSPixelsToAppUnits(width); + } + + if (useImageRegion && myList->mImageRegion.height > 0) { + // CSS has specified an image region. + // Use the height of the image region. + imageSize.height = myList->mImageRegion.height; + } + else if (image) { + nscoord height; + image->GetHeight(&height); + imageSize.height = nsPresContext::CSSPixelsToAppUnits(height); + } + + if (needWidth) { + if (!needHeight && imageSize.height != 0) { + // The CSS specified the destination height, but not the destination + // width. We need to calculate the width so that we maintain the + // image's aspect ratio. + size.width = imageSize.width * size.height / imageSize.height; + } + else { + size.width = imageSize.width; + } + } + + if (needHeight) { + if (!needWidth && imageSize.width != 0) { + // The CSS specified the destination width, but not the destination + // height. We need to calculate the height so that we maintain the + // image's aspect ratio. + size.height = imageSize.height * size.width / imageSize.width; + } + else { + size.height = imageSize.height; + } + } + } + + return size; +} + +// GetImageSourceRect returns the source rectangle of the image to be +// displayed. +// The width and height reflect the image region specified in CSS, or +// the natural size of the image. +// The width and height do not include borders and padding. +// The width and height do not reflect the destination size specified +// in CSS. +nsRect +nsTreeBodyFrame::GetImageSourceRect(nsStyleContext* aStyleContext, + bool useImageRegion, + imgIContainer* image) +{ + nsRect r(0,0,0,0); + + const nsStyleList* myList = aStyleContext->StyleList(); + + if (useImageRegion && + (myList->mImageRegion.width > 0 || myList->mImageRegion.height > 0)) { + // CSS has specified an image region. + r = myList->mImageRegion; + } + else if (image) { + // Use the actual image size. + nscoord coord; + image->GetWidth(&coord); + r.width = nsPresContext::CSSPixelsToAppUnits(coord); + image->GetHeight(&coord); + r.height = nsPresContext::CSSPixelsToAppUnits(coord); + } + + return r; +} + +int32_t nsTreeBodyFrame::GetRowHeight() +{ + // Look up the correct height. It is equal to the specified height + // + the specified margins. + mScratchArray.Clear(); + nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow); + if (rowContext) { + const nsStylePosition* myPosition = rowContext->StylePosition(); + + nscoord minHeight = 0; + if (myPosition->mMinHeight.GetUnit() == eStyleUnit_Coord) + minHeight = myPosition->mMinHeight.GetCoordValue(); + + nscoord height = 0; + if (myPosition->mHeight.GetUnit() == eStyleUnit_Coord) + height = myPosition->mHeight.GetCoordValue(); + + if (height < minHeight) + height = minHeight; + + if (height > 0) { + height = nsPresContext::AppUnitsToIntCSSPixels(height); + height += height % 2; + height = nsPresContext::CSSPixelsToAppUnits(height); + + // XXX Check box-sizing to determine if border/padding should augment the height + // Inflate the height by our margins. + nsRect rowRect(0,0,0,height); + nsMargin rowMargin; + rowContext->StyleMargin()->GetMargin(rowMargin); + rowRect.Inflate(rowMargin); + height = rowRect.height; + return height; + } + } + + return nsPresContext::CSSPixelsToAppUnits(18); // As good a default as any. +} + +int32_t nsTreeBodyFrame::GetIndentation() +{ + // Look up the correct indentation. It is equal to the specified indentation width. + mScratchArray.Clear(); + nsStyleContext* indentContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeindentation); + if (indentContext) { + const nsStylePosition* myPosition = indentContext->StylePosition(); + if (myPosition->mWidth.GetUnit() == eStyleUnit_Coord) { + nscoord val = myPosition->mWidth.GetCoordValue(); + return val; + } + } + + return nsPresContext::CSSPixelsToAppUnits(16); // As good a default as any. +} + +void nsTreeBodyFrame::CalcInnerBox() +{ + mInnerBox.SetRect(0, 0, mRect.width, mRect.height); + AdjustForBorderPadding(mStyleContext, mInnerBox); +} + +nscoord +nsTreeBodyFrame::CalcHorzWidth(const ScrollParts& aParts) +{ + // Compute the adjustment to the last column. This varies depending on the + // visibility of the columnpicker and the scrollbar. + if (aParts.mColumnsFrame) + mAdjustWidth = mRect.width - aParts.mColumnsFrame->GetRect().width; + else + mAdjustWidth = 0; + + nscoord width = 0; + + // We calculate this from the scrollable frame, so that it + // properly covers all contingencies of what could be + // scrollable (columns, body, etc...) + + if (aParts.mColumnsScrollFrame) { + width = aParts.mColumnsScrollFrame->GetScrollRange().width + + aParts.mColumnsScrollFrame->GetScrollPortRect().width; + } + + // If no horz scrolling periphery is present, then just return our width + if (width == 0) + width = mRect.width; + + return width; +} + +nsresult +nsTreeBodyFrame::GetCursor(const nsPoint& aPoint, + nsIFrame::Cursor& aCursor) +{ + // Check the GetScriptHandlingObject so we don't end up running code when + // the document is a zombie. + bool dummy; + if (mView && GetContent()->GetComposedDoc()->GetScriptHandlingObject(dummy)) { + int32_t row; + nsTreeColumn* col; + nsIAtom* child; + GetCellAt(aPoint.x, aPoint.y, &row, &col, &child); + + if (child) { + // Our scratch array is already prefilled. + nsStyleContext* childContext = GetPseudoStyleContext(child); + + FillCursorInformationFromStyle(childContext->StyleUserInterface(), + aCursor); + if (aCursor.mCursor == NS_STYLE_CURSOR_AUTO) + aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT; + + return NS_OK; + } + } + + return nsLeafBoxFrame::GetCursor(aPoint, aCursor); +} + +static uint32_t GetDropEffect(WidgetGUIEvent* aEvent) +{ + NS_ASSERTION(aEvent->mClass == eDragEventClass, "wrong event type"); + WidgetDragEvent* dragEvent = aEvent->AsDragEvent(); + nsContentUtils::SetDataTransferInEvent(dragEvent); + + uint32_t action = 0; + if (dragEvent->mDataTransfer) { + dragEvent->mDataTransfer->GetDropEffectInt(&action); + } + return action; +} + +nsresult +nsTreeBodyFrame::HandleEvent(nsPresContext* aPresContext, + WidgetGUIEvent* aEvent, + nsEventStatus* aEventStatus) +{ + if (aEvent->mMessage == eMouseOver || aEvent->mMessage == eMouseMove) { + nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this); + int32_t xTwips = pt.x - mInnerBox.x; + int32_t yTwips = pt.y - mInnerBox.y; + int32_t newrow = GetRowAt(xTwips, yTwips); + if (mMouseOverRow != newrow) { + // redraw the old and the new row + if (mMouseOverRow != -1) + InvalidateRow(mMouseOverRow); + mMouseOverRow = newrow; + if (mMouseOverRow != -1) + InvalidateRow(mMouseOverRow); + } + } else if (aEvent->mMessage == eMouseOut) { + if (mMouseOverRow != -1) { + InvalidateRow(mMouseOverRow); + mMouseOverRow = -1; + } + } else if (aEvent->mMessage == eDragEnter) { + if (!mSlots) + mSlots = new Slots(); + + // Cache several things we'll need throughout the course of our work. These + // will all get released on a drag exit. + + if (mSlots->mTimer) { + mSlots->mTimer->Cancel(); + mSlots->mTimer = nullptr; + } + + // Cache the drag session. + mSlots->mIsDragging = true; + mSlots->mDropRow = -1; + mSlots->mDropOrient = -1; + mSlots->mDragAction = GetDropEffect(aEvent); + } else if (aEvent->mMessage == eDragOver) { + // The mouse is hovering over this tree. If we determine things are + // different from the last time, invalidate the drop feedback at the old + // position, query the view to see if the current location is droppable, + // and then invalidate the drop feedback at the new location if it is. + // The mouse may or may not have changed position from the last time + // we were called, so optimize out a lot of the extra notifications by + // checking if anything changed first. For drop feedback we use drop, + // dropBefore and dropAfter property. + + if (!mView || !mSlots) + return NS_OK; + + // Save last values, we will need them. + int32_t lastDropRow = mSlots->mDropRow; + int16_t lastDropOrient = mSlots->mDropOrient; +#ifndef XP_MACOSX + int16_t lastScrollLines = mSlots->mScrollLines; +#endif + + // Find out the current drag action + uint32_t lastDragAction = mSlots->mDragAction; + mSlots->mDragAction = GetDropEffect(aEvent); + + // Compute the row mouse is over and the above/below/on state. + // Below we'll use this to see if anything changed. + // Also check if we want to auto-scroll. + ComputeDropPosition(aEvent, &mSlots->mDropRow, &mSlots->mDropOrient, &mSlots->mScrollLines); + + // While we're here, handle tracking of scrolling during a drag. + if (mSlots->mScrollLines) { + if (mSlots->mDropAllowed) { + // Invalidate primary cell at old location. + mSlots->mDropAllowed = false; + InvalidateDropFeedback(lastDropRow, lastDropOrient); + } +#ifdef XP_MACOSX + ScrollByLines(mSlots->mScrollLines); +#else + if (!lastScrollLines) { + // Cancel any previously initialized timer. + if (mSlots->mTimer) { + mSlots->mTimer->Cancel(); + mSlots->mTimer = nullptr; + } + + // Set a timer to trigger the tree scrolling. + CreateTimer(LookAndFeel::eIntID_TreeLazyScrollDelay, + LazyScrollCallback, nsITimer::TYPE_ONE_SHOT, + getter_AddRefs(mSlots->mTimer)); + } +#endif + // Bail out to prevent spring loaded timer and feedback line settings. + return NS_OK; + } + + // If changed from last time, invalidate primary cell at the old location and if allowed, + // invalidate primary cell at the new location. If nothing changed, just bail. + if (mSlots->mDropRow != lastDropRow || + mSlots->mDropOrient != lastDropOrient || + mSlots->mDragAction != lastDragAction) { + + // Invalidate row at the old location. + if (mSlots->mDropAllowed) { + mSlots->mDropAllowed = false; + InvalidateDropFeedback(lastDropRow, lastDropOrient); + } + + if (mSlots->mTimer) { + // Timer is active but for a different row than the current one, kill it. + mSlots->mTimer->Cancel(); + mSlots->mTimer = nullptr; + } + + if (mSlots->mDropRow >= 0) { + if (!mSlots->mTimer && mSlots->mDropOrient == nsITreeView::DROP_ON) { + // Either there wasn't a timer running or it was just killed above. + // If over a folder, start up a timer to open the folder. + bool isContainer = false; + mView->IsContainer(mSlots->mDropRow, &isContainer); + if (isContainer) { + bool isOpen = false; + mView->IsContainerOpen(mSlots->mDropRow, &isOpen); + if (!isOpen) { + // This node isn't expanded, set a timer to expand it. + CreateTimer(LookAndFeel::eIntID_TreeOpenDelay, + OpenCallback, nsITimer::TYPE_ONE_SHOT, + getter_AddRefs(mSlots->mTimer)); + } + } + } + + // The dataTransfer was initialized by the call to GetDropEffect above. + bool canDropAtNewLocation = false; + mView->CanDrop(mSlots->mDropRow, mSlots->mDropOrient, + aEvent->AsDragEvent()->mDataTransfer, + &canDropAtNewLocation); + + if (canDropAtNewLocation) { + // Invalidate row at the new location. + mSlots->mDropAllowed = canDropAtNewLocation; + InvalidateDropFeedback(mSlots->mDropRow, mSlots->mDropOrient); + } + } + } + + // Indicate that the drop is allowed by preventing the default behaviour. + if (mSlots->mDropAllowed) + *aEventStatus = nsEventStatus_eConsumeNoDefault; + } else if (aEvent->mMessage == eDrop) { + // this event was meant for another frame, so ignore it + if (!mSlots) + return NS_OK; + + // Tell the view where the drop happened. + + // Remove the drop folder and all its parents from the array. + int32_t parentIndex; + nsresult rv = mView->GetParentIndex(mSlots->mDropRow, &parentIndex); + while (NS_SUCCEEDED(rv) && parentIndex >= 0) { + mSlots->mArray.RemoveElement(parentIndex); + rv = mView->GetParentIndex(parentIndex, &parentIndex); + } + + NS_ASSERTION(aEvent->mClass == eDragEventClass, "wrong event type"); + WidgetDragEvent* dragEvent = aEvent->AsDragEvent(); + nsContentUtils::SetDataTransferInEvent(dragEvent); + + mView->Drop(mSlots->mDropRow, mSlots->mDropOrient, + dragEvent->mDataTransfer); + mSlots->mDropRow = -1; + mSlots->mDropOrient = -1; + mSlots->mIsDragging = false; + *aEventStatus = nsEventStatus_eConsumeNoDefault; // already handled the drop + } else if (aEvent->mMessage == eDragExit) { + // this event was meant for another frame, so ignore it + if (!mSlots) + return NS_OK; + + // Clear out all our tracking vars. + + if (mSlots->mDropAllowed) { + mSlots->mDropAllowed = false; + InvalidateDropFeedback(mSlots->mDropRow, mSlots->mDropOrient); + } + else + mSlots->mDropAllowed = false; + mSlots->mIsDragging = false; + mSlots->mScrollLines = 0; + // If a drop is occuring, the exit event will fire just before the drop + // event, so don't reset mDropRow or mDropOrient as these fields are used + // by the drop event. + if (mSlots->mTimer) { + mSlots->mTimer->Cancel(); + mSlots->mTimer = nullptr; + } + + if (!mSlots->mArray.IsEmpty()) { + // Close all spring loaded folders except the drop folder. + CreateTimer(LookAndFeel::eIntID_TreeCloseDelay, + CloseCallback, nsITimer::TYPE_ONE_SHOT, + getter_AddRefs(mSlots->mTimer)); + } + } + + return NS_OK; +} + +class nsDisplayTreeBody final : public nsDisplayItem { +public: + nsDisplayTreeBody(nsDisplayListBuilder* aBuilder, nsFrame* aFrame) : + nsDisplayItem(aBuilder, aFrame), + mDisableSubpixelAA(false) { + MOZ_COUNT_CTOR(nsDisplayTreeBody); + } +#ifdef NS_BUILD_REFCNT_LOGGING + virtual ~nsDisplayTreeBody() { + MOZ_COUNT_DTOR(nsDisplayTreeBody); + } +#endif + + nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override + { + return new nsDisplayItemGenericImageGeometry(this, aBuilder); + } + + void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, + const nsDisplayItemGeometry* aGeometry, + nsRegion *aInvalidRegion) override + { + auto geometry = + static_cast(aGeometry); + + if (aBuilder->ShouldSyncDecodeImages() && + geometry->ShouldInvalidateToSyncDecodeImages()) { + bool snap; + aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap)); + } + + nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion); + } + + virtual void Paint(nsDisplayListBuilder* aBuilder, + nsRenderingContext* aCtx) override + { + DrawTargetAutoDisableSubpixelAntialiasing disable(aCtx->GetDrawTarget(), + mDisableSubpixelAA); + + DrawResult result = static_cast(mFrame) + ->PaintTreeBody(*aCtx, mVisibleRect, ToReferenceFrame()); + + nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result); + } + + NS_DISPLAY_DECL_NAME("XULTreeBody", TYPE_XUL_TREE_BODY) + + virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) override + { + bool snap; + return GetBounds(aBuilder, &snap); + } + virtual void DisableComponentAlpha() override { + mDisableSubpixelAA = true; + } + + bool mDisableSubpixelAA; +}; + +// Painting routines +void +nsTreeBodyFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsRect& aDirtyRect, + const nsDisplayListSet& aLists) +{ + // REVIEW: why did we paint if we were collapsed? that makes no sense! + if (!IsVisibleForPainting(aBuilder)) + return; // We're invisible. Don't paint. + + // Handles painting our background, border, and outline. + nsLeafBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists); + + // Bail out now if there's no view or we can't run script because the + // document is a zombie + if (!mView || !GetContent ()->GetComposedDoc()->GetWindow()) + return; + +#ifdef XP_MACOSX + nsIContent* baseElement = GetBaseElement(); + nsIFrame* treeFrame = + baseElement ? baseElement->GetPrimaryFrame() : nullptr; + nsCOMPtr selection; + mView->GetSelection(getter_AddRefs(selection)); + nsITheme* theme = PresContext()->GetTheme(); + // On Mac, we support native theming of selected rows. On 10.10 and higher, + // this means applying vibrancy which require us to register the theme + // geometrics for the row. In order to make the vibrancy effect to work + // properly, we also need the tree to be themed as a source list. + if (selection && treeFrame && theme && + treeFrame->StyleDisplay()->mAppearance == NS_THEME_MAC_SOURCE_LIST) { + // Loop through our onscreen rows. If the row is selected and a + // -moz-appearance is provided, RegisterThemeGeometry might be necessary. + const auto end = std::min(mRowCount, LastVisibleRow() + 1); + for (auto i = FirstVisibleRow(); i < end; i++) { + bool isSelected; + selection->IsSelected(i, &isSelected); + if (isSelected) { + PrefillPropertyArray(i, nullptr); + nsAutoString properties; + mView->GetRowProperties(i, properties); + nsTreeUtils::TokenizeProperties(properties, mScratchArray); + nsStyleContext* rowContext = + GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow); + auto appearance = rowContext->StyleDisplay()->mAppearance; + if (appearance) { + if (theme->ThemeSupportsWidget(PresContext(), this, appearance)) { + nsITheme::ThemeGeometryType type = + theme->ThemeGeometryTypeForWidget(this, appearance); + if (type != nsITheme::eThemeGeometryTypeUnknown) { + nsRect rowRect(mInnerBox.x, mInnerBox.y + mRowHeight * + (i - FirstVisibleRow()), mInnerBox.width, + mRowHeight); + aBuilder->RegisterThemeGeometry(type, + LayoutDeviceIntRect::FromUnknownRect( + (rowRect + aBuilder->ToReferenceFrame(this)).ToNearestPixels( + PresContext()->AppUnitsPerDevPixel()))); + } + } + } + } + } + } +#endif + + aLists.Content()->AppendNewToTop(new (aBuilder) + nsDisplayTreeBody(aBuilder, this)); +} + +DrawResult +nsTreeBodyFrame::PaintTreeBody(nsRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, nsPoint aPt) +{ + // Update our available height and our page count. + CalcInnerBox(); + + DrawTarget* drawTarget = aRenderingContext.GetDrawTarget(); + gfxContext* gfx = aRenderingContext.ThebesContext(); + + gfx->Save(); + gfx->Clip(NSRectToSnappedRect(mInnerBox + aPt, + PresContext()->AppUnitsPerDevPixel(), + *drawTarget)); + int32_t oldPageCount = mPageLength; + if (!mHasFixedRowCount) + mPageLength = mInnerBox.height/mRowHeight; + + if (oldPageCount != mPageLength || mHorzWidth != CalcHorzWidth(GetScrollParts())) { + // Schedule a ResizeReflow that will update our info properly. + PresContext()->PresShell()-> + FrameNeedsReflow(this, nsIPresShell::eResize, NS_FRAME_IS_DIRTY); + } +#ifdef DEBUG + int32_t rowCount = mRowCount; + mView->GetRowCount(&rowCount); + NS_WARNING_ASSERTION(mRowCount == rowCount, "row count changed unexpectedly"); +#endif + + DrawResult result = DrawResult::SUCCESS; + + // Loop through our columns and paint them (e.g., for sorting). This is only + // relevant when painting backgrounds, since columns contain no content. Content + // is contained in the rows. + for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol; + currCol = currCol->GetNext()) { + nsRect colRect; + nsresult rv = currCol->GetRect(this, mInnerBox.y, mInnerBox.height, + &colRect); + // Don't paint hidden columns. + if (NS_FAILED(rv) || colRect.width == 0) continue; + + if (OffsetForHorzScroll(colRect, false)) { + nsRect dirtyRect; + colRect += aPt; + if (dirtyRect.IntersectRect(aDirtyRect, colRect)) { + result &= + PaintColumn(currCol, colRect, PresContext(), aRenderingContext, aDirtyRect); + } + } + } + // Loop through our on-screen rows. + for (int32_t i = mTopRowIndex; i < mRowCount && i <= mTopRowIndex+mPageLength; i++) { + nsRect rowRect(mInnerBox.x, mInnerBox.y+mRowHeight*(i-mTopRowIndex), mInnerBox.width, mRowHeight); + nsRect dirtyRect; + if (dirtyRect.IntersectRect(aDirtyRect, rowRect + aPt) && + rowRect.y < (mInnerBox.y+mInnerBox.height)) { + result &= + PaintRow(i, rowRect + aPt, PresContext(), aRenderingContext, aDirtyRect, aPt); + } + } + + if (mSlots && mSlots->mDropAllowed && (mSlots->mDropOrient == nsITreeView::DROP_BEFORE || + mSlots->mDropOrient == nsITreeView::DROP_AFTER)) { + nscoord yPos = mInnerBox.y + mRowHeight * (mSlots->mDropRow - mTopRowIndex) - mRowHeight / 2; + nsRect feedbackRect(mInnerBox.x, yPos, mInnerBox.width, mRowHeight); + if (mSlots->mDropOrient == nsITreeView::DROP_AFTER) + feedbackRect.y += mRowHeight; + + nsRect dirtyRect; + feedbackRect += aPt; + if (dirtyRect.IntersectRect(aDirtyRect, feedbackRect)) { + result &= + PaintDropFeedback(feedbackRect, PresContext(), aRenderingContext, + aDirtyRect, aPt); + } + } + gfx->Restore(); + + return result; +} + + + +DrawResult +nsTreeBodyFrame::PaintColumn(nsTreeColumn* aColumn, + const nsRect& aColumnRect, + nsPresContext* aPresContext, + nsRenderingContext& aRenderingContext, + const nsRect& aDirtyRect) +{ + NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed"); + + // Now obtain the properties for our cell. + PrefillPropertyArray(-1, aColumn); + nsAutoString properties; + mView->GetColumnProperties(aColumn, properties); + nsTreeUtils::TokenizeProperties(properties, mScratchArray); + + // Resolve style for the column. It contains all the info we need to lay ourselves + // out and to paint. + nsStyleContext* colContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecolumn); + + // Obtain the margins for the cell and then deflate our rect by that + // amount. The cell is assumed to be contained within the deflated rect. + nsRect colRect(aColumnRect); + nsMargin colMargin; + colContext->StyleMargin()->GetMargin(colMargin); + colRect.Deflate(colMargin); + + return PaintBackgroundLayer(colContext, aPresContext, aRenderingContext, + colRect, aDirtyRect); +} + +DrawResult +nsTreeBodyFrame::PaintRow(int32_t aRowIndex, + const nsRect& aRowRect, + nsPresContext* aPresContext, + nsRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nsPoint aPt) +{ + // We have been given a rect for our row. We treat this row like a full-blown + // frame, meaning that it can have borders, margins, padding, and a background. + + // Without a view, we have no data. Check for this up front. + if (!mView) { + return DrawResult::SUCCESS; + } + + nsresult rv; + + // Now obtain the properties for our row. + // XXX Automatically fill in the following props: open, closed, container, leaf, selected, focused + PrefillPropertyArray(aRowIndex, nullptr); + + nsAutoString properties; + mView->GetRowProperties(aRowIndex, properties); + nsTreeUtils::TokenizeProperties(properties, mScratchArray); + + // Resolve style for the row. It contains all the info we need to lay ourselves + // out and to paint. + nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow); + + // Obtain the margins for the row and then deflate our rect by that + // amount. The row is assumed to be contained within the deflated rect. + nsRect rowRect(aRowRect); + nsMargin rowMargin; + rowContext->StyleMargin()->GetMargin(rowMargin); + rowRect.Deflate(rowMargin); + + DrawResult result = DrawResult::SUCCESS; + + // Paint our borders and background for our row rect. + nsITheme* theme = nullptr; + auto appearance = rowContext->StyleDisplay()->mAppearance; + if (appearance) { + theme = aPresContext->GetTheme(); + } + gfxContext* ctx = aRenderingContext.ThebesContext(); + // Save the current font smoothing background color in case we change it. + Color originalColor(ctx->GetFontSmoothingBackgroundColor()); + if (theme && theme->ThemeSupportsWidget(aPresContext, nullptr, appearance)) { + nscolor color; + if (theme->WidgetProvidesFontSmoothingBackgroundColor(this, appearance, + &color)) { + // Set the font smoothing background color provided by the widget. + ctx->SetFontSmoothingBackgroundColor(ToDeviceColor(color)); + } + nsRect dirty; + dirty.IntersectRect(rowRect, aDirtyRect); + theme->DrawWidgetBackground(&aRenderingContext, this, appearance, rowRect, + dirty); + } else { + result &= PaintBackgroundLayer(rowContext, aPresContext, aRenderingContext, + rowRect, aDirtyRect); + } + + // Adjust the rect for its border and padding. + nsRect originalRowRect = rowRect; + AdjustForBorderPadding(rowContext, rowRect); + + bool isSeparator = false; + mView->IsSeparator(aRowIndex, &isSeparator); + if (isSeparator) { + // The row is a separator. + + nscoord primaryX = rowRect.x; + nsTreeColumn* primaryCol = mColumns->GetPrimaryColumn(); + if (primaryCol) { + // Paint the primary cell. + nsRect cellRect; + rv = primaryCol->GetRect(this, rowRect.y, rowRect.height, &cellRect); + if (NS_FAILED(rv)) { + NS_NOTREACHED("primary column is invalid"); + return result; + } + + if (OffsetForHorzScroll(cellRect, false)) { + cellRect.x += aPt.x; + nsRect dirtyRect; + nsRect checkRect(cellRect.x, originalRowRect.y, + cellRect.width, originalRowRect.height); + if (dirtyRect.IntersectRect(aDirtyRect, checkRect)) { + result &= PaintCell(aRowIndex, primaryCol, cellRect, aPresContext, + aRenderingContext, aDirtyRect, primaryX, aPt); + } + } + + // Paint the left side of the separator. + nscoord currX; + nsTreeColumn* previousCol = primaryCol->GetPrevious(); + if (previousCol) { + nsRect prevColRect; + rv = previousCol->GetRect(this, 0, 0, &prevColRect); + if (NS_SUCCEEDED(rv)) { + currX = (prevColRect.x - mHorzPosition) + prevColRect.width + aPt.x; + } else { + NS_NOTREACHED("The column before the primary column is invalid"); + currX = rowRect.x; + } + } else { + currX = rowRect.x; + } + + int32_t level; + mView->GetLevel(aRowIndex, &level); + if (level == 0) + currX += mIndentation; + + if (currX > rowRect.x) { + nsRect separatorRect(rowRect); + separatorRect.width -= rowRect.x + rowRect.width - currX; + result &= PaintSeparator(aRowIndex, separatorRect, aPresContext, + aRenderingContext, aDirtyRect); + } + } + + // Paint the right side (whole) separator. + nsRect separatorRect(rowRect); + if (primaryX > rowRect.x) { + separatorRect.width -= primaryX - rowRect.x; + separatorRect.x += primaryX - rowRect.x; + } + result &= PaintSeparator(aRowIndex, separatorRect, aPresContext, + aRenderingContext, aDirtyRect); + } + else { + // Now loop over our cells. Only paint a cell if it intersects with our dirty rect. + for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol; + currCol = currCol->GetNext()) { + nsRect cellRect; + rv = currCol->GetRect(this, rowRect.y, rowRect.height, &cellRect); + // Don't paint cells in hidden columns. + if (NS_FAILED(rv) || cellRect.width == 0) + continue; + + if (OffsetForHorzScroll(cellRect, false)) { + cellRect.x += aPt.x; + + // for primary columns, use the row's vertical size so that the + // lines get drawn properly + nsRect checkRect = cellRect; + if (currCol->IsPrimary()) + checkRect = nsRect(cellRect.x, originalRowRect.y, + cellRect.width, originalRowRect.height); + + nsRect dirtyRect; + nscoord dummy; + if (dirtyRect.IntersectRect(aDirtyRect, checkRect)) + result &= PaintCell(aRowIndex, currCol, cellRect, aPresContext, + aRenderingContext, aDirtyRect, dummy, aPt); + } + } + } + // If we've changed the font smoothing background color for this row, restore + // the color to the original one. + if (originalColor != ctx->GetFontSmoothingBackgroundColor()) { + ctx->SetFontSmoothingBackgroundColor(originalColor); + } + + return result; +} + +DrawResult +nsTreeBodyFrame::PaintSeparator(int32_t aRowIndex, + const nsRect& aSeparatorRect, + nsPresContext* aPresContext, + nsRenderingContext& aRenderingContext, + const nsRect& aDirtyRect) +{ + // Resolve style for the separator. + nsStyleContext* separatorContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeseparator); + bool useTheme = false; + nsITheme *theme = nullptr; + const nsStyleDisplay* displayData = separatorContext->StyleDisplay(); + if ( displayData->mAppearance ) { + theme = aPresContext->GetTheme(); + if (theme && theme->ThemeSupportsWidget(aPresContext, nullptr, displayData->mAppearance)) + useTheme = true; + } + + DrawResult result = DrawResult::SUCCESS; + + // use -moz-appearance if provided. + if (useTheme) { + nsRect dirty; + dirty.IntersectRect(aSeparatorRect, aDirtyRect); + theme->DrawWidgetBackground(&aRenderingContext, this, + displayData->mAppearance, aSeparatorRect, dirty); + } + else { + const nsStylePosition* stylePosition = separatorContext->StylePosition(); + + // Obtain the height for the separator or use the default value. + nscoord height; + if (stylePosition->mHeight.GetUnit() == eStyleUnit_Coord) + height = stylePosition->mHeight.GetCoordValue(); + else { + // Use default height 2px. + height = nsPresContext::CSSPixelsToAppUnits(2); + } + + // Obtain the margins for the separator and then deflate our rect by that + // amount. The separator is assumed to be contained within the deflated rect. + nsRect separatorRect(aSeparatorRect.x, aSeparatorRect.y, aSeparatorRect.width, height); + nsMargin separatorMargin; + separatorContext->StyleMargin()->GetMargin(separatorMargin); + separatorRect.Deflate(separatorMargin); + + // Center the separator. + separatorRect.y += (aSeparatorRect.height - height) / 2; + + result &= PaintBackgroundLayer(separatorContext, aPresContext, + aRenderingContext, separatorRect, + aDirtyRect); + } + + return result; +} + +DrawResult +nsTreeBodyFrame::PaintCell(int32_t aRowIndex, + nsTreeColumn* aColumn, + const nsRect& aCellRect, + nsPresContext* aPresContext, + nsRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nscoord& aCurrX, + nsPoint aPt) +{ + NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed"); + + // Now obtain the properties for our cell. + // XXX Automatically fill in the following props: open, closed, container, leaf, selected, focused, and the col ID. + PrefillPropertyArray(aRowIndex, aColumn); + nsAutoString properties; + mView->GetCellProperties(aRowIndex, aColumn, properties); + nsTreeUtils::TokenizeProperties(properties, mScratchArray); + + // Resolve style for the cell. It contains all the info we need to lay ourselves + // out and to paint. + nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell); + + bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; + + // Obtain the margins for the cell and then deflate our rect by that + // amount. The cell is assumed to be contained within the deflated rect. + nsRect cellRect(aCellRect); + nsMargin cellMargin; + cellContext->StyleMargin()->GetMargin(cellMargin); + cellRect.Deflate(cellMargin); + + // Paint our borders and background for our row rect. + DrawResult result = PaintBackgroundLayer(cellContext, aPresContext, + aRenderingContext, cellRect, + aDirtyRect); + + // Adjust the rect for its border and padding. + AdjustForBorderPadding(cellContext, cellRect); + + nscoord currX = cellRect.x; + nscoord remainingWidth = cellRect.width; + + // Now we paint the contents of the cells. + // Directionality of the tree determines the order in which we paint. + // NS_STYLE_DIRECTION_LTR means paint from left to right. + // NS_STYLE_DIRECTION_RTL means paint from right to left. + + if (aColumn->IsPrimary()) { + // If we're the primary column, we need to indent and paint the twisty and any connecting lines + // between siblings. + + int32_t level; + mView->GetLevel(aRowIndex, &level); + + if (!isRTL) + currX += mIndentation * level; + remainingWidth -= mIndentation * level; + + // Resolve the style to use for the connecting lines. + nsStyleContext* lineContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeline); + + if (mIndentation && level && + lineContext->StyleVisibility()->IsVisibleOrCollapsed()) { + // Paint the thread lines. + + // Get the size of the twisty. We don't want to paint the twisty + // before painting of connecting lines since it would paint lines over + // the twisty. But we need to leave a place for it. + nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty); + + nsRect imageSize; + nsRect twistyRect(aCellRect); + GetTwistyRect(aRowIndex, aColumn, imageSize, twistyRect, aPresContext, + twistyContext); + + nsMargin twistyMargin; + twistyContext->StyleMargin()->GetMargin(twistyMargin); + twistyRect.Inflate(twistyMargin); + + aRenderingContext.ThebesContext()->Save(); + + const nsStyleBorder* borderStyle = lineContext->StyleBorder(); + // Resolve currentcolor values against the treeline context + nscolor color = lineContext->StyleColor()-> + CalcComplexColor(borderStyle->mBorderLeftColor); + ColorPattern colorPatt(ToDeviceColor(color)); + + uint8_t style = borderStyle->GetBorderStyle(NS_SIDE_LEFT); + StrokeOptions strokeOptions; + nsLayoutUtils::InitDashPattern(strokeOptions, style); + + nscoord srcX = currX + twistyRect.width - mIndentation / 2; + nscoord lineY = (aRowIndex - mTopRowIndex) * mRowHeight + aPt.y; + + DrawTarget* drawTarget = aRenderingContext.GetDrawTarget(); + nsPresContext* pc = PresContext(); + + // Don't paint off our cell. + if (srcX <= cellRect.x + cellRect.width) { + nscoord destX = currX + twistyRect.width; + if (destX > cellRect.x + cellRect.width) + destX = cellRect.x + cellRect.width; + if (isRTL) { + srcX = currX + remainingWidth - (srcX - cellRect.x); + destX = currX + remainingWidth - (destX - cellRect.x); + } + Point p1(pc->AppUnitsToGfxUnits(srcX), + pc->AppUnitsToGfxUnits(lineY + mRowHeight / 2)); + Point p2(pc->AppUnitsToGfxUnits(destX), + pc->AppUnitsToGfxUnits(lineY + mRowHeight / 2)); + SnapLineToDevicePixelsForStroking(p1, p2, *drawTarget, + strokeOptions.mLineWidth); + drawTarget->StrokeLine(p1, p2, colorPatt, strokeOptions); + } + + int32_t currentParent = aRowIndex; + for (int32_t i = level; i > 0; i--) { + if (srcX <= cellRect.x + cellRect.width) { + // Paint full vertical line only if we have next sibling. + bool hasNextSibling; + mView->HasNextSibling(currentParent, aRowIndex, &hasNextSibling); + if (hasNextSibling || i == level) { + Point p1(pc->AppUnitsToGfxUnits(srcX), + pc->AppUnitsToGfxUnits(lineY)); + Point p2; + p2.x = pc->AppUnitsToGfxUnits(srcX); + + if (hasNextSibling) + p2.y = pc->AppUnitsToGfxUnits(lineY + mRowHeight); + else if (i == level) + p2.y = pc->AppUnitsToGfxUnits(lineY + mRowHeight / 2); + + SnapLineToDevicePixelsForStroking(p1, p2, *drawTarget, + strokeOptions.mLineWidth); + drawTarget->StrokeLine(p1, p2, colorPatt, strokeOptions); + } + } + + int32_t parent; + if (NS_FAILED(mView->GetParentIndex(currentParent, &parent)) || parent < 0) + break; + currentParent = parent; + srcX -= mIndentation; + } + + aRenderingContext.ThebesContext()->Restore(); + } + + // Always leave space for the twisty. + nsRect twistyRect(currX, cellRect.y, remainingWidth, cellRect.height); + result &= PaintTwisty(aRowIndex, aColumn, twistyRect, aPresContext, + aRenderingContext, aDirtyRect, remainingWidth, + currX); + } + + // Now paint the icon for our cell. + nsRect iconRect(currX, cellRect.y, remainingWidth, cellRect.height); + nsRect dirtyRect; + if (dirtyRect.IntersectRect(aDirtyRect, iconRect)) { + result &= PaintImage(aRowIndex, aColumn, iconRect, aPresContext, + aRenderingContext, aDirtyRect, remainingWidth, + currX); + } + + // Now paint our element, but only if we aren't a cycler column. + // XXX until we have the ability to load images, allow the view to + // insert text into cycler columns... + if (!aColumn->IsCycler()) { + nsRect elementRect(currX, cellRect.y, remainingWidth, cellRect.height); + nsRect dirtyRect; + if (dirtyRect.IntersectRect(aDirtyRect, elementRect)) { + switch (aColumn->GetType()) { + case nsITreeColumn::TYPE_TEXT: + case nsITreeColumn::TYPE_PASSWORD: + result &= PaintText(aRowIndex, aColumn, elementRect, aPresContext, + aRenderingContext, aDirtyRect, currX); + break; + case nsITreeColumn::TYPE_CHECKBOX: + result &= PaintCheckbox(aRowIndex, aColumn, elementRect, aPresContext, + aRenderingContext, aDirtyRect); + break; + case nsITreeColumn::TYPE_PROGRESSMETER: + int32_t state; + mView->GetProgressMode(aRowIndex, aColumn, &state); + switch (state) { + case nsITreeView::PROGRESS_NORMAL: + case nsITreeView::PROGRESS_UNDETERMINED: + result &= PaintProgressMeter(aRowIndex, aColumn, elementRect, + aPresContext, aRenderingContext, + aDirtyRect); + break; + case nsITreeView::PROGRESS_NONE: + default: + result &= PaintText(aRowIndex, aColumn, elementRect, aPresContext, + aRenderingContext, aDirtyRect, currX); + break; + } + break; + } + } + } + + aCurrX = currX; + + return result; +} + +DrawResult +nsTreeBodyFrame::PaintTwisty(int32_t aRowIndex, + nsTreeColumn* aColumn, + const nsRect& aTwistyRect, + nsPresContext* aPresContext, + nsRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nscoord& aRemainingWidth, + nscoord& aCurrX) +{ + NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed"); + + bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; + nscoord rightEdge = aCurrX + aRemainingWidth; + // Paint the twisty, but only if we are a non-empty container. + bool shouldPaint = false; + bool isContainer = false; + mView->IsContainer(aRowIndex, &isContainer); + if (isContainer) { + bool isContainerEmpty = false; + mView->IsContainerEmpty(aRowIndex, &isContainerEmpty); + if (!isContainerEmpty) + shouldPaint = true; + } + + // Resolve style for the twisty. + nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty); + + // Obtain the margins for the twisty and then deflate our rect by that + // amount. The twisty is assumed to be contained within the deflated rect. + nsRect twistyRect(aTwistyRect); + nsMargin twistyMargin; + twistyContext->StyleMargin()->GetMargin(twistyMargin); + twistyRect.Deflate(twistyMargin); + + nsRect imageSize; + nsITheme* theme = GetTwistyRect(aRowIndex, aColumn, imageSize, twistyRect, + aPresContext, twistyContext); + + // Subtract out the remaining width. This is done even when we don't actually paint a twisty in + // this cell, so that cells in different rows still line up. + nsRect copyRect(twistyRect); + copyRect.Inflate(twistyMargin); + aRemainingWidth -= copyRect.width; + if (!isRTL) + aCurrX += copyRect.width; + + DrawResult result = DrawResult::SUCCESS; + + if (shouldPaint) { + // Paint our borders and background for our image rect. + result &= PaintBackgroundLayer(twistyContext, aPresContext, + aRenderingContext, twistyRect, + aDirtyRect); + + if (theme) { + if (isRTL) + twistyRect.x = rightEdge - twistyRect.width; + // yeah, I know it says we're drawing a background, but a twisty is really a fg + // object since it doesn't have anything that gecko would want to draw over it. Besides, + // we have to prevent imagelib from drawing it. + nsRect dirty; + dirty.IntersectRect(twistyRect, aDirtyRect); + theme->DrawWidgetBackground(&aRenderingContext, this, + twistyContext->StyleDisplay()->mAppearance, twistyRect, dirty); + } + else { + // Time to paint the twisty. + // Adjust the rect for its border and padding. + nsMargin bp(0,0,0,0); + GetBorderPadding(twistyContext, bp); + twistyRect.Deflate(bp); + if (isRTL) + twistyRect.x = rightEdge - twistyRect.width; + imageSize.Deflate(bp); + + // Get the image for drawing. + nsCOMPtr image; + bool useImageRegion = true; + GetImage(aRowIndex, aColumn, true, twistyContext, useImageRegion, getter_AddRefs(image)); + if (image) { + nsPoint pt = twistyRect.TopLeft(); + + // Center the image. XXX Obey vertical-align style prop? + if (imageSize.height < twistyRect.height) { + pt.y += (twistyRect.height - imageSize.height)/2; + } + + // Paint the image. + result &= + nsLayoutUtils::DrawSingleUnscaledImage( + *aRenderingContext.ThebesContext(), aPresContext, image, + SamplingFilter::POINT, pt, &aDirtyRect, + imgIContainer::FLAG_NONE, &imageSize); + } + } + } + + return result; +} + +DrawResult +nsTreeBodyFrame::PaintImage(int32_t aRowIndex, + nsTreeColumn* aColumn, + const nsRect& aImageRect, + nsPresContext* aPresContext, + nsRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nscoord& aRemainingWidth, + nscoord& aCurrX) +{ + NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed"); + + bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; + nscoord rightEdge = aCurrX + aRemainingWidth; + // Resolve style for the image. + nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage); + + // Obtain opacity value for the image. + float opacity = imageContext->StyleEffects()->mOpacity; + + // Obtain the margins for the image and then deflate our rect by that + // amount. The image is assumed to be contained within the deflated rect. + nsRect imageRect(aImageRect); + nsMargin imageMargin; + imageContext->StyleMargin()->GetMargin(imageMargin); + imageRect.Deflate(imageMargin); + + // Get the image. + bool useImageRegion = true; + nsCOMPtr image; + GetImage(aRowIndex, aColumn, false, imageContext, useImageRegion, getter_AddRefs(image)); + + // Get the image destination size. + nsSize imageDestSize = GetImageDestSize(imageContext, useImageRegion, image); + if (!imageDestSize.width || !imageDestSize.height) { + return DrawResult::SUCCESS; + } + + // Get the borders and padding. + nsMargin bp(0,0,0,0); + GetBorderPadding(imageContext, bp); + + // destRect will be passed as the aDestRect argument in the DrawImage method. + // Start with the imageDestSize width and height. + nsRect destRect(0, 0, imageDestSize.width, imageDestSize.height); + // Inflate destRect for borders and padding so that we can compare/adjust + // with respect to imageRect. + destRect.Inflate(bp); + + // The destRect width and height have not been adjusted to fit within the + // cell width and height. + // We must adjust the width even if image is null, because the width is used + // to update the aRemainingWidth and aCurrX values. + // Since the height isn't used unless the image is not null, we will adjust + // the height inside the if (image) block below. + + if (destRect.width > imageRect.width) { + // The destRect is too wide to fit within the cell width. + // Adjust destRect width to fit within the cell width. + destRect.width = imageRect.width; + } + else { + // The cell is wider than the destRect. + // In a cycler column, the image is centered horizontally. + if (!aColumn->IsCycler()) { + // If this column is not a cycler, we won't center the image horizontally. + // We adjust the imageRect width so that the image is placed at the start + // of the cell. + imageRect.width = destRect.width; + } + } + + DrawResult result = DrawResult::SUCCESS; + + if (image) { + if (isRTL) + imageRect.x = rightEdge - imageRect.width; + // Paint our borders and background for our image rect + result &= PaintBackgroundLayer(imageContext, aPresContext, + aRenderingContext, imageRect, + aDirtyRect); + + // The destRect x and y have not been set yet. Let's do that now. + // Initially, we use the imageRect x and y. + destRect.x = imageRect.x; + destRect.y = imageRect.y; + + if (destRect.width < imageRect.width) { + // The destRect width is smaller than the cell width. + // Center the image horizontally in the cell. + // Adjust the destRect x accordingly. + destRect.x += (imageRect.width - destRect.width)/2; + } + + // Now it's time to adjust the destRect height to fit within the cell height. + if (destRect.height > imageRect.height) { + // The destRect height is larger than the cell height. + // Adjust destRect height to fit within the cell height. + destRect.height = imageRect.height; + } + else if (destRect.height < imageRect.height) { + // The destRect height is smaller than the cell height. + // Center the image vertically in the cell. + // Adjust the destRect y accordingly. + destRect.y += (imageRect.height - destRect.height)/2; + } + + // It's almost time to paint the image. + // Deflate destRect for the border and padding. + destRect.Deflate(bp); + + // Compute the area where our whole image would be mapped, to get the + // desired subregion onto our actual destRect: + nsRect wholeImageDest; + CSSIntSize rawImageCSSIntSize; + if (NS_SUCCEEDED(image->GetWidth(&rawImageCSSIntSize.width)) && + NS_SUCCEEDED(image->GetHeight(&rawImageCSSIntSize.height))) { + // Get the image source rectangle - the rectangle containing the part of + // the image that we are going to display. sourceRect will be passed as + // the aSrcRect argument in the DrawImage method. + nsRect sourceRect = GetImageSourceRect(imageContext, useImageRegion, image); + + // Let's say that the image is 100 pixels tall and that the CSS has + // specified that the destination height should be 50 pixels tall. Let's + // say that the cell height is only 20 pixels. So, in those 20 visible + // pixels, we want to see the top 20/50ths of the image. So, the + // sourceRect.height should be 100 * 20 / 50, which is 40 pixels. + // Essentially, we are scaling the image as dictated by the CSS + // destination height and width, and we are then clipping the scaled + // image by the cell width and height. + nsSize rawImageSize(CSSPixel::ToAppUnits(rawImageCSSIntSize)); + wholeImageDest = + nsLayoutUtils::GetWholeImageDestination(rawImageSize, sourceRect, + nsRect(destRect.TopLeft(), + imageDestSize)); + } else { + // GetWidth/GetHeight failed, so we can't easily map a subregion of the + // source image onto the destination area. + // * If this happens with a RasterImage, it probably means the image is + // in an error state, and we shouldn't draw anything. Hence, we leave + // wholeImageDest as an empty rect (its initial state). + // * If this happens with a VectorImage, it probably means the image has + // no explicit width or height attribute -- but we can still proceed and + // just treat the destination area as our whole SVG image area. Hence, we + // set wholeImageDest to the full destRect. + if (image->GetType() == imgIContainer::TYPE_VECTOR) { + wholeImageDest = destRect; + } + } + + gfxContext* ctx = aRenderingContext.ThebesContext(); + if (opacity != 1.0f) { + ctx->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity); + } + + result &= + nsLayoutUtils::DrawImage(*ctx, aPresContext, image, + nsLayoutUtils::GetSamplingFilterForFrame(this), + wholeImageDest, destRect, destRect.TopLeft(), aDirtyRect, + imgIContainer::FLAG_NONE); + + if (opacity != 1.0f) { + ctx->PopGroupAndBlend(); + } + } + + // Update the aRemainingWidth and aCurrX values. + imageRect.Inflate(imageMargin); + aRemainingWidth -= imageRect.width; + if (!isRTL) { + aCurrX += imageRect.width; + } + + return result; +} + +// Disable PGO for PaintText because MSVC 2015 seems to have decided +// that it can null out the alreadyAddRefed used to +// initialize fontMet after storing fontMet on the stack in the same +// space, overwriting fontMet's stack storage with null. +#ifdef _MSC_VER +# pragma optimize("g", off) +#endif +DrawResult +nsTreeBodyFrame::PaintText(int32_t aRowIndex, + nsTreeColumn* aColumn, + const nsRect& aTextRect, + nsPresContext* aPresContext, + nsRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nscoord& aCurrX) +{ + NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed"); + + bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; + + // Now obtain the text for our cell. + nsAutoString text; + mView->GetCellText(aRowIndex, aColumn, text); + + if (aColumn->Type() == nsITreeColumn::TYPE_PASSWORD) { + TextEditRules::FillBufWithPWChars(&text, text.Length()); + } + + // We're going to paint this text so we need to ensure bidi is enabled if + // necessary + CheckTextForBidi(text); + + DrawResult result = DrawResult::SUCCESS; + + if (text.Length() == 0) { + // Don't paint an empty string. XXX What about background/borders? Still paint? + return result; + } + + int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel(); + DrawTarget* drawTarget = aRenderingContext.GetDrawTarget(); + + // Resolve style for the text. It contains all the info we need to lay ourselves + // out and to paint. + nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext); + + // Obtain opacity value for the image. + float opacity = textContext->StyleEffects()->mOpacity; + + // Obtain the margins for the text and then deflate our rect by that + // amount. The text is assumed to be contained within the deflated rect. + nsRect textRect(aTextRect); + nsMargin textMargin; + textContext->StyleMargin()->GetMargin(textMargin); + textRect.Deflate(textMargin); + + // Adjust the rect for its border and padding. + nsMargin bp(0,0,0,0); + GetBorderPadding(textContext, bp); + textRect.Deflate(bp); + + // Compute our text size. + RefPtr fontMet = + nsLayoutUtils::GetFontMetricsForStyleContext(textContext); + + nscoord height = fontMet->MaxHeight(); + nscoord baseline = fontMet->MaxAscent(); + + // Center the text. XXX Obey vertical-align style prop? + if (height < textRect.height) { + textRect.y += (textRect.height - height)/2; + textRect.height = height; + } + + // Set our font. + AdjustForCellText(text, aRowIndex, aColumn, aRenderingContext, *fontMet, textRect); + textRect.Inflate(bp); + + // Subtract out the remaining width. + if (!isRTL) + aCurrX += textRect.width + textMargin.LeftRight(); + + result &= PaintBackgroundLayer(textContext, aPresContext, aRenderingContext, + textRect, aDirtyRect); + + // Time to paint our text. + textRect.Deflate(bp); + + // Set our color. + ColorPattern color(ToDeviceColor(textContext->StyleColor()->mColor)); + + // Draw decorations. + uint8_t decorations = textContext->StyleTextReset()->mTextDecorationLine; + + nscoord offset; + nscoord size; + if (decorations & (NS_STYLE_TEXT_DECORATION_LINE_OVERLINE | + NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE)) { + fontMet->GetUnderline(offset, size); + if (decorations & NS_STYLE_TEXT_DECORATION_LINE_OVERLINE) { + nsRect r(textRect.x, textRect.y, textRect.width, size); + Rect devPxRect = + NSRectToSnappedRect(r, appUnitsPerDevPixel, *drawTarget); + drawTarget->FillRect(devPxRect, color); + } + if (decorations & NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE) { + nsRect r(textRect.x, textRect.y + baseline - offset, + textRect.width, size); + Rect devPxRect = + NSRectToSnappedRect(r, appUnitsPerDevPixel, *drawTarget); + drawTarget->FillRect(devPxRect, color); + } + } + if (decorations & NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) { + fontMet->GetStrikeout(offset, size); + nsRect r(textRect.x, textRect.y + baseline - offset, textRect.width, size); + Rect devPxRect = + NSRectToSnappedRect(r, appUnitsPerDevPixel, *drawTarget); + drawTarget->FillRect(devPxRect, color); + } + nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell); + + gfxContext* ctx = aRenderingContext.ThebesContext(); + if (opacity != 1.0f) { + ctx->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity); + } + + ctx->SetColor(Color::FromABGR(textContext->StyleColor()->mColor)); + nsLayoutUtils::DrawString(this, *fontMet, &aRenderingContext, text.get(), + text.Length(), + textRect.TopLeft() + nsPoint(0, baseline), + cellContext); + + if (opacity != 1.0f) { + ctx->PopGroupAndBlend(); + } + + return result; +} +#ifdef _MSC_VER +# pragma optimize("", on) +#endif + +DrawResult +nsTreeBodyFrame::PaintCheckbox(int32_t aRowIndex, + nsTreeColumn* aColumn, + const nsRect& aCheckboxRect, + nsPresContext* aPresContext, + nsRenderingContext& aRenderingContext, + const nsRect& aDirtyRect) +{ + NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed"); + + // Resolve style for the checkbox. + nsStyleContext* checkboxContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecheckbox); + + nscoord rightEdge = aCheckboxRect.XMost(); + + // Obtain the margins for the checkbox and then deflate our rect by that + // amount. The checkbox is assumed to be contained within the deflated rect. + nsRect checkboxRect(aCheckboxRect); + nsMargin checkboxMargin; + checkboxContext->StyleMargin()->GetMargin(checkboxMargin); + checkboxRect.Deflate(checkboxMargin); + + nsRect imageSize = GetImageSize(aRowIndex, aColumn, true, checkboxContext); + + if (imageSize.height > checkboxRect.height) + imageSize.height = checkboxRect.height; + if (imageSize.width > checkboxRect.width) + imageSize.width = checkboxRect.width; + + if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) + checkboxRect.x = rightEdge - checkboxRect.width; + + // Paint our borders and background for our image rect. + DrawResult result = PaintBackgroundLayer(checkboxContext, aPresContext, + aRenderingContext, checkboxRect, + aDirtyRect); + + // Time to paint the checkbox. + // Adjust the rect for its border and padding. + nsMargin bp(0,0,0,0); + GetBorderPadding(checkboxContext, bp); + checkboxRect.Deflate(bp); + + // Get the image for drawing. + nsCOMPtr image; + bool useImageRegion = true; + GetImage(aRowIndex, aColumn, true, checkboxContext, useImageRegion, getter_AddRefs(image)); + if (image) { + nsPoint pt = checkboxRect.TopLeft(); + + if (imageSize.height < checkboxRect.height) { + pt.y += (checkboxRect.height - imageSize.height)/2; + } + + if (imageSize.width < checkboxRect.width) { + pt.x += (checkboxRect.width - imageSize.width)/2; + } + + // Paint the image. + result &= + nsLayoutUtils::DrawSingleUnscaledImage(*aRenderingContext.ThebesContext(), + aPresContext, + image, SamplingFilter::POINT, pt, &aDirtyRect, + imgIContainer::FLAG_NONE, &imageSize); + } + + return result; +} + +DrawResult +nsTreeBodyFrame::PaintProgressMeter(int32_t aRowIndex, + nsTreeColumn* aColumn, + const nsRect& aProgressMeterRect, + nsPresContext* aPresContext, + nsRenderingContext& aRenderingContext, + const nsRect& aDirtyRect) +{ + NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed"); + + // Resolve style for the progress meter. It contains all the info we need + // to lay ourselves out and to paint. + nsStyleContext* meterContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeprogressmeter); + + // Obtain the margins for the progress meter and then deflate our rect by that + // amount. The progress meter is assumed to be contained within the deflated + // rect. + nsRect meterRect(aProgressMeterRect); + nsMargin meterMargin; + meterContext->StyleMargin()->GetMargin(meterMargin); + meterRect.Deflate(meterMargin); + + // Paint our borders and background for our progress meter rect. + DrawResult result = PaintBackgroundLayer(meterContext, aPresContext, + aRenderingContext, meterRect, + aDirtyRect); + + // Time to paint our progress. + int32_t state; + mView->GetProgressMode(aRowIndex, aColumn, &state); + if (state == nsITreeView::PROGRESS_NORMAL) { + // Adjust the rect for its border and padding. + AdjustForBorderPadding(meterContext, meterRect); + + // Now obtain the value for our cell. + nsAutoString value; + mView->GetCellValue(aRowIndex, aColumn, value); + + nsresult rv; + int32_t intValue = value.ToInteger(&rv); + if (intValue < 0) + intValue = 0; + else if (intValue > 100) + intValue = 100; + + nscoord meterWidth = NSToCoordRound((float)intValue / 100 * meterRect.width); + if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) + meterRect.x += meterRect.width - meterWidth; // right align + meterRect.width = meterWidth; + bool useImageRegion = true; + nsCOMPtr image; + GetImage(aRowIndex, aColumn, true, meterContext, useImageRegion, getter_AddRefs(image)); + if (image) { + int32_t width, height; + image->GetWidth(&width); + image->GetHeight(&height); + nsSize size(width*nsDeviceContext::AppUnitsPerCSSPixel(), + height*nsDeviceContext::AppUnitsPerCSSPixel()); + result &= + nsLayoutUtils::DrawImage(*aRenderingContext.ThebesContext(), + aPresContext, image, + nsLayoutUtils::GetSamplingFilterForFrame(this), + nsRect(meterRect.TopLeft(), size), meterRect, meterRect.TopLeft(), + aDirtyRect, imgIContainer::FLAG_NONE); + } else { + DrawTarget* drawTarget = aRenderingContext.GetDrawTarget(); + int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel(); + Rect rect = + NSRectToSnappedRect(meterRect, appUnitsPerDevPixel, *drawTarget); + ColorPattern color(ToDeviceColor(meterContext->StyleColor()->mColor)); + drawTarget->FillRect(rect, color); + } + } + else if (state == nsITreeView::PROGRESS_UNDETERMINED) { + // Adjust the rect for its border and padding. + AdjustForBorderPadding(meterContext, meterRect); + + bool useImageRegion = true; + nsCOMPtr image; + GetImage(aRowIndex, aColumn, true, meterContext, useImageRegion, getter_AddRefs(image)); + if (image) { + int32_t width, height; + image->GetWidth(&width); + image->GetHeight(&height); + nsSize size(width*nsDeviceContext::AppUnitsPerCSSPixel(), + height*nsDeviceContext::AppUnitsPerCSSPixel()); + result &= + nsLayoutUtils::DrawImage(*aRenderingContext.ThebesContext(), + aPresContext, image, + nsLayoutUtils::GetSamplingFilterForFrame(this), + nsRect(meterRect.TopLeft(), size), meterRect, meterRect.TopLeft(), + aDirtyRect, imgIContainer::FLAG_NONE); + } + } + + return result; +} + + +DrawResult +nsTreeBodyFrame::PaintDropFeedback(const nsRect& aDropFeedbackRect, + nsPresContext* aPresContext, + nsRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nsPoint aPt) +{ + // Paint the drop feedback in between rows. + + nscoord currX; + + // Adjust for the primary cell. + nsTreeColumn* primaryCol = mColumns->GetPrimaryColumn(); + + if (primaryCol) { +#ifdef DEBUG + nsresult rv = +#endif + primaryCol->GetXInTwips(this, &currX); + NS_ASSERTION(NS_SUCCEEDED(rv), "primary column is invalid?"); + + currX += aPt.x - mHorzPosition; + } else { + currX = aDropFeedbackRect.x; + } + + PrefillPropertyArray(mSlots->mDropRow, primaryCol); + + // Resolve the style to use for the drop feedback. + nsStyleContext* feedbackContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreedropfeedback); + + DrawResult result = DrawResult::SUCCESS; + + // Paint only if it is visible. + if (feedbackContext->StyleVisibility()->IsVisibleOrCollapsed()) { + int32_t level; + mView->GetLevel(mSlots->mDropRow, &level); + + // If our previous or next row has greater level use that for + // correct visual indentation. + if (mSlots->mDropOrient == nsITreeView::DROP_BEFORE) { + if (mSlots->mDropRow > 0) { + int32_t previousLevel; + mView->GetLevel(mSlots->mDropRow - 1, &previousLevel); + if (previousLevel > level) + level = previousLevel; + } + } + else { + if (mSlots->mDropRow < mRowCount - 1) { + int32_t nextLevel; + mView->GetLevel(mSlots->mDropRow + 1, &nextLevel); + if (nextLevel > level) + level = nextLevel; + } + } + + currX += mIndentation * level; + + if (primaryCol){ + nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty); + nsRect imageSize; + nsRect twistyRect; + GetTwistyRect(mSlots->mDropRow, primaryCol, imageSize, twistyRect, + aPresContext, twistyContext); + nsMargin twistyMargin; + twistyContext->StyleMargin()->GetMargin(twistyMargin); + twistyRect.Inflate(twistyMargin); + currX += twistyRect.width; + } + + const nsStylePosition* stylePosition = feedbackContext->StylePosition(); + + // Obtain the width for the drop feedback or use default value. + nscoord width; + if (stylePosition->mWidth.GetUnit() == eStyleUnit_Coord) + width = stylePosition->mWidth.GetCoordValue(); + else { + // Use default width 50px. + width = nsPresContext::CSSPixelsToAppUnits(50); + } + + // Obtain the height for the drop feedback or use default value. + nscoord height; + if (stylePosition->mHeight.GetUnit() == eStyleUnit_Coord) + height = stylePosition->mHeight.GetCoordValue(); + else { + // Use default height 2px. + height = nsPresContext::CSSPixelsToAppUnits(2); + } + + // Obtain the margins for the drop feedback and then deflate our rect + // by that amount. + nsRect feedbackRect(currX, aDropFeedbackRect.y, width, height); + nsMargin margin; + feedbackContext->StyleMargin()->GetMargin(margin); + feedbackRect.Deflate(margin); + + feedbackRect.y += (aDropFeedbackRect.height - height) / 2; + + // Finally paint the drop feedback. + result &= PaintBackgroundLayer(feedbackContext, aPresContext, + aRenderingContext, feedbackRect, + aDirtyRect); + } + + return result; +} + +DrawResult +nsTreeBodyFrame::PaintBackgroundLayer(nsStyleContext* aStyleContext, + nsPresContext* aPresContext, + nsRenderingContext& aRenderingContext, + const nsRect& aRect, + const nsRect& aDirtyRect) +{ + const nsStyleBorder* myBorder = aStyleContext->StyleBorder(); + nsCSSRendering::PaintBGParams params = + nsCSSRendering::PaintBGParams::ForAllLayers(*aPresContext, aRenderingContext, + aDirtyRect, aRect, this, + nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES); + DrawResult result = + nsCSSRendering::PaintBackgroundWithSC(params, aStyleContext, *myBorder); + + result &= + nsCSSRendering::PaintBorderWithStyleBorder(aPresContext, aRenderingContext, + this, aDirtyRect, aRect, + *myBorder, mStyleContext, + PaintBorderFlags::SYNC_DECODE_IMAGES); + + nsCSSRendering::PaintOutline(aPresContext, aRenderingContext, this, + aDirtyRect, aRect, aStyleContext); + + return result; +} + +// Scrolling +nsresult +nsTreeBodyFrame::EnsureRowIsVisible(int32_t aRow) +{ + ScrollParts parts = GetScrollParts(); + nsresult rv = EnsureRowIsVisibleInternal(parts, aRow); + NS_ENSURE_SUCCESS(rv, rv); + UpdateScrollbars(parts); + return rv; +} + +nsresult nsTreeBodyFrame::EnsureRowIsVisibleInternal(const ScrollParts& aParts, int32_t aRow) +{ + if (!mView || !mPageLength) + return NS_OK; + + if (mTopRowIndex <= aRow && mTopRowIndex+mPageLength > aRow) + return NS_OK; + + if (aRow < mTopRowIndex) + ScrollToRowInternal(aParts, aRow); + else { + // Bring it just on-screen. + int32_t distance = aRow - (mTopRowIndex+mPageLength)+1; + ScrollToRowInternal(aParts, mTopRowIndex+distance); + } + + return NS_OK; +} + +nsresult +nsTreeBodyFrame::EnsureCellIsVisible(int32_t aRow, nsITreeColumn* aCol) +{ + RefPtr col = GetColumnImpl(aCol); + if (!col) + return NS_ERROR_INVALID_ARG; + + ScrollParts parts = GetScrollParts(); + + nscoord result = -1; + nsresult rv; + + nscoord columnPos; + rv = col->GetXInTwips(this, &columnPos); + if(NS_FAILED(rv)) return rv; + + nscoord columnWidth; + rv = col->GetWidthInTwips(this, &columnWidth); + if(NS_FAILED(rv)) return rv; + + // If the start of the column is before the + // start of the horizontal view, then scroll + if (columnPos < mHorzPosition) + result = columnPos; + // If the end of the column is past the end of + // the horizontal view, then scroll + else if ((columnPos + columnWidth) > (mHorzPosition + mInnerBox.width)) + result = ((columnPos + columnWidth) - (mHorzPosition + mInnerBox.width)) + mHorzPosition; + + if (result != -1) { + rv = ScrollHorzInternal(parts, result); + if(NS_FAILED(rv)) return rv; + } + + rv = EnsureRowIsVisibleInternal(parts, aRow); + NS_ENSURE_SUCCESS(rv, rv); + UpdateScrollbars(parts); + return rv; +} + +nsresult +nsTreeBodyFrame::ScrollToCell(int32_t aRow, nsITreeColumn* aCol) +{ + ScrollParts parts = GetScrollParts(); + nsresult rv = ScrollToRowInternal(parts, aRow); + NS_ENSURE_SUCCESS(rv, rv); + + rv = ScrollToColumnInternal(parts, aCol); + NS_ENSURE_SUCCESS(rv, rv); + + UpdateScrollbars(parts); + return rv; +} + +nsresult +nsTreeBodyFrame::ScrollToColumn(nsITreeColumn* aCol) +{ + ScrollParts parts = GetScrollParts(); + nsresult rv = ScrollToColumnInternal(parts, aCol); + NS_ENSURE_SUCCESS(rv, rv); + UpdateScrollbars(parts); + return rv; +} + +nsresult nsTreeBodyFrame::ScrollToColumnInternal(const ScrollParts& aParts, + nsITreeColumn* aCol) +{ + RefPtr col = GetColumnImpl(aCol); + if (!col) + return NS_ERROR_INVALID_ARG; + + nscoord x; + nsresult rv = col->GetXInTwips(this, &x); + if (NS_FAILED(rv)) + return rv; + + return ScrollHorzInternal(aParts, x); +} + +nsresult +nsTreeBodyFrame::ScrollToHorizontalPosition(int32_t aHorizontalPosition) +{ + ScrollParts parts = GetScrollParts(); + int32_t position = nsPresContext::CSSPixelsToAppUnits(aHorizontalPosition); + nsresult rv = ScrollHorzInternal(parts, position); + NS_ENSURE_SUCCESS(rv, rv); + UpdateScrollbars(parts); + return rv; +} + +nsresult +nsTreeBodyFrame::ScrollToRow(int32_t aRow) +{ + ScrollParts parts = GetScrollParts(); + ScrollToRowInternal(parts, aRow); + UpdateScrollbars(parts); + return NS_OK; +} + +nsresult nsTreeBodyFrame::ScrollToRowInternal(const ScrollParts& aParts, int32_t aRow) +{ + ScrollInternal(aParts, aRow); + + return NS_OK; +} + +nsresult +nsTreeBodyFrame::ScrollByLines(int32_t aNumLines) +{ + if (!mView) { + return NS_OK; + } + int32_t newIndex = mTopRowIndex + aNumLines; + ScrollToRow(newIndex); + return NS_OK; +} + +nsresult +nsTreeBodyFrame::ScrollByPages(int32_t aNumPages) +{ + if (!mView) { + return NS_OK; + } + int32_t newIndex = mTopRowIndex + aNumPages * mPageLength; + ScrollToRow(newIndex); + return NS_OK; +} + +nsresult +nsTreeBodyFrame::ScrollInternal(const ScrollParts& aParts, int32_t aRow) +{ + if (!mView) { + return NS_OK; + } + + // Note that we may be "over scrolled" at this point; that is the + // current mTopRowIndex may be larger than mRowCount - mPageLength. + // This can happen when items are removed for example. (bug 1085050) + + int32_t maxTopRowIndex = std::max(0, mRowCount - mPageLength); + aRow = mozilla::clamped(aRow, 0, maxTopRowIndex); + if (aRow == mTopRowIndex) { + return NS_OK; + } + mTopRowIndex = aRow; + Invalidate(); + PostScrollEvent(); + return NS_OK; +} + +nsresult +nsTreeBodyFrame::ScrollHorzInternal(const ScrollParts& aParts, int32_t aPosition) +{ + if (!mView || !aParts.mColumnsScrollFrame || !aParts.mHScrollbar) + return NS_OK; + + if (aPosition == mHorzPosition) + return NS_OK; + + if (aPosition < 0 || aPosition > mHorzWidth) + return NS_OK; + + nsRect bounds = aParts.mColumnsFrame->GetRect(); + if (aPosition > (mHorzWidth - bounds.width)) + aPosition = mHorzWidth - bounds.width; + + mHorzPosition = aPosition; + + Invalidate(); + + // Update the column scroll view + nsWeakFrame weakFrame(this); + aParts.mColumnsScrollFrame->ScrollTo(nsPoint(mHorzPosition, 0), + nsIScrollableFrame::INSTANT); + if (!weakFrame.IsAlive()) { + return NS_ERROR_FAILURE; + } + // And fire off an event about it all + PostScrollEvent(); + return NS_OK; +} + +void +nsTreeBodyFrame::ScrollByPage(nsScrollbarFrame* aScrollbar, int32_t aDirection, + nsIScrollbarMediator::ScrollSnapMode aSnap) +{ + // CSS Scroll Snapping is not enabled for XUL, aSnap is ignored + MOZ_ASSERT(aScrollbar != nullptr); + ScrollByPages(aDirection); +} + +void +nsTreeBodyFrame::ScrollByWhole(nsScrollbarFrame* aScrollbar, int32_t aDirection, + nsIScrollbarMediator::ScrollSnapMode aSnap) +{ + // CSS Scroll Snapping is not enabled for XUL, aSnap is ignored + MOZ_ASSERT(aScrollbar != nullptr); + int32_t newIndex = aDirection < 0 ? 0 : mTopRowIndex; + ScrollToRow(newIndex); +} + +void +nsTreeBodyFrame::ScrollByLine(nsScrollbarFrame* aScrollbar, int32_t aDirection, + nsIScrollbarMediator::ScrollSnapMode aSnap) +{ + // CSS Scroll Snapping is not enabled for XUL, aSnap is ignored + MOZ_ASSERT(aScrollbar != nullptr); + ScrollByLines(aDirection); +} + +void +nsTreeBodyFrame::RepeatButtonScroll(nsScrollbarFrame* aScrollbar) +{ + ScrollParts parts = GetScrollParts(); + int32_t increment = aScrollbar->GetIncrement(); + int32_t direction = 0; + if (increment < 0) { + direction = -1; + } else if (increment > 0) { + direction = 1; + } + bool isHorizontal = aScrollbar->IsXULHorizontal(); + + nsWeakFrame weakFrame(this); + if (isHorizontal) { + int32_t curpos = aScrollbar->MoveToNewPosition(); + if (weakFrame.IsAlive()) { + ScrollHorzInternal(parts, curpos); + } + } else { + ScrollToRowInternal(parts, mTopRowIndex+direction); + } + + if (weakFrame.IsAlive() && mScrollbarActivity) { + mScrollbarActivity->ActivityOccurred(); + } + if (weakFrame.IsAlive()) { + UpdateScrollbars(parts); + } +} + +void +nsTreeBodyFrame::ThumbMoved(nsScrollbarFrame* aScrollbar, + nscoord aOldPos, + nscoord aNewPos) +{ + ScrollParts parts = GetScrollParts(); + + if (aOldPos == aNewPos) + return; + + nsWeakFrame weakFrame(this); + + // Vertical Scrollbar + if (parts.mVScrollbar == aScrollbar) { + nscoord rh = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight); + nscoord newIndex = nsPresContext::AppUnitsToIntCSSPixels(aNewPos); + nscoord newrow = newIndex/rh; + ScrollInternal(parts, newrow); + // Horizontal Scrollbar + } else if (parts.mHScrollbar == aScrollbar) { + int32_t newIndex = nsPresContext::AppUnitsToIntCSSPixels(aNewPos); + ScrollHorzInternal(parts, newIndex); + } + if (weakFrame.IsAlive()) { + UpdateScrollbars(parts); + } +} + +// The style cache. +nsStyleContext* +nsTreeBodyFrame::GetPseudoStyleContext(nsIAtom* aPseudoElement) +{ + return mStyleCache.GetStyleContext(this, PresContext(), mContent, + mStyleContext, aPseudoElement, + mScratchArray); +} + +// Our comparator for resolving our complex pseudos +bool +nsTreeBodyFrame::PseudoMatches(nsCSSSelector* aSelector) +{ + // Iterate the class list. For each item in the list, see if + // it is contained in our scratch array. If we have a miss, then + // we aren't a match. If all items in the class list are + // present in the scratch array, then we have a match. + nsAtomList* curr = aSelector->mClassList; + while (curr) { + if (!mScratchArray.Contains(curr->mAtom)) + return false; + curr = curr->mNext; + } + return true; +} + +nsIContent* +nsTreeBodyFrame::GetBaseElement() +{ + nsIFrame* parent = GetParent(); + while (parent) { + nsIContent* content = parent->GetContent(); + if (content) { + dom::NodeInfo* ni = content->NodeInfo(); + + if (ni->Equals(nsGkAtoms::tree, kNameSpaceID_XUL) || + (ni->Equals(nsGkAtoms::select) && + content->IsHTMLElement())) + return content; + } + + parent = parent->GetParent(); + } + + return nullptr; +} + +nsresult +nsTreeBodyFrame::ClearStyleAndImageCaches() +{ + mStyleCache.Clear(); + CancelImageRequests(); + mImageCache.Clear(); + return NS_OK; +} + +/* virtual */ void +nsTreeBodyFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) +{ + nsLeafBoxFrame::DidSetStyleContext(aOldStyleContext); + + // Clear the style cache; the pointers are no longer even valid + mStyleCache.Clear(); + // XXX The following is hacky, but it's not incorrect, + // and appears to fix a few bugs with style changes, like text zoom and + // dpi changes + mIndentation = GetIndentation(); + mRowHeight = GetRowHeight(); + mStringWidth = -1; +} + +bool +nsTreeBodyFrame::OffsetForHorzScroll(nsRect& rect, bool clip) +{ + rect.x -= mHorzPosition; + + // Scrolled out before + if (rect.XMost() <= mInnerBox.x) + return false; + + // Scrolled out after + if (rect.x > mInnerBox.XMost()) + return false; + + if (clip) { + nscoord leftEdge = std::max(rect.x, mInnerBox.x); + nscoord rightEdge = std::min(rect.XMost(), mInnerBox.XMost()); + rect.x = leftEdge; + rect.width = rightEdge - leftEdge; + + // Should have returned false above + NS_ASSERTION(rect.width >= 0, "horz scroll code out of sync"); + } + + return true; +} + +bool +nsTreeBodyFrame::CanAutoScroll(int32_t aRowIndex) +{ + // Check first for partially visible last row. + if (aRowIndex == mRowCount - 1) { + nscoord y = mInnerBox.y + (aRowIndex - mTopRowIndex) * mRowHeight; + if (y < mInnerBox.height && y + mRowHeight > mInnerBox.height) + return true; + } + + if (aRowIndex > 0 && aRowIndex < mRowCount - 1) + return true; + + return false; +} + +// Given a dom event, figure out which row in the tree the mouse is over, +// if we should drop before/after/on that row or we should auto-scroll. +// Doesn't query the content about if the drag is allowable, that's done elsewhere. +// +// For containers, we break up the vertical space of the row as follows: if in +// the topmost 25%, the drop is _before_ the row the mouse is over; if in the +// last 25%, _after_; in the middle 50%, we consider it a drop _on_ the container. +// +// For non-containers, if the mouse is in the top 50% of the row, the drop is +// _before_ and the bottom 50% _after_ +void +nsTreeBodyFrame::ComputeDropPosition(WidgetGUIEvent* aEvent, + int32_t* aRow, + int16_t* aOrient, + int16_t* aScrollLines) +{ + *aOrient = -1; + *aScrollLines = 0; + + // Convert the event's point to our coordinates. We want it in + // the coordinates of our inner box's coordinates. + nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this); + int32_t xTwips = pt.x - mInnerBox.x; + int32_t yTwips = pt.y - mInnerBox.y; + + *aRow = GetRowAt(xTwips, yTwips); + if (*aRow >=0) { + // Compute the top/bottom of the row in question. + int32_t yOffset = yTwips - mRowHeight * (*aRow - mTopRowIndex); + + bool isContainer = false; + mView->IsContainer (*aRow, &isContainer); + if (isContainer) { + // for a container, use a 25%/50%/25% breakdown + if (yOffset < mRowHeight / 4) + *aOrient = nsITreeView::DROP_BEFORE; + else if (yOffset > mRowHeight - (mRowHeight / 4)) + *aOrient = nsITreeView::DROP_AFTER; + else + *aOrient = nsITreeView::DROP_ON; + } + else { + // for a non-container use a 50%/50% breakdown + if (yOffset < mRowHeight / 2) + *aOrient = nsITreeView::DROP_BEFORE; + else + *aOrient = nsITreeView::DROP_AFTER; + } + } + + if (CanAutoScroll(*aRow)) { + // Get the max value from the look and feel service. + int32_t scrollLinesMax = + LookAndFeel::GetInt(LookAndFeel::eIntID_TreeScrollLinesMax, 0); + scrollLinesMax--; + if (scrollLinesMax < 0) + scrollLinesMax = 0; + + // Determine if we're w/in a margin of the top/bottom of the tree during a drag. + // This will ultimately cause us to scroll, but that's done elsewhere. + nscoord height = (3 * mRowHeight) / 4; + if (yTwips < height) { + // scroll up + *aScrollLines = NSToIntRound(-scrollLinesMax * (1 - (float)yTwips / height) - 1); + } + else if (yTwips > mRect.height - height) { + // scroll down + *aScrollLines = NSToIntRound(scrollLinesMax * (1 - (float)(mRect.height - yTwips) / height) + 1); + } + } +} // ComputeDropPosition + +void +nsTreeBodyFrame::OpenCallback(nsITimer *aTimer, void *aClosure) +{ + nsTreeBodyFrame* self = static_cast(aClosure); + if (self) { + aTimer->Cancel(); + self->mSlots->mTimer = nullptr; + + if (self->mSlots->mDropRow >= 0) { + self->mSlots->mArray.AppendElement(self->mSlots->mDropRow); + self->mView->ToggleOpenState(self->mSlots->mDropRow); + } + } +} + +void +nsTreeBodyFrame::CloseCallback(nsITimer *aTimer, void *aClosure) +{ + nsTreeBodyFrame* self = static_cast(aClosure); + if (self) { + aTimer->Cancel(); + self->mSlots->mTimer = nullptr; + + for (uint32_t i = self->mSlots->mArray.Length(); i--; ) { + if (self->mView) + self->mView->ToggleOpenState(self->mSlots->mArray[i]); + } + self->mSlots->mArray.Clear(); + } +} + +void +nsTreeBodyFrame::LazyScrollCallback(nsITimer *aTimer, void *aClosure) +{ + nsTreeBodyFrame* self = static_cast(aClosure); + if (self) { + aTimer->Cancel(); + self->mSlots->mTimer = nullptr; + + if (self->mView) { + // Set a new timer to scroll the tree repeatedly. + self->CreateTimer(LookAndFeel::eIntID_TreeScrollDelay, + ScrollCallback, nsITimer::TYPE_REPEATING_SLACK, + getter_AddRefs(self->mSlots->mTimer)); + self->ScrollByLines(self->mSlots->mScrollLines); + // ScrollByLines may have deleted |self|. + } + } +} + +void +nsTreeBodyFrame::ScrollCallback(nsITimer *aTimer, void *aClosure) +{ + nsTreeBodyFrame* self = static_cast(aClosure); + if (self) { + // Don't scroll if we are already at the top or bottom of the view. + if (self->mView && self->CanAutoScroll(self->mSlots->mDropRow)) { + self->ScrollByLines(self->mSlots->mScrollLines); + } + else { + aTimer->Cancel(); + self->mSlots->mTimer = nullptr; + } + } +} + +NS_IMETHODIMP +nsTreeBodyFrame::ScrollEvent::Run() +{ + if (mInner) { + mInner->FireScrollEvent(); + } + return NS_OK; +} + + +void +nsTreeBodyFrame::FireScrollEvent() +{ + mScrollEvent.Forget(); + WidgetGUIEvent event(true, eScroll, nullptr); + // scroll events fired at elements don't bubble + event.mFlags.mBubbles = false; + EventDispatcher::Dispatch(GetContent(), PresContext(), &event); +} + +void +nsTreeBodyFrame::PostScrollEvent() +{ + if (mScrollEvent.IsPending()) + return; + + RefPtr ev = new ScrollEvent(this); + if (NS_FAILED(NS_DispatchToCurrentThread(ev))) { + NS_WARNING("failed to dispatch ScrollEvent"); + } else { + mScrollEvent = ev; + } +} + +void +nsTreeBodyFrame::ScrollbarActivityStarted() const +{ + if (mScrollbarActivity) { + mScrollbarActivity->ActivityStarted(); + } +} + +void +nsTreeBodyFrame::ScrollbarActivityStopped() const +{ + if (mScrollbarActivity) { + mScrollbarActivity->ActivityStopped(); + } +} + +void +nsTreeBodyFrame::DetachImageListeners() +{ + mCreatedListeners.Clear(); +} + +void +nsTreeBodyFrame::RemoveTreeImageListener(nsTreeImageListener* aListener) +{ + if (aListener) { + mCreatedListeners.RemoveEntry(aListener); + } +} + +#ifdef ACCESSIBILITY +void +nsTreeBodyFrame::FireRowCountChangedEvent(int32_t aIndex, int32_t aCount) +{ + nsCOMPtr content(GetBaseElement()); + if (!content) + return; + + nsCOMPtr domDoc = do_QueryInterface(content->OwnerDoc()); + if (!domDoc) + return; + + nsCOMPtr event; + domDoc->CreateEvent(NS_LITERAL_STRING("customevent"), + getter_AddRefs(event)); + + nsCOMPtr treeEvent(do_QueryInterface(event)); + if (!treeEvent) + return; + + nsCOMPtr propBag( + do_CreateInstance("@mozilla.org/hash-property-bag;1")); + if (!propBag) + return; + + // Set 'index' data - the row index rows are changed from. + propBag->SetPropertyAsInt32(NS_LITERAL_STRING("index"), aIndex); + + // Set 'count' data - the number of changed rows. + propBag->SetPropertyAsInt32(NS_LITERAL_STRING("count"), aCount); + + RefPtr detailVariant(new nsVariant()); + + detailVariant->SetAsISupports(propBag); + treeEvent->InitCustomEvent(NS_LITERAL_STRING("TreeRowCountChanged"), + true, false, detailVariant); + + event->SetTrusted(true); + + RefPtr asyncDispatcher = + new AsyncEventDispatcher(content, event); + asyncDispatcher->PostDOMEvent(); +} + +void +nsTreeBodyFrame::FireInvalidateEvent(int32_t aStartRowIdx, int32_t aEndRowIdx, + nsITreeColumn *aStartCol, + nsITreeColumn *aEndCol) +{ + nsCOMPtr content(GetBaseElement()); + if (!content) + return; + + nsCOMPtr domDoc = do_QueryInterface(content->OwnerDoc()); + if (!domDoc) + return; + + nsCOMPtr event; + domDoc->CreateEvent(NS_LITERAL_STRING("customevent"), + getter_AddRefs(event)); + + nsCOMPtr treeEvent(do_QueryInterface(event)); + if (!treeEvent) + return; + + nsCOMPtr propBag( + do_CreateInstance("@mozilla.org/hash-property-bag;1")); + if (!propBag) + return; + + if (aStartRowIdx != -1 && aEndRowIdx != -1) { + // Set 'startrow' data - the start index of invalidated rows. + propBag->SetPropertyAsInt32(NS_LITERAL_STRING("startrow"), + aStartRowIdx); + + // Set 'endrow' data - the end index of invalidated rows. + propBag->SetPropertyAsInt32(NS_LITERAL_STRING("endrow"), + aEndRowIdx); + } + + if (aStartCol && aEndCol) { + // Set 'startcolumn' data - the start index of invalidated rows. + int32_t startColIdx = 0; + nsresult rv = aStartCol->GetIndex(&startColIdx); + if (NS_FAILED(rv)) + return; + + propBag->SetPropertyAsInt32(NS_LITERAL_STRING("startcolumn"), + startColIdx); + + // Set 'endcolumn' data - the start index of invalidated rows. + int32_t endColIdx = 0; + rv = aEndCol->GetIndex(&endColIdx); + if (NS_FAILED(rv)) + return; + + propBag->SetPropertyAsInt32(NS_LITERAL_STRING("endcolumn"), + endColIdx); + } + + RefPtr detailVariant(new nsVariant()); + + detailVariant->SetAsISupports(propBag); + treeEvent->InitCustomEvent(NS_LITERAL_STRING("TreeInvalidated"), + true, false, detailVariant); + + event->SetTrusted(true); + + RefPtr asyncDispatcher = + new AsyncEventDispatcher(content, event); + asyncDispatcher->PostDOMEvent(); +} +#endif + +class nsOverflowChecker : public Runnable +{ +public: + explicit nsOverflowChecker(nsTreeBodyFrame* aFrame) : mFrame(aFrame) {} + NS_IMETHOD Run() override + { + if (mFrame.IsAlive()) { + nsTreeBodyFrame* tree = static_cast(mFrame.GetFrame()); + nsTreeBodyFrame::ScrollParts parts = tree->GetScrollParts(); + tree->CheckOverflow(parts); + } + return NS_OK; + } +private: + nsWeakFrame mFrame; +}; + +bool +nsTreeBodyFrame::FullScrollbarsUpdate(bool aNeedsFullInvalidation) +{ + ScrollParts parts = GetScrollParts(); + nsWeakFrame weakFrame(this); + nsWeakFrame weakColumnsFrame(parts.mColumnsFrame); + UpdateScrollbars(parts); + NS_ENSURE_TRUE(weakFrame.IsAlive(), false); + if (aNeedsFullInvalidation) { + Invalidate(); + } + InvalidateScrollbars(parts, weakColumnsFrame); + NS_ENSURE_TRUE(weakFrame.IsAlive(), false); + + // Overflow checking dispatches synchronous events, which can cause infinite + // recursion during reflow. Do the first overflow check synchronously, but + // force any nested checks to round-trip through the event loop. See bug + // 905909. + RefPtr checker = new nsOverflowChecker(this); + if (!mCheckingOverflow) { + nsContentUtils::AddScriptRunner(checker); + } else { + NS_DispatchToCurrentThread(checker); + } + return weakFrame.IsAlive(); +} + +nsresult +nsTreeBodyFrame::OnImageIsAnimated(imgIRequest* aRequest) +{ + nsLayoutUtils::RegisterImageRequest(PresContext(), + aRequest, nullptr); + + return NS_OK; +} diff --git a/layout/xul/tree/nsTreeBodyFrame.h b/layout/xul/tree/nsTreeBodyFrame.h new file mode 100644 index 000000000..9620c8ccb --- /dev/null +++ b/layout/xul/tree/nsTreeBodyFrame.h @@ -0,0 +1,651 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#ifndef nsTreeBodyFrame_h +#define nsTreeBodyFrame_h + +#include "mozilla/Attributes.h" + +#include "nsLeafBoxFrame.h" +#include "nsITreeView.h" +#include "nsICSSPseudoComparator.h" +#include "nsIScrollbarMediator.h" +#include "nsITimer.h" +#include "nsIReflowCallback.h" +#include "nsTArray.h" +#include "nsTreeStyleCache.h" +#include "nsTreeColumns.h" +#include "nsDataHashtable.h" +#include "imgIRequest.h" +#include "imgINotificationObserver.h" +#include "nsScrollbarFrame.h" +#include "nsThreadUtils.h" +#include "mozilla/LookAndFeel.h" + +class nsFontMetrics; +class nsOverflowChecker; +class nsTreeImageListener; + +namespace mozilla { +namespace layout { +class ScrollbarActivity; +} // namespace layout +} // namespace mozilla + +// An entry in the tree's image cache +struct nsTreeImageCacheEntry +{ + nsTreeImageCacheEntry() {} + nsTreeImageCacheEntry(imgIRequest *aRequest, imgINotificationObserver *aListener) + : request(aRequest), listener(aListener) {} + + nsCOMPtr request; + nsCOMPtr listener; +}; + +// The actual frame that paints the cells and rows. +class nsTreeBodyFrame final + : public nsLeafBoxFrame + , public nsICSSPseudoComparator + , public nsIScrollbarMediator + , public nsIReflowCallback +{ + typedef mozilla::layout::ScrollbarActivity ScrollbarActivity; + typedef mozilla::image::DrawResult DrawResult; + +public: + explicit nsTreeBodyFrame(nsStyleContext* aContext); + ~nsTreeBodyFrame(); + + NS_DECL_QUERYFRAME_TARGET(nsTreeBodyFrame) + NS_DECL_QUERYFRAME + NS_DECL_FRAMEARENA_HELPERS + + // Callback handler methods for refresh driver based animations. + // Calls to these functions are forwarded from nsTreeImageListener. These + // mirror how nsImageFrame works. + nsresult OnImageIsAnimated(imgIRequest* aRequest); + + // non-virtual signatures like nsITreeBodyFrame + already_AddRefed Columns() const + { + RefPtr cols = mColumns; + return cols.forget(); + } + already_AddRefed GetExistingView() const + { + nsCOMPtr view = mView; + return view.forget(); + } + nsresult GetView(nsITreeView **aView); + nsresult SetView(nsITreeView *aView); + bool GetFocused() const { return mFocused; } + nsresult SetFocused(bool aFocused); + nsresult GetTreeBody(nsIDOMElement **aElement); + int32_t RowHeight() const; + int32_t RowWidth(); + int32_t GetHorizontalPosition() const; + nsresult GetSelectionRegion(nsIScriptableRegion **aRegion); + int32_t FirstVisibleRow() const { return mTopRowIndex; } + int32_t LastVisibleRow() const { return mTopRowIndex + mPageLength; } + int32_t PageLength() const { return mPageLength; } + nsresult EnsureRowIsVisible(int32_t aRow); + nsresult EnsureCellIsVisible(int32_t aRow, nsITreeColumn *aCol); + nsresult ScrollToRow(int32_t aRow); + nsresult ScrollByLines(int32_t aNumLines); + nsresult ScrollByPages(int32_t aNumPages); + nsresult ScrollToCell(int32_t aRow, nsITreeColumn *aCol); + nsresult ScrollToColumn(nsITreeColumn *aCol); + nsresult ScrollToHorizontalPosition(int32_t aValue); + nsresult Invalidate(); + nsresult InvalidateColumn(nsITreeColumn *aCol); + nsresult InvalidateRow(int32_t aRow); + nsresult InvalidateCell(int32_t aRow, nsITreeColumn *aCol); + nsresult InvalidateRange(int32_t aStart, int32_t aEnd); + nsresult InvalidateColumnRange(int32_t aStart, int32_t aEnd, + nsITreeColumn *aCol); + nsresult GetRowAt(int32_t aX, int32_t aY, int32_t *aValue); + nsresult GetCellAt(int32_t aX, int32_t aY, int32_t *aRow, + nsITreeColumn **aCol, nsACString &aChildElt); + nsresult GetCoordsForCellItem(int32_t aRow, nsITreeColumn *aCol, + const nsACString &aElt, + int32_t *aX, int32_t *aY, + int32_t *aWidth, int32_t *aHeight); + nsresult IsCellCropped(int32_t aRow, nsITreeColumn *aCol, bool *aResult); + nsresult RowCountChanged(int32_t aIndex, int32_t aCount); + nsresult BeginUpdateBatch(); + nsresult EndUpdateBatch(); + nsresult ClearStyleAndImageCaches(); + + void CancelImageRequests(); + + void ManageReflowCallback(const nsRect& aRect, nscoord aHorzWidth); + + virtual nsSize GetXULMinSize(nsBoxLayoutState& aBoxLayoutState) override; + virtual void SetXULBounds(nsBoxLayoutState& aBoxLayoutState, const nsRect& aRect, + bool aRemoveOverflowArea = false) override; + + // nsIReflowCallback + virtual bool ReflowFinished() override; + virtual void ReflowCallbackCanceled() override; + + // nsICSSPseudoComparator + virtual bool PseudoMatches(nsCSSSelector* aSelector) override; + + // nsIScrollbarMediator + virtual void ScrollByPage(nsScrollbarFrame* aScrollbar, int32_t aDirection, + nsIScrollbarMediator::ScrollSnapMode aSnap + = nsIScrollbarMediator::DISABLE_SNAP) override; + virtual void ScrollByWhole(nsScrollbarFrame* aScrollbar, int32_t aDirection, + nsIScrollbarMediator::ScrollSnapMode aSnap + = nsIScrollbarMediator::DISABLE_SNAP) override; + virtual void ScrollByLine(nsScrollbarFrame* aScrollbar, int32_t aDirection, + nsIScrollbarMediator::ScrollSnapMode aSnap + = nsIScrollbarMediator::DISABLE_SNAP) override; + virtual void RepeatButtonScroll(nsScrollbarFrame* aScrollbar) override; + virtual void ThumbMoved(nsScrollbarFrame* aScrollbar, + nscoord aOldPos, + nscoord aNewPos) override; + virtual void ScrollbarReleased(nsScrollbarFrame* aScrollbar) override {} + virtual void VisibilityChanged(bool aVisible) override { Invalidate(); } + virtual nsIFrame* GetScrollbarBox(bool aVertical) override { + ScrollParts parts = GetScrollParts(); + return aVertical ? parts.mVScrollbar : parts.mHScrollbar; + } + virtual void ScrollbarActivityStarted() const override; + virtual void ScrollbarActivityStopped() const override; + virtual bool IsScrollbarOnRight() const override { + return (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR); + } + virtual bool ShouldSuppressScrollbarRepaints() const override { + return false; + } + + // Overridden from nsIFrame to cache our pres context. + virtual void Init(nsIContent* aContent, + nsContainerFrame* aParent, + nsIFrame* aPrevInFlow) override; + virtual void DestroyFrom(nsIFrame* aDestructRoot) override; + + virtual nsresult GetCursor(const nsPoint& aPoint, + nsIFrame::Cursor& aCursor) override; + + virtual nsresult HandleEvent(nsPresContext* aPresContext, + mozilla::WidgetGUIEvent* aEvent, + nsEventStatus* aEventStatus) override; + + virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsRect& aDirtyRect, + const nsDisplayListSet& aLists) override; + + virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) override; + + friend nsIFrame* NS_NewTreeBodyFrame(nsIPresShell* aPresShell); + friend class nsTreeColumn; + + struct ScrollParts { + nsScrollbarFrame* mVScrollbar; + nsCOMPtr mVScrollbarContent; + nsScrollbarFrame* mHScrollbar; + nsCOMPtr mHScrollbarContent; + nsIFrame* mColumnsFrame; + nsIScrollableFrame* mColumnsScrollFrame; + }; + + DrawResult PaintTreeBody(nsRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, nsPoint aPt); + + nsITreeBoxObject* GetTreeBoxObject() const { return mTreeBoxObject; } + + // Get the base element, or