|
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 "nsBoxObject.h" |
|
7 #include "nsCOMPtr.h" |
|
8 #include "nsIDocument.h" |
|
9 #include "nsIPresShell.h" |
|
10 #include "nsPresContext.h" |
|
11 #include "nsIContent.h" |
|
12 #include "nsIFrame.h" |
|
13 #include "nsIDocShell.h" |
|
14 #include "nsReadableUtils.h" |
|
15 #include "nsDOMClassInfoID.h" |
|
16 #include "nsView.h" |
|
17 #ifdef MOZ_XUL |
|
18 #include "nsIDOMXULElement.h" |
|
19 #else |
|
20 #include "nsIDOMElement.h" |
|
21 #endif |
|
22 #include "nsLayoutUtils.h" |
|
23 #include "nsISupportsPrimitives.h" |
|
24 #include "nsSupportsPrimitives.h" |
|
25 #include "mozilla/dom/Element.h" |
|
26 #include "nsComponentManagerUtils.h" |
|
27 |
|
28 using namespace mozilla::dom; |
|
29 |
|
30 // Implementation ///////////////////////////////////////////////////////////////// |
|
31 |
|
32 // Static member variable initialization |
|
33 |
|
34 // Implement our nsISupports methods |
|
35 |
|
36 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsBoxObject) |
|
37 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsBoxObject) |
|
38 |
|
39 DOMCI_DATA(BoxObject, nsBoxObject) |
|
40 |
|
41 // QueryInterface implementation for nsBoxObject |
|
42 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsBoxObject) |
|
43 NS_INTERFACE_MAP_ENTRY(nsIBoxObject) |
|
44 NS_INTERFACE_MAP_ENTRY(nsPIBoxObject) |
|
45 NS_INTERFACE_MAP_ENTRY(nsISupports) |
|
46 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(BoxObject) |
|
47 NS_INTERFACE_MAP_END |
|
48 |
|
49 static PLDHashOperator |
|
50 PropertyTraverser(const nsAString& aKey, nsISupports* aProperty, void* userArg) |
|
51 { |
|
52 nsCycleCollectionTraversalCallback *cb = |
|
53 static_cast<nsCycleCollectionTraversalCallback*>(userArg); |
|
54 |
|
55 cb->NoteXPCOMChild(aProperty); |
|
56 |
|
57 return PL_DHASH_NEXT; |
|
58 } |
|
59 |
|
60 NS_IMPL_CYCLE_COLLECTION_CLASS(nsBoxObject) |
|
61 |
|
62 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsBoxObject) |
|
63 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsBoxObject) |
|
64 if (tmp->mPropertyTable) { |
|
65 tmp->mPropertyTable->EnumerateRead(PropertyTraverser, &cb); |
|
66 } |
|
67 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
68 |
|
69 // Constructors/Destructors |
|
70 nsBoxObject::nsBoxObject(void) |
|
71 :mContent(nullptr) |
|
72 { |
|
73 } |
|
74 |
|
75 nsBoxObject::~nsBoxObject(void) |
|
76 { |
|
77 } |
|
78 |
|
79 NS_IMETHODIMP |
|
80 nsBoxObject::GetElement(nsIDOMElement** aResult) |
|
81 { |
|
82 if (mContent) { |
|
83 return CallQueryInterface(mContent, aResult); |
|
84 } |
|
85 |
|
86 *aResult = nullptr; |
|
87 return NS_OK; |
|
88 } |
|
89 |
|
90 // nsPIBoxObject ////////////////////////////////////////////////////////////////////////// |
|
91 |
|
92 nsresult |
|
93 nsBoxObject::Init(nsIContent* aContent) |
|
94 { |
|
95 mContent = aContent; |
|
96 return NS_OK; |
|
97 } |
|
98 |
|
99 void |
|
100 nsBoxObject::Clear() |
|
101 { |
|
102 mPropertyTable = nullptr; |
|
103 mContent = nullptr; |
|
104 } |
|
105 |
|
106 void |
|
107 nsBoxObject::ClearCachedValues() |
|
108 { |
|
109 } |
|
110 |
|
111 nsIFrame* |
|
112 nsBoxObject::GetFrame(bool aFlushLayout) |
|
113 { |
|
114 nsIPresShell* shell = GetPresShell(aFlushLayout); |
|
115 if (!shell) |
|
116 return nullptr; |
|
117 |
|
118 if (!aFlushLayout) { |
|
119 // If we didn't flush layout when getting the presshell, we should at least |
|
120 // flush to make sure our frame model is up to date. |
|
121 // XXXbz should flush on document, no? Except people call this from |
|
122 // frame code, maybe? |
|
123 shell->FlushPendingNotifications(Flush_Frames); |
|
124 } |
|
125 |
|
126 // The flush might have killed mContent. |
|
127 if (!mContent) { |
|
128 return nullptr; |
|
129 } |
|
130 |
|
131 return mContent->GetPrimaryFrame(); |
|
132 } |
|
133 |
|
134 nsIPresShell* |
|
135 nsBoxObject::GetPresShell(bool aFlushLayout) |
|
136 { |
|
137 if (!mContent) { |
|
138 return nullptr; |
|
139 } |
|
140 |
|
141 nsCOMPtr<nsIDocument> doc = mContent->GetCurrentDoc(); |
|
142 if (!doc) { |
|
143 return nullptr; |
|
144 } |
|
145 |
|
146 if (aFlushLayout) { |
|
147 doc->FlushPendingNotifications(Flush_Layout); |
|
148 } |
|
149 |
|
150 return doc->GetShell(); |
|
151 } |
|
152 |
|
153 nsresult |
|
154 nsBoxObject::GetOffsetRect(nsIntRect& aRect) |
|
155 { |
|
156 aRect.SetRect(0, 0, 0, 0); |
|
157 |
|
158 if (!mContent) |
|
159 return NS_ERROR_NOT_INITIALIZED; |
|
160 |
|
161 // Get the Frame for our content |
|
162 nsIFrame* frame = GetFrame(true); |
|
163 if (frame) { |
|
164 // Get its origin |
|
165 nsPoint origin = frame->GetPositionIgnoringScrolling(); |
|
166 |
|
167 // Find the frame parent whose content is the document element. |
|
168 Element *docElement = mContent->GetCurrentDoc()->GetRootElement(); |
|
169 nsIFrame* parent = frame->GetParent(); |
|
170 for (;;) { |
|
171 // If we've hit the document element, break here |
|
172 if (parent->GetContent() == docElement) { |
|
173 break; |
|
174 } |
|
175 |
|
176 nsIFrame* next = parent->GetParent(); |
|
177 if (!next) { |
|
178 NS_WARNING("We should have hit the document element..."); |
|
179 origin += parent->GetPosition(); |
|
180 break; |
|
181 } |
|
182 |
|
183 // Add the parent's origin to our own to get to the |
|
184 // right coordinate system |
|
185 origin += next->GetPositionOfChildIgnoringScrolling(parent); |
|
186 parent = next; |
|
187 } |
|
188 |
|
189 // For the origin, add in the border for the frame |
|
190 const nsStyleBorder* border = frame->StyleBorder(); |
|
191 origin.x += border->GetComputedBorderWidth(NS_SIDE_LEFT); |
|
192 origin.y += border->GetComputedBorderWidth(NS_SIDE_TOP); |
|
193 |
|
194 // And subtract out the border for the parent |
|
195 const nsStyleBorder* parentBorder = parent->StyleBorder(); |
|
196 origin.x -= parentBorder->GetComputedBorderWidth(NS_SIDE_LEFT); |
|
197 origin.y -= parentBorder->GetComputedBorderWidth(NS_SIDE_TOP); |
|
198 |
|
199 aRect.x = nsPresContext::AppUnitsToIntCSSPixels(origin.x); |
|
200 aRect.y = nsPresContext::AppUnitsToIntCSSPixels(origin.y); |
|
201 |
|
202 // Get the union of all rectangles in this and continuation frames. |
|
203 // It doesn't really matter what we use as aRelativeTo here, since |
|
204 // we only care about the size. Using 'parent' might make things |
|
205 // a bit faster by speeding up the internal GetOffsetTo operations. |
|
206 nsRect rcFrame = nsLayoutUtils::GetAllInFlowRectsUnion(frame, parent); |
|
207 aRect.width = nsPresContext::AppUnitsToIntCSSPixels(rcFrame.width); |
|
208 aRect.height = nsPresContext::AppUnitsToIntCSSPixels(rcFrame.height); |
|
209 } |
|
210 |
|
211 return NS_OK; |
|
212 } |
|
213 |
|
214 nsresult |
|
215 nsBoxObject::GetScreenPosition(nsIntPoint& aPoint) |
|
216 { |
|
217 aPoint.x = aPoint.y = 0; |
|
218 |
|
219 if (!mContent) |
|
220 return NS_ERROR_NOT_INITIALIZED; |
|
221 |
|
222 nsIFrame* frame = GetFrame(true); |
|
223 if (frame) { |
|
224 nsIntRect rect = frame->GetScreenRect(); |
|
225 aPoint.x = rect.x; |
|
226 aPoint.y = rect.y; |
|
227 } |
|
228 |
|
229 return NS_OK; |
|
230 } |
|
231 |
|
232 NS_IMETHODIMP |
|
233 nsBoxObject::GetX(int32_t* aResult) |
|
234 { |
|
235 nsIntRect rect; |
|
236 GetOffsetRect(rect); |
|
237 *aResult = rect.x; |
|
238 return NS_OK; |
|
239 } |
|
240 |
|
241 NS_IMETHODIMP |
|
242 nsBoxObject::GetY(int32_t* aResult) |
|
243 { |
|
244 nsIntRect rect; |
|
245 GetOffsetRect(rect); |
|
246 *aResult = rect.y; |
|
247 return NS_OK; |
|
248 } |
|
249 |
|
250 NS_IMETHODIMP |
|
251 nsBoxObject::GetWidth(int32_t* aResult) |
|
252 { |
|
253 nsIntRect rect; |
|
254 GetOffsetRect(rect); |
|
255 *aResult = rect.width; |
|
256 return NS_OK; |
|
257 } |
|
258 |
|
259 NS_IMETHODIMP |
|
260 nsBoxObject::GetHeight(int32_t* aResult) |
|
261 { |
|
262 nsIntRect rect; |
|
263 GetOffsetRect(rect); |
|
264 *aResult = rect.height; |
|
265 return NS_OK; |
|
266 } |
|
267 |
|
268 NS_IMETHODIMP |
|
269 nsBoxObject::GetScreenX(int32_t *_retval) |
|
270 { |
|
271 nsIntPoint position; |
|
272 nsresult rv = GetScreenPosition(position); |
|
273 if (NS_FAILED(rv)) return rv; |
|
274 |
|
275 *_retval = position.x; |
|
276 |
|
277 return NS_OK; |
|
278 } |
|
279 |
|
280 NS_IMETHODIMP |
|
281 nsBoxObject::GetScreenY(int32_t *_retval) |
|
282 { |
|
283 nsIntPoint position; |
|
284 nsresult rv = GetScreenPosition(position); |
|
285 if (NS_FAILED(rv)) return rv; |
|
286 |
|
287 *_retval = position.y; |
|
288 |
|
289 return NS_OK; |
|
290 } |
|
291 |
|
292 NS_IMETHODIMP |
|
293 nsBoxObject::GetPropertyAsSupports(const char16_t* aPropertyName, nsISupports** aResult) |
|
294 { |
|
295 NS_ENSURE_ARG(aPropertyName && *aPropertyName); |
|
296 if (!mPropertyTable) { |
|
297 *aResult = nullptr; |
|
298 return NS_OK; |
|
299 } |
|
300 nsDependentString propertyName(aPropertyName); |
|
301 mPropertyTable->Get(propertyName, aResult); // Addref here. |
|
302 return NS_OK; |
|
303 } |
|
304 |
|
305 NS_IMETHODIMP |
|
306 nsBoxObject::SetPropertyAsSupports(const char16_t* aPropertyName, nsISupports* aValue) |
|
307 { |
|
308 NS_ENSURE_ARG(aPropertyName && *aPropertyName); |
|
309 |
|
310 if (!mPropertyTable) { |
|
311 mPropertyTable = new nsInterfaceHashtable<nsStringHashKey,nsISupports>(8); |
|
312 } |
|
313 |
|
314 nsDependentString propertyName(aPropertyName); |
|
315 mPropertyTable->Put(propertyName, aValue); |
|
316 return NS_OK; |
|
317 } |
|
318 |
|
319 NS_IMETHODIMP |
|
320 nsBoxObject::GetProperty(const char16_t* aPropertyName, char16_t** aResult) |
|
321 { |
|
322 nsCOMPtr<nsISupports> data; |
|
323 nsresult rv = GetPropertyAsSupports(aPropertyName,getter_AddRefs(data)); |
|
324 NS_ENSURE_SUCCESS(rv, rv); |
|
325 |
|
326 if (!data) { |
|
327 *aResult = nullptr; |
|
328 return NS_OK; |
|
329 } |
|
330 |
|
331 nsCOMPtr<nsISupportsString> supportsStr = do_QueryInterface(data); |
|
332 if (!supportsStr) |
|
333 return NS_ERROR_FAILURE; |
|
334 |
|
335 return supportsStr->ToString(aResult); |
|
336 } |
|
337 |
|
338 NS_IMETHODIMP |
|
339 nsBoxObject::SetProperty(const char16_t* aPropertyName, const char16_t* aPropertyValue) |
|
340 { |
|
341 NS_ENSURE_ARG(aPropertyName && *aPropertyName); |
|
342 |
|
343 nsDependentString propertyName(aPropertyName); |
|
344 nsDependentString propertyValue; |
|
345 if (aPropertyValue) { |
|
346 propertyValue.Rebind(aPropertyValue); |
|
347 } else { |
|
348 propertyValue.SetIsVoid(true); |
|
349 } |
|
350 |
|
351 nsCOMPtr<nsISupportsString> supportsStr(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID)); |
|
352 NS_ENSURE_TRUE(supportsStr, NS_ERROR_OUT_OF_MEMORY); |
|
353 supportsStr->SetData(propertyValue); |
|
354 |
|
355 return SetPropertyAsSupports(aPropertyName,supportsStr); |
|
356 } |
|
357 |
|
358 NS_IMETHODIMP |
|
359 nsBoxObject::RemoveProperty(const char16_t* aPropertyName) |
|
360 { |
|
361 NS_ENSURE_ARG(aPropertyName && *aPropertyName); |
|
362 |
|
363 if (!mPropertyTable) return NS_OK; |
|
364 |
|
365 nsDependentString propertyName(aPropertyName); |
|
366 mPropertyTable->Remove(propertyName); |
|
367 return NS_OK; |
|
368 } |
|
369 |
|
370 NS_IMETHODIMP |
|
371 nsBoxObject::GetParentBox(nsIDOMElement * *aParentBox) |
|
372 { |
|
373 *aParentBox = nullptr; |
|
374 nsIFrame* frame = GetFrame(false); |
|
375 if (!frame) return NS_OK; |
|
376 nsIFrame* parent = frame->GetParent(); |
|
377 if (!parent) return NS_OK; |
|
378 |
|
379 nsCOMPtr<nsIDOMElement> el = do_QueryInterface(parent->GetContent()); |
|
380 *aParentBox = el; |
|
381 NS_IF_ADDREF(*aParentBox); |
|
382 return NS_OK; |
|
383 } |
|
384 |
|
385 NS_IMETHODIMP |
|
386 nsBoxObject::GetFirstChild(nsIDOMElement * *aFirstVisibleChild) |
|
387 { |
|
388 *aFirstVisibleChild = nullptr; |
|
389 nsIFrame* frame = GetFrame(false); |
|
390 if (!frame) return NS_OK; |
|
391 nsIFrame* firstFrame = frame->GetFirstPrincipalChild(); |
|
392 if (!firstFrame) return NS_OK; |
|
393 // get the content for the box and query to a dom element |
|
394 nsCOMPtr<nsIDOMElement> el = do_QueryInterface(firstFrame->GetContent()); |
|
395 el.swap(*aFirstVisibleChild); |
|
396 return NS_OK; |
|
397 } |
|
398 |
|
399 NS_IMETHODIMP |
|
400 nsBoxObject::GetLastChild(nsIDOMElement * *aLastVisibleChild) |
|
401 { |
|
402 *aLastVisibleChild = nullptr; |
|
403 nsIFrame* frame = GetFrame(false); |
|
404 if (!frame) return NS_OK; |
|
405 return GetPreviousSibling(frame, nullptr, aLastVisibleChild); |
|
406 } |
|
407 |
|
408 NS_IMETHODIMP |
|
409 nsBoxObject::GetNextSibling(nsIDOMElement **aNextOrdinalSibling) |
|
410 { |
|
411 *aNextOrdinalSibling = nullptr; |
|
412 nsIFrame* frame = GetFrame(false); |
|
413 if (!frame) return NS_OK; |
|
414 nsIFrame* nextFrame = frame->GetNextSibling(); |
|
415 if (!nextFrame) return NS_OK; |
|
416 // get the content for the box and query to a dom element |
|
417 nsCOMPtr<nsIDOMElement> el = do_QueryInterface(nextFrame->GetContent()); |
|
418 el.swap(*aNextOrdinalSibling); |
|
419 return NS_OK; |
|
420 } |
|
421 |
|
422 NS_IMETHODIMP |
|
423 nsBoxObject::GetPreviousSibling(nsIDOMElement **aPreviousOrdinalSibling) |
|
424 { |
|
425 *aPreviousOrdinalSibling = nullptr; |
|
426 nsIFrame* frame = GetFrame(false); |
|
427 if (!frame) return NS_OK; |
|
428 nsIFrame* parentFrame = frame->GetParent(); |
|
429 if (!parentFrame) return NS_OK; |
|
430 return GetPreviousSibling(parentFrame, frame, aPreviousOrdinalSibling); |
|
431 } |
|
432 |
|
433 nsresult |
|
434 nsBoxObject::GetPreviousSibling(nsIFrame* aParentFrame, nsIFrame* aFrame, |
|
435 nsIDOMElement** aResult) |
|
436 { |
|
437 *aResult = nullptr; |
|
438 nsIFrame* nextFrame = aParentFrame->GetFirstPrincipalChild(); |
|
439 nsIFrame* prevFrame = nullptr; |
|
440 while (nextFrame) { |
|
441 if (nextFrame == aFrame) |
|
442 break; |
|
443 prevFrame = nextFrame; |
|
444 nextFrame = nextFrame->GetNextSibling(); |
|
445 } |
|
446 |
|
447 if (!prevFrame) return NS_OK; |
|
448 // get the content for the box and query to a dom element |
|
449 nsCOMPtr<nsIDOMElement> el = do_QueryInterface(prevFrame->GetContent()); |
|
450 el.swap(*aResult); |
|
451 return NS_OK; |
|
452 } |
|
453 |
|
454 // Creation Routine /////////////////////////////////////////////////////////////////////// |
|
455 |
|
456 nsresult |
|
457 NS_NewBoxObject(nsIBoxObject** aResult) |
|
458 { |
|
459 *aResult = new nsBoxObject; |
|
460 if (!*aResult) |
|
461 return NS_ERROR_OUT_OF_MEMORY; |
|
462 NS_ADDREF(*aResult); |
|
463 return NS_OK; |
|
464 } |
|
465 |