Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "mozilla/dom/BoxObject.h"
8 : #include "nsCOMPtr.h"
9 : #include "nsIDocument.h"
10 : #include "nsIPresShell.h"
11 : #include "nsPresContext.h"
12 : #include "nsIContent.h"
13 : #include "nsContainerFrame.h"
14 : #include "nsIDocShell.h"
15 : #include "nsReadableUtils.h"
16 : #include "nsDOMClassInfoID.h"
17 : #include "nsView.h"
18 : #ifdef MOZ_XUL
19 : #include "nsIDOMXULElement.h"
20 : #else
21 : #include "nsIDOMElement.h"
22 : #endif
23 : #include "nsLayoutUtils.h"
24 : #include "nsISupportsPrimitives.h"
25 : #include "nsSupportsPrimitives.h"
26 : #include "mozilla/dom/Element.h"
27 : #include "nsComponentManagerUtils.h"
28 : #include "mozilla/dom/BoxObjectBinding.h"
29 :
30 : // Implementation /////////////////////////////////////////////////////////////////
31 :
32 : namespace mozilla {
33 : namespace dom {
34 :
35 : // Static member variable initialization
36 :
37 : // Implement our nsISupports methods
38 : NS_IMPL_CYCLE_COLLECTION_CLASS(BoxObject)
39 34 : NS_IMPL_CYCLE_COLLECTING_ADDREF(BoxObject)
40 12 : NS_IMPL_CYCLE_COLLECTING_RELEASE(BoxObject)
41 :
42 : // QueryInterface implementation for BoxObject
43 110 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BoxObject)
44 11 : NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
45 0 : NS_INTERFACE_MAP_ENTRY(nsIBoxObject)
46 0 : NS_INTERFACE_MAP_ENTRY(nsPIBoxObject)
47 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
48 0 : NS_INTERFACE_MAP_END
49 :
50 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BoxObject)
51 : // XXX jmorton: why aren't we unlinking mPropertyTable?
52 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
53 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
54 :
55 11 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BoxObject)
56 11 : if (tmp->mPropertyTable) {
57 0 : for (auto iter = tmp->mPropertyTable->Iter(); !iter.Done(); iter.Next()) {
58 0 : cb.NoteXPCOMChild(iter.UserData());
59 : }
60 : }
61 11 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
62 :
63 22 : NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(BoxObject)
64 :
65 : // Constructors/Destructors
66 11 : BoxObject::BoxObject()
67 11 : : mContent(nullptr)
68 : {
69 11 : }
70 :
71 0 : BoxObject::~BoxObject()
72 : {
73 0 : }
74 :
75 : NS_IMETHODIMP
76 0 : BoxObject::GetElement(nsIDOMElement** aResult)
77 : {
78 0 : if (mContent) {
79 0 : return CallQueryInterface(mContent, aResult);
80 : }
81 :
82 0 : *aResult = nullptr;
83 0 : return NS_OK;
84 : }
85 :
86 : // nsPIBoxObject //////////////////////////////////////////////////////////////////////////
87 :
88 : nsresult
89 11 : BoxObject::Init(nsIContent* aContent)
90 : {
91 11 : mContent = aContent;
92 11 : return NS_OK;
93 : }
94 :
95 : void
96 0 : BoxObject::Clear()
97 : {
98 0 : mPropertyTable = nullptr;
99 0 : mContent = nullptr;
100 0 : }
101 :
102 : void
103 0 : BoxObject::ClearCachedValues()
104 : {
105 0 : }
106 :
107 : nsIFrame*
108 2 : BoxObject::GetFrame(bool aFlushLayout)
109 : {
110 2 : nsIPresShell* shell = GetPresShell(aFlushLayout);
111 2 : if (!shell)
112 0 : return nullptr;
113 :
114 2 : if (!aFlushLayout) {
115 : // If we didn't flush layout when getting the presshell, we should at least
116 : // flush to make sure our frame model is up to date.
117 : // XXXbz should flush on document, no? Except people call this from
118 : // frame code, maybe?
119 0 : shell->FlushPendingNotifications(FlushType::Frames);
120 : }
121 :
122 : // The flush might have killed mContent.
123 2 : if (!mContent) {
124 0 : return nullptr;
125 : }
126 :
127 2 : return mContent->GetPrimaryFrame();
128 : }
129 :
130 : nsIPresShell*
131 2 : BoxObject::GetPresShell(bool aFlushLayout)
132 : {
133 2 : if (!mContent) {
134 0 : return nullptr;
135 : }
136 :
137 4 : nsCOMPtr<nsIDocument> doc = mContent->GetUncomposedDoc();
138 2 : if (!doc) {
139 0 : return nullptr;
140 : }
141 :
142 2 : if (aFlushLayout) {
143 2 : doc->FlushPendingNotifications(FlushType::Layout);
144 : }
145 :
146 2 : return doc->GetShell();
147 : }
148 :
149 : nsresult
150 2 : BoxObject::GetOffsetRect(nsIntRect& aRect)
151 : {
152 2 : aRect.SetRect(0, 0, 0, 0);
153 :
154 2 : if (!mContent)
155 0 : return NS_ERROR_NOT_INITIALIZED;
156 :
157 : // Get the Frame for our content
158 2 : nsIFrame* frame = GetFrame(true);
159 2 : if (frame) {
160 : // Get its origin
161 2 : nsPoint origin = frame->GetPositionIgnoringScrolling();
162 :
163 : // Find the frame parent whose content is the document element.
164 2 : Element* docElement = mContent->GetComposedDoc()->GetRootElement();
165 2 : nsIFrame* parent = frame->GetParent();
166 : for (;;) {
167 : // If we've hit the document element, break here
168 12 : if (parent->GetContent() == docElement) {
169 2 : break;
170 : }
171 :
172 10 : nsIFrame* next = parent->GetParent();
173 10 : if (!next) {
174 0 : NS_WARNING("We should have hit the document element...");
175 0 : origin += parent->GetPosition();
176 0 : break;
177 : }
178 :
179 : // Add the parent's origin to our own to get to the
180 : // right coordinate system
181 10 : origin += next->GetPositionOfChildIgnoringScrolling(parent);
182 10 : parent = next;
183 10 : }
184 :
185 : // For the origin, add in the border for the frame
186 2 : const nsStyleBorder* border = frame->StyleBorder();
187 2 : origin.x += border->GetComputedBorderWidth(eSideLeft);
188 2 : origin.y += border->GetComputedBorderWidth(eSideTop);
189 :
190 : // And subtract out the border for the parent
191 2 : const nsStyleBorder* parentBorder = parent->StyleBorder();
192 2 : origin.x -= parentBorder->GetComputedBorderWidth(eSideLeft);
193 2 : origin.y -= parentBorder->GetComputedBorderWidth(eSideTop);
194 :
195 2 : aRect.x = nsPresContext::AppUnitsToIntCSSPixels(origin.x);
196 2 : aRect.y = nsPresContext::AppUnitsToIntCSSPixels(origin.y);
197 :
198 : // Get the union of all rectangles in this and continuation frames.
199 : // It doesn't really matter what we use as aRelativeTo here, since
200 : // we only care about the size. Using 'parent' might make things
201 : // a bit faster by speeding up the internal GetOffsetTo operations.
202 4 : nsRect rcFrame = nsLayoutUtils::GetAllInFlowRectsUnion(frame, parent);
203 2 : aRect.width = nsPresContext::AppUnitsToIntCSSPixels(rcFrame.width);
204 2 : aRect.height = nsPresContext::AppUnitsToIntCSSPixels(rcFrame.height);
205 : }
206 :
207 2 : return NS_OK;
208 : }
209 :
210 : nsresult
211 0 : BoxObject::GetScreenPosition(nsIntPoint& aPoint)
212 : {
213 0 : aPoint.x = aPoint.y = 0;
214 :
215 0 : if (!mContent)
216 0 : return NS_ERROR_NOT_INITIALIZED;
217 :
218 0 : nsIFrame* frame = GetFrame(true);
219 0 : if (frame) {
220 0 : CSSIntRect rect = frame->GetScreenRect();
221 0 : aPoint.x = rect.x;
222 0 : aPoint.y = rect.y;
223 : }
224 :
225 0 : return NS_OK;
226 : }
227 :
228 : NS_IMETHODIMP
229 0 : BoxObject::GetX(int32_t* aResult)
230 : {
231 0 : nsIntRect rect;
232 0 : GetOffsetRect(rect);
233 0 : *aResult = rect.x;
234 0 : return NS_OK;
235 : }
236 :
237 : NS_IMETHODIMP
238 0 : BoxObject::GetY(int32_t* aResult)
239 : {
240 0 : nsIntRect rect;
241 0 : GetOffsetRect(rect);
242 0 : *aResult = rect.y;
243 0 : return NS_OK;
244 : }
245 :
246 : NS_IMETHODIMP
247 2 : BoxObject::GetWidth(int32_t* aResult)
248 : {
249 2 : nsIntRect rect;
250 2 : GetOffsetRect(rect);
251 2 : *aResult = rect.width;
252 2 : return NS_OK;
253 : }
254 :
255 : NS_IMETHODIMP
256 0 : BoxObject::GetHeight(int32_t* aResult)
257 : {
258 0 : nsIntRect rect;
259 0 : GetOffsetRect(rect);
260 0 : *aResult = rect.height;
261 0 : return NS_OK;
262 : }
263 :
264 : NS_IMETHODIMP
265 0 : BoxObject::GetScreenX(int32_t *_retval)
266 : {
267 0 : nsIntPoint position;
268 0 : nsresult rv = GetScreenPosition(position);
269 0 : if (NS_FAILED(rv)) return rv;
270 0 : *_retval = position.x;
271 0 : return NS_OK;
272 : }
273 :
274 : NS_IMETHODIMP
275 0 : BoxObject::GetScreenY(int32_t *_retval)
276 : {
277 0 : nsIntPoint position;
278 0 : nsresult rv = GetScreenPosition(position);
279 0 : if (NS_FAILED(rv)) return rv;
280 0 : *_retval = position.y;
281 0 : return NS_OK;
282 : }
283 :
284 : NS_IMETHODIMP
285 2 : BoxObject::GetPropertyAsSupports(const char16_t* aPropertyName, nsISupports** aResult)
286 : {
287 2 : NS_ENSURE_ARG(aPropertyName && *aPropertyName);
288 2 : if (!mPropertyTable) {
289 2 : *aResult = nullptr;
290 2 : return NS_OK;
291 : }
292 0 : nsDependentString propertyName(aPropertyName);
293 0 : mPropertyTable->Get(propertyName, aResult); // Addref here.
294 0 : return NS_OK;
295 : }
296 :
297 : NS_IMETHODIMP
298 0 : BoxObject::SetPropertyAsSupports(const char16_t* aPropertyName, nsISupports* aValue)
299 : {
300 0 : NS_ENSURE_ARG(aPropertyName && *aPropertyName);
301 :
302 0 : if (!mPropertyTable) {
303 0 : mPropertyTable = new nsInterfaceHashtable<nsStringHashKey,nsISupports>(4);
304 : }
305 :
306 0 : nsDependentString propertyName(aPropertyName);
307 0 : mPropertyTable->Put(propertyName, aValue);
308 0 : return NS_OK;
309 : }
310 :
311 : NS_IMETHODIMP
312 0 : BoxObject::GetProperty(const char16_t* aPropertyName, char16_t** aResult)
313 : {
314 0 : nsCOMPtr<nsISupports> data;
315 0 : nsresult rv = GetPropertyAsSupports(aPropertyName,getter_AddRefs(data));
316 0 : NS_ENSURE_SUCCESS(rv, rv);
317 :
318 0 : if (!data) {
319 0 : *aResult = nullptr;
320 0 : return NS_OK;
321 : }
322 :
323 0 : nsCOMPtr<nsISupportsString> supportsStr = do_QueryInterface(data);
324 0 : if (!supportsStr) {
325 0 : return NS_ERROR_FAILURE;
326 : }
327 :
328 0 : return supportsStr->ToString(aResult);
329 : }
330 :
331 : NS_IMETHODIMP
332 0 : BoxObject::SetProperty(const char16_t* aPropertyName, const char16_t* aPropertyValue)
333 : {
334 0 : NS_ENSURE_ARG(aPropertyName && *aPropertyName);
335 :
336 0 : nsDependentString propertyName(aPropertyName);
337 0 : nsDependentString propertyValue;
338 0 : if (aPropertyValue) {
339 0 : propertyValue.Rebind(aPropertyValue);
340 : } else {
341 0 : propertyValue.SetIsVoid(true);
342 : }
343 :
344 0 : nsCOMPtr<nsISupportsString> supportsStr(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
345 0 : NS_ENSURE_TRUE(supportsStr, NS_ERROR_OUT_OF_MEMORY);
346 0 : supportsStr->SetData(propertyValue);
347 :
348 0 : return SetPropertyAsSupports(aPropertyName,supportsStr);
349 : }
350 :
351 : NS_IMETHODIMP
352 0 : BoxObject::RemoveProperty(const char16_t* aPropertyName)
353 : {
354 0 : NS_ENSURE_ARG(aPropertyName && *aPropertyName);
355 :
356 0 : if (!mPropertyTable) return NS_OK;
357 :
358 0 : nsDependentString propertyName(aPropertyName);
359 0 : mPropertyTable->Remove(propertyName);
360 0 : return NS_OK;
361 : }
362 :
363 : NS_IMETHODIMP
364 0 : BoxObject::GetParentBox(nsIDOMElement * *aParentBox)
365 : {
366 0 : *aParentBox = nullptr;
367 0 : nsIFrame* frame = GetFrame(false);
368 0 : if (!frame) return NS_OK;
369 0 : nsIFrame* parent = frame->GetParent();
370 0 : if (!parent) return NS_OK;
371 :
372 0 : nsCOMPtr<nsIDOMElement> el = do_QueryInterface(parent->GetContent());
373 0 : *aParentBox = el;
374 0 : NS_IF_ADDREF(*aParentBox);
375 0 : return NS_OK;
376 : }
377 :
378 : NS_IMETHODIMP
379 0 : BoxObject::GetFirstChild(nsIDOMElement * *aFirstVisibleChild)
380 : {
381 0 : *aFirstVisibleChild = nullptr;
382 0 : nsIFrame* frame = GetFrame(false);
383 0 : if (!frame) return NS_OK;
384 0 : nsIFrame* firstFrame = frame->PrincipalChildList().FirstChild();
385 0 : if (!firstFrame) return NS_OK;
386 : // get the content for the box and query to a dom element
387 0 : nsCOMPtr<nsIDOMElement> el = do_QueryInterface(firstFrame->GetContent());
388 0 : el.swap(*aFirstVisibleChild);
389 0 : return NS_OK;
390 : }
391 :
392 : NS_IMETHODIMP
393 0 : BoxObject::GetLastChild(nsIDOMElement * *aLastVisibleChild)
394 : {
395 0 : *aLastVisibleChild = nullptr;
396 0 : nsIFrame* frame = GetFrame(false);
397 0 : if (!frame) return NS_OK;
398 0 : return GetPreviousSibling(frame, nullptr, aLastVisibleChild);
399 : }
400 :
401 : NS_IMETHODIMP
402 0 : BoxObject::GetNextSibling(nsIDOMElement **aNextOrdinalSibling)
403 : {
404 0 : *aNextOrdinalSibling = nullptr;
405 0 : nsIFrame* frame = GetFrame(false);
406 0 : if (!frame) return NS_OK;
407 0 : nsIFrame* nextFrame = frame->GetNextSibling();
408 0 : if (!nextFrame) return NS_OK;
409 : // get the content for the box and query to a dom element
410 0 : nsCOMPtr<nsIDOMElement> el = do_QueryInterface(nextFrame->GetContent());
411 0 : el.swap(*aNextOrdinalSibling);
412 0 : return NS_OK;
413 : }
414 :
415 : NS_IMETHODIMP
416 0 : BoxObject::GetPreviousSibling(nsIDOMElement **aPreviousOrdinalSibling)
417 : {
418 0 : *aPreviousOrdinalSibling = nullptr;
419 0 : nsIFrame* frame = GetFrame(false);
420 0 : if (!frame) return NS_OK;
421 0 : nsIFrame* parentFrame = frame->GetParent();
422 0 : if (!parentFrame) return NS_OK;
423 0 : return GetPreviousSibling(parentFrame, frame, aPreviousOrdinalSibling);
424 : }
425 :
426 : nsresult
427 0 : BoxObject::GetPreviousSibling(nsIFrame* aParentFrame, nsIFrame* aFrame,
428 : nsIDOMElement** aResult)
429 : {
430 0 : *aResult = nullptr;
431 0 : nsIFrame* nextFrame = aParentFrame->PrincipalChildList().FirstChild();
432 0 : nsIFrame* prevFrame = nullptr;
433 0 : while (nextFrame) {
434 0 : if (nextFrame == aFrame)
435 0 : break;
436 0 : prevFrame = nextFrame;
437 0 : nextFrame = nextFrame->GetNextSibling();
438 : }
439 :
440 0 : if (!prevFrame) return NS_OK;
441 : // get the content for the box and query to a dom element
442 0 : nsCOMPtr<nsIDOMElement> el = do_QueryInterface(prevFrame->GetContent());
443 0 : el.swap(*aResult);
444 0 : return NS_OK;
445 : }
446 :
447 : nsIContent*
448 11 : BoxObject::GetParentObject() const
449 : {
450 11 : return mContent;
451 : }
452 :
453 : JSObject*
454 4 : BoxObject::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
455 : {
456 4 : return BoxObjectBinding::Wrap(aCx, this, aGivenProto);
457 : }
458 :
459 : Element*
460 0 : BoxObject::GetElement() const
461 : {
462 0 : return mContent && mContent->IsElement() ? mContent->AsElement() : nullptr;
463 : }
464 :
465 : int32_t
466 0 : BoxObject::X()
467 : {
468 0 : int32_t ret = 0;
469 0 : GetX(&ret);
470 0 : return ret;
471 : }
472 :
473 : int32_t
474 0 : BoxObject::Y()
475 : {
476 0 : int32_t ret = 0;
477 0 : GetY(&ret);
478 0 : return ret;
479 : }
480 :
481 : int32_t
482 0 : BoxObject::GetScreenX(ErrorResult& aRv)
483 : {
484 0 : int32_t ret = 0;
485 0 : aRv = GetScreenX(&ret);
486 0 : return ret;
487 : }
488 :
489 : int32_t
490 0 : BoxObject::GetScreenY(ErrorResult& aRv)
491 : {
492 0 : int32_t ret = 0;
493 0 : aRv = GetScreenY(&ret);
494 0 : return ret;
495 : }
496 :
497 : int32_t
498 2 : BoxObject::Width()
499 : {
500 2 : int32_t ret = 0;
501 2 : GetWidth(&ret);
502 2 : return ret;
503 : }
504 :
505 : int32_t
506 0 : BoxObject::Height()
507 : {
508 0 : int32_t ret = 0;
509 0 : GetHeight(&ret);
510 0 : return ret;
511 : }
512 :
513 : already_AddRefed<nsISupports>
514 2 : BoxObject::GetPropertyAsSupports(const nsAString& propertyName)
515 : {
516 4 : nsCOMPtr<nsISupports> ret;
517 2 : GetPropertyAsSupports(PromiseFlatString(propertyName).get(), getter_AddRefs(ret));
518 4 : return ret.forget();
519 : }
520 :
521 : void
522 0 : BoxObject::SetPropertyAsSupports(const nsAString& propertyName, nsISupports* value)
523 : {
524 0 : SetPropertyAsSupports(PromiseFlatString(propertyName).get(), value);
525 0 : }
526 :
527 : void
528 2 : BoxObject::GetProperty(const nsAString& propertyName, nsString& aRetVal, ErrorResult& aRv)
529 : {
530 2 : nsCOMPtr<nsISupports> data(GetPropertyAsSupports(propertyName));
531 2 : if (!data) {
532 2 : return;
533 : }
534 :
535 0 : nsCOMPtr<nsISupportsString> supportsStr(do_QueryInterface(data));
536 0 : if (!supportsStr) {
537 0 : aRv.Throw(NS_ERROR_FAILURE);
538 0 : return;
539 : }
540 :
541 0 : supportsStr->GetData(aRetVal);
542 : }
543 :
544 : void
545 0 : BoxObject::SetProperty(const nsAString& propertyName, const nsAString& propertyValue)
546 : {
547 0 : SetProperty(PromiseFlatString(propertyName).get(), PromiseFlatString(propertyValue).get());
548 0 : }
549 :
550 : void
551 0 : BoxObject::RemoveProperty(const nsAString& propertyName)
552 : {
553 0 : RemoveProperty(PromiseFlatString(propertyName).get());
554 0 : }
555 :
556 : already_AddRefed<Element>
557 0 : BoxObject::GetParentBox()
558 : {
559 0 : nsCOMPtr<nsIDOMElement> el;
560 0 : GetParentBox(getter_AddRefs(el));
561 0 : nsCOMPtr<Element> ret(do_QueryInterface(el));
562 0 : return ret.forget();
563 : }
564 :
565 : already_AddRefed<Element>
566 0 : BoxObject::GetFirstChild()
567 : {
568 0 : nsCOMPtr<nsIDOMElement> el;
569 0 : GetFirstChild(getter_AddRefs(el));
570 0 : nsCOMPtr<Element> ret(do_QueryInterface(el));
571 0 : return ret.forget();
572 : }
573 :
574 : already_AddRefed<Element>
575 0 : BoxObject::GetLastChild()
576 : {
577 0 : nsCOMPtr<nsIDOMElement> el;
578 0 : GetLastChild(getter_AddRefs(el));
579 0 : nsCOMPtr<Element> ret(do_QueryInterface(el));
580 0 : return ret.forget();
581 : }
582 :
583 : already_AddRefed<Element>
584 0 : BoxObject::GetNextSibling()
585 : {
586 0 : nsCOMPtr<nsIDOMElement> el;
587 0 : GetNextSibling(getter_AddRefs(el));
588 0 : nsCOMPtr<Element> ret(do_QueryInterface(el));
589 0 : return ret.forget();
590 : }
591 :
592 : already_AddRefed<Element>
593 0 : BoxObject::GetPreviousSibling()
594 : {
595 0 : nsCOMPtr<nsIDOMElement> el;
596 0 : GetPreviousSibling(getter_AddRefs(el));
597 0 : nsCOMPtr<Element> ret(do_QueryInterface(el));
598 0 : return ret.forget();
599 : }
600 :
601 : } // namespace dom
602 : } // namespace mozilla
603 :
604 : // Creation Routine ///////////////////////////////////////////////////////////////////////
605 :
606 : using namespace mozilla::dom;
607 :
608 : nsresult
609 0 : NS_NewBoxObject(nsIBoxObject** aResult)
610 : {
611 0 : NS_ADDREF(*aResult = new BoxObject());
612 0 : return NS_OK;
613 : }
|