1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "mozilla/dom/ListBoxObject.h"
7 #include "nsCOMPtr.h"
8 #include "nsIFrame.h"
9 #include "nsGkAtoms.h"
10 #include "nsIScrollableFrame.h"
11 #include "nsListBoxBodyFrame.h"
12 #include "ChildIterator.h"
13 #include "mozilla/dom/Element.h"
14 #include "mozilla/dom/ListBoxObjectBinding.h"
15 
16 namespace mozilla {
17 namespace dom {
18 
19 NS_IMPL_ISUPPORTS_INHERITED(ListBoxObject, BoxObject, nsIListBoxObject,
20                             nsPIListBoxObject)
21 
print_insn_detail(mode, insn)22 ListBoxObject::ListBoxObject()
23   : mListBoxBody(nullptr)
24 {
25 }
26 
27 ListBoxObject::~ListBoxObject()
28 {
29 }
30 
31 JSObject* ListBoxObject::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
32 {
33   return ListBoxObjectBinding::Wrap(aCx, this, aGivenProto);
34 }
35 
36 // nsIListBoxObject
37 NS_IMETHODIMP
38 ListBoxObject::GetRowCount(int32_t *aResult)
39 {
40   *aResult = GetRowCount();
41   return NS_OK;
42 }
43 
44 NS_IMETHODIMP
45 ListBoxObject::GetItemAtIndex(int32_t index, nsIDOMElement **_retval)
46 {
47   nsListBoxBodyFrame* body = GetListBoxBody(true);
48   if (body) {
49     return body->GetItemAtIndex(index, _retval);
50   }
51   return NS_OK;
52  }
53 
54 NS_IMETHODIMP
55 ListBoxObject::GetIndexOfItem(nsIDOMElement* aElement, int32_t *aResult)
56 {
57   *aResult = 0;
58 
59   nsListBoxBodyFrame* body = GetListBoxBody(true);
60   if (body) {
61     return body->GetIndexOfItem(aElement, aResult);
62   }
63   return NS_OK;
64 }
65 
66 // ListBoxObject
67 
68 int32_t
69 ListBoxObject::GetRowCount()
70 {
71   nsListBoxBodyFrame* body = GetListBoxBody(true);
72   if (body) {
73     return body->GetRowCount();
74   }
75   return 0;
76 }
77 
78 int32_t
79 ListBoxObject::GetNumberOfVisibleRows()
80 {
81   nsListBoxBodyFrame* body = GetListBoxBody(true);
82   if (body) {
83     return body->GetNumberOfVisibleRows();
84   }
85   return 0;
86 }
87 
88 int32_t
89 ListBoxObject::GetIndexOfFirstVisibleRow()
90 {
91   nsListBoxBodyFrame* body = GetListBoxBody(true);
92   if (body) {
93     return body->GetIndexOfFirstVisibleRow();
94   }
95   return 0;
96 }
97 
98 void
99 ListBoxObject::EnsureIndexIsVisible(int32_t aRowIndex)
100 {
101   nsListBoxBodyFrame* body = GetListBoxBody(true);
102   if (body) {
103     body->EnsureIndexIsVisible(aRowIndex);
104   }
105 }
106 
107 void
108 ListBoxObject::ScrollToIndex(int32_t aRowIndex)
109 {
110   nsListBoxBodyFrame* body = GetListBoxBody(true);
111   if (body) {
112     body->ScrollToIndex(aRowIndex);
113   }
114 }
115 
116 void
117 ListBoxObject::ScrollByLines(int32_t aNumLines)
118 {
119   nsListBoxBodyFrame* body = GetListBoxBody(true);
120   if (body) {
121     body->ScrollByLines(aNumLines);
122   }
123 }
124 
test_class()125 already_AddRefed<Element>
126 ListBoxObject::GetItemAtIndex(int32_t index)
127 {
128   nsCOMPtr<nsIDOMElement> el;
129   GetItemAtIndex(index, getter_AddRefs(el));
130   nsCOMPtr<Element> ret(do_QueryInterface(el));
131   return ret.forget();
132 }
133 
134 int32_t
135 ListBoxObject::GetIndexOfItem(Element& aElement)
136 {
137   int32_t ret;
138   nsCOMPtr<nsIDOMElement> el(do_QueryInterface(&aElement));
139   GetIndexOfItem(el, &ret);
140   return ret;
141 }
142 
143 //////////////////////
144 
145 static nsIContent*
146 FindBodyContent(nsIContent* aParent)
147 {
148   if (aParent->IsXULElement(nsGkAtoms::listboxbody)) {
149     return aParent;
150   }
151 
152   mozilla::dom::FlattenedChildIterator iter(aParent);
153   for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
154     nsIContent* result = FindBodyContent(child);
155     if (result) {
156       return result;
157     }
158   }
159 
160   return nullptr;
161 }
162 
163 nsListBoxBodyFrame*
164 ListBoxObject::GetListBoxBody(bool aFlush)
165 {
166   if (mListBoxBody) {
167     return mListBoxBody;
168   }
169 
170   nsIPresShell* shell = GetPresShell(false);
171   if (!shell) {
172     return nullptr;
173   }
174 
175   nsIFrame* frame = aFlush ?
176                       GetFrame(false) /* does Flush_Frames */ :
177                       mContent->GetPrimaryFrame();
178   if (!frame) {
179     return nullptr;
180   }
181 
182   // Iterate over our content model children looking for the body.
183   nsCOMPtr<nsIContent> content = FindBodyContent(frame->GetContent());
184 
185   if (!content) {
186     return nullptr;
187   }
188 
189   // this frame will be a nsGFXScrollFrame
190   frame = content->GetPrimaryFrame();
191   if (!frame) {
192      return nullptr;
193   }
194 
195   nsIScrollableFrame* scrollFrame = do_QueryFrame(frame);
196   if (!scrollFrame) {
197     return nullptr;
198   }
199 
200   // this frame will be the one we want
201   nsIFrame* yeahBaby = scrollFrame->GetScrolledFrame();
202   if (!yeahBaby) {
203      return nullptr;
204   }
205 
206   // It's a frame. Refcounts are irrelevant.
207   nsListBoxBodyFrame* listBoxBody = do_QueryFrame(yeahBaby);
208   NS_ENSURE_TRUE(listBoxBody &&
209                  listBoxBody->SetBoxObject(this),
210                  nullptr);
211   mListBoxBody = listBoxBody;
212   return mListBoxBody;
213 }
214 
215 void
216 ListBoxObject::Clear()
217 {
218   ClearCachedValues();
219   BoxObject::Clear();
220 }
221 
222 void
223 ListBoxObject::ClearCachedValues()
224 {
225   mListBoxBody = nullptr;
226 }
227 
228 } // namespace dom
229 } // namespace mozilla
230 
231 // Creation Routine ///////////////////////////////////////////////////////////////////////
232 
233 nsresult
234 NS_NewListBoxObject(nsIBoxObject** aResult)
235 {
236   NS_ADDREF(*aResult = new mozilla::dom::ListBoxObject());
237   return NS_OK;
238 }
239