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