1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 sw=2 et tw=78: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * L. David Baron <dbaron@dbaron.org>, Mozilla Corporation
25 : * Mats Palmgren <matspal@gmail.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "mozilla/Util.h"
42 :
43 : #include "nsLayoutUtils.h"
44 : #include "nsIFormControlFrame.h"
45 : #include "nsPresContext.h"
46 : #include "nsIContent.h"
47 : #include "nsIDOMDocument.h"
48 : #include "nsIDOMHTMLDocument.h"
49 : #include "nsIDOMHTMLElement.h"
50 : #include "nsFrameList.h"
51 : #include "nsGkAtoms.h"
52 : #include "nsIAtom.h"
53 : #include "nsCSSPseudoElements.h"
54 : #include "nsCSSAnonBoxes.h"
55 : #include "nsCSSColorUtils.h"
56 : #include "nsIView.h"
57 : #include "nsPlaceholderFrame.h"
58 : #include "nsIScrollableFrame.h"
59 : #include "nsCSSFrameConstructor.h"
60 : #include "nsIPrivateDOMEvent.h"
61 : #include "nsIDOMEvent.h"
62 : #include "nsGUIEvent.h"
63 : #include "nsDisplayList.h"
64 : #include "nsRegion.h"
65 : #include "nsFrameManager.h"
66 : #include "nsBlockFrame.h"
67 : #include "nsBidiPresUtils.h"
68 : #include "imgIContainer.h"
69 : #include "gfxRect.h"
70 : #include "gfxContext.h"
71 : #include "gfxFont.h"
72 : #include "nsIInterfaceRequestorUtils.h"
73 : #include "nsCSSRendering.h"
74 : #include "nsContentUtils.h"
75 : #include "nsThemeConstants.h"
76 : #include "nsPIDOMWindow.h"
77 : #include "nsIBaseWindow.h"
78 : #include "nsIDocShell.h"
79 : #include "nsIDocShellTreeItem.h"
80 : #include "nsIWidget.h"
81 : #include "gfxMatrix.h"
82 : #include "gfxPoint3D.h"
83 : #include "gfxTypes.h"
84 : #include "gfxUserFontSet.h"
85 : #include "nsTArray.h"
86 : #include "nsHTMLCanvasElement.h"
87 : #include "nsICanvasRenderingContextInternal.h"
88 : #include "gfxPlatform.h"
89 : #include "nsClientRect.h"
90 : #ifdef MOZ_MEDIA
91 : #include "nsHTMLVideoElement.h"
92 : #endif
93 : #include "imgIRequest.h"
94 : #include "nsIImageLoadingContent.h"
95 : #include "nsCOMPtr.h"
96 : #include "nsListControlFrame.h"
97 : #include "ImageLayers.h"
98 : #include "mozilla/arm.h"
99 : #include "mozilla/dom/Element.h"
100 : #include "nsCanvasFrame.h"
101 : #include "gfxDrawable.h"
102 : #include "gfxUtils.h"
103 : #include "nsDataHashtable.h"
104 : #include "nsTextFrame.h"
105 : #include "nsFontFaceList.h"
106 :
107 : #include "nsSVGUtils.h"
108 : #include "nsSVGIntegrationUtils.h"
109 : #include "nsSVGForeignObjectFrame.h"
110 : #include "nsSVGOuterSVGFrame.h"
111 :
112 : #include "mozilla/Preferences.h"
113 :
114 : #ifdef MOZ_XUL
115 : #include "nsXULPopupManager.h"
116 : #endif
117 :
118 : #include "sampler.h"
119 :
120 : using namespace mozilla;
121 : using namespace mozilla::layers;
122 : using namespace mozilla::dom;
123 : using namespace mozilla::layout;
124 :
125 : #ifdef DEBUG
126 : // TODO: remove, see bug 598468.
127 : bool nsLayoutUtils::gPreventAssertInCompareTreePosition = false;
128 : #endif // DEBUG
129 :
130 : typedef gfxPattern::GraphicsFilter GraphicsFilter;
131 : typedef FrameMetrics::ViewID ViewID;
132 :
133 : static PRUint32 sFontSizeInflationEmPerLine;
134 : static PRUint32 sFontSizeInflationMinTwips;
135 :
136 1464 : static ViewID sScrollIdCounter = FrameMetrics::START_SCROLL_ID;
137 :
138 : typedef nsDataHashtable<nsUint64HashKey, nsIContent*> ContentMap;
139 : static ContentMap* sContentMap = NULL;
140 0 : static ContentMap& GetContentMap() {
141 0 : if (!sContentMap) {
142 0 : sContentMap = new ContentMap();
143 : #ifdef DEBUG
144 : nsresult rv =
145 : #endif
146 0 : sContentMap->Init();
147 0 : NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "Could not initialize map.");
148 : }
149 0 : return *sContentMap;
150 : }
151 :
152 :
153 : bool
154 0 : nsLayoutUtils::Are3DTransformsEnabled()
155 : {
156 : static bool s3DTransformsEnabled;
157 : static bool s3DTransformPrefCached = false;
158 :
159 0 : if (!s3DTransformPrefCached) {
160 0 : s3DTransformPrefCached = true;
161 : mozilla::Preferences::AddBoolVarCache(&s3DTransformsEnabled,
162 0 : "layout.3d-transforms.enabled");
163 : }
164 :
165 0 : return s3DTransformsEnabled;
166 : }
167 :
168 : void
169 0 : nsLayoutUtils::UnionChildOverflow(nsIFrame* aFrame,
170 : nsOverflowAreas& aOverflowAreas)
171 : {
172 : // Iterate over all children except pop-ups.
173 : const nsIFrame::ChildListIDs skip(nsIFrame::kPopupList |
174 0 : nsIFrame::kSelectPopupList);
175 0 : for (nsIFrame::ChildListIterator childLists(aFrame);
176 0 : !childLists.IsDone(); childLists.Next()) {
177 0 : if (skip.Contains(childLists.CurrentID())) {
178 0 : continue;
179 : }
180 :
181 0 : nsFrameList children = childLists.CurrentList();
182 0 : for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
183 0 : nsIFrame* child = e.get();
184 : nsOverflowAreas childOverflow =
185 0 : child->GetOverflowAreas() + child->GetPosition();
186 0 : aOverflowAreas.UnionWith(childOverflow);
187 : }
188 : }
189 0 : }
190 :
191 0 : static void DestroyViewID(void* aObject, nsIAtom* aPropertyName,
192 : void* aPropertyValue, void* aData)
193 : {
194 0 : ViewID* id = static_cast<ViewID*>(aPropertyValue);
195 0 : GetContentMap().Remove(*id);
196 : delete id;
197 0 : }
198 :
199 : /**
200 : * A namespace class for static layout utilities.
201 : */
202 :
203 : ViewID
204 0 : nsLayoutUtils::FindIDFor(nsIContent* aContent)
205 : {
206 : ViewID scrollId;
207 :
208 0 : void* scrollIdProperty = aContent->GetProperty(nsGkAtoms::RemoteId);
209 0 : if (scrollIdProperty) {
210 0 : scrollId = *static_cast<ViewID*>(scrollIdProperty);
211 : } else {
212 0 : scrollId = sScrollIdCounter++;
213 : aContent->SetProperty(nsGkAtoms::RemoteId, new ViewID(scrollId),
214 0 : DestroyViewID);
215 0 : GetContentMap().Put(scrollId, aContent);
216 : }
217 :
218 0 : return scrollId;
219 : }
220 :
221 : nsIContent*
222 0 : nsLayoutUtils::FindContentFor(ViewID aId)
223 : {
224 0 : NS_ABORT_IF_FALSE(aId != FrameMetrics::NULL_SCROLL_ID &&
225 : aId != FrameMetrics::ROOT_SCROLL_ID,
226 : "Cannot find a content element in map for null or root IDs.");
227 : nsIContent* content;
228 0 : bool exists = GetContentMap().Get(aId, &content);
229 :
230 0 : if (exists) {
231 0 : return content;
232 : } else {
233 0 : return nsnull;
234 : }
235 : }
236 :
237 : bool
238 0 : nsLayoutUtils::GetDisplayPort(nsIContent* aContent, nsRect *aResult)
239 : {
240 0 : void* property = aContent->GetProperty(nsGkAtoms::DisplayPort);
241 0 : if (!property) {
242 0 : return false;
243 : }
244 :
245 0 : if (aResult) {
246 0 : *aResult = *static_cast<nsRect*>(property);
247 : }
248 0 : return true;
249 : }
250 :
251 : nsIFrame*
252 0 : nsLayoutUtils::GetLastContinuationWithChild(nsIFrame* aFrame)
253 : {
254 0 : NS_PRECONDITION(aFrame, "NULL frame pointer");
255 0 : aFrame = aFrame->GetLastContinuation();
256 0 : while (!aFrame->GetFirstPrincipalChild() &&
257 0 : aFrame->GetPrevContinuation()) {
258 0 : aFrame = aFrame->GetPrevContinuation();
259 : }
260 0 : return aFrame;
261 : }
262 :
263 : /**
264 : * GetFirstChildFrame returns the first "real" child frame of a
265 : * given frame. It will descend down into pseudo-frames (unless the
266 : * pseudo-frame is the :before generated frame).
267 : * @param aFrame the frame
268 : * @param aFrame the frame's content node
269 : */
270 : static nsIFrame*
271 0 : GetFirstChildFrame(nsIFrame* aFrame,
272 : nsIContent* aContent)
273 : {
274 0 : NS_PRECONDITION(aFrame, "NULL frame pointer");
275 :
276 : // Get the first child frame
277 0 : nsIFrame* childFrame = aFrame->GetFirstPrincipalChild();
278 :
279 : // If the child frame is a pseudo-frame, then return its first child.
280 : // Note that the frame we create for the generated content is also a
281 : // pseudo-frame and so don't drill down in that case
282 0 : if (childFrame &&
283 0 : childFrame->IsPseudoFrame(aContent) &&
284 0 : !childFrame->IsGeneratedContentFrame()) {
285 0 : return GetFirstChildFrame(childFrame, aContent);
286 : }
287 :
288 0 : return childFrame;
289 : }
290 :
291 : /**
292 : * GetLastChildFrame returns the last "real" child frame of a
293 : * given frame. It will descend down into pseudo-frames (unless the
294 : * pseudo-frame is the :after generated frame).
295 : * @param aFrame the frame
296 : * @param aFrame the frame's content node
297 : */
298 : static nsIFrame*
299 0 : GetLastChildFrame(nsIFrame* aFrame,
300 : nsIContent* aContent)
301 : {
302 0 : NS_PRECONDITION(aFrame, "NULL frame pointer");
303 :
304 : // Get the last continuation frame that's a parent
305 : nsIFrame* lastParentContinuation =
306 0 : nsLayoutUtils::GetLastContinuationWithChild(aFrame);
307 : nsIFrame* lastChildFrame =
308 0 : lastParentContinuation->GetLastChild(nsIFrame::kPrincipalList);
309 0 : if (lastChildFrame) {
310 : // Get the frame's first continuation. This matters in case the frame has
311 : // been continued across multiple lines or split by BiDi resolution.
312 0 : lastChildFrame = lastChildFrame->GetFirstContinuation();
313 :
314 : // If the last child frame is a pseudo-frame, then return its last child.
315 : // Note that the frame we create for the generated content is also a
316 : // pseudo-frame and so don't drill down in that case
317 0 : if (lastChildFrame &&
318 0 : lastChildFrame->IsPseudoFrame(aContent) &&
319 0 : !lastChildFrame->IsGeneratedContentFrame()) {
320 0 : return GetLastChildFrame(lastChildFrame, aContent);
321 : }
322 :
323 0 : return lastChildFrame;
324 : }
325 :
326 0 : return nsnull;
327 : }
328 :
329 : //static
330 : nsIFrame::ChildListID
331 0 : nsLayoutUtils::GetChildListNameFor(nsIFrame* aChildFrame)
332 : {
333 0 : nsIFrame::ChildListID id = nsIFrame::kPrincipalList;
334 :
335 0 : if (aChildFrame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
336 0 : nsIFrame* pif = aChildFrame->GetPrevInFlow();
337 0 : if (pif->GetParent() == aChildFrame->GetParent()) {
338 0 : id = nsIFrame::kExcessOverflowContainersList;
339 : }
340 : else {
341 0 : id = nsIFrame::kOverflowContainersList;
342 : }
343 : }
344 : // See if the frame is moved out of the flow
345 0 : else if (aChildFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
346 : // Look at the style information to tell
347 0 : const nsStyleDisplay* disp = aChildFrame->GetStyleDisplay();
348 :
349 0 : if (NS_STYLE_POSITION_ABSOLUTE == disp->mPosition) {
350 0 : id = nsIFrame::kAbsoluteList;
351 0 : } else if (NS_STYLE_POSITION_FIXED == disp->mPosition) {
352 0 : if (nsLayoutUtils::IsReallyFixedPos(aChildFrame)) {
353 0 : id = nsIFrame::kFixedList;
354 : } else {
355 0 : id = nsIFrame::kAbsoluteList;
356 : }
357 : #ifdef MOZ_XUL
358 0 : } else if (NS_STYLE_DISPLAY_POPUP == disp->mDisplay) {
359 : // Out-of-flows that are DISPLAY_POPUP must be kids of the root popup set
360 : #ifdef DEBUG
361 0 : nsIFrame* parent = aChildFrame->GetParent();
362 0 : NS_ASSERTION(parent && parent->GetType() == nsGkAtoms::popupSetFrame,
363 : "Unexpected parent");
364 : #endif // DEBUG
365 :
366 : // XXX FIXME: Bug 350740
367 : // Return here, because the postcondition for this function actually
368 : // fails for this case, since the popups are not in a "real" frame list
369 : // in the popup set.
370 0 : return nsIFrame::kPopupList;
371 : #endif // MOZ_XUL
372 : } else {
373 0 : NS_ASSERTION(aChildFrame->GetStyleDisplay()->IsFloating(),
374 : "not a floated frame");
375 0 : id = nsIFrame::kFloatList;
376 : }
377 :
378 : } else {
379 0 : nsIAtom* childType = aChildFrame->GetType();
380 0 : if (nsGkAtoms::menuPopupFrame == childType) {
381 0 : nsIFrame* parent = aChildFrame->GetParent();
382 : nsIFrame* firstPopup = (parent)
383 : ? parent->GetFirstChild(nsIFrame::kPopupList)
384 0 : : nsnull;
385 0 : NS_ASSERTION(!firstPopup || !firstPopup->GetNextSibling(),
386 : "We assume popupList only has one child, but it has more.");
387 : id = firstPopup == aChildFrame
388 : ? nsIFrame::kPopupList
389 0 : : nsIFrame::kPrincipalList;
390 0 : } else if (nsGkAtoms::tableColGroupFrame == childType) {
391 0 : id = nsIFrame::kColGroupList;
392 0 : } else if (nsGkAtoms::tableCaptionFrame == aChildFrame->GetType()) {
393 0 : id = nsIFrame::kCaptionList;
394 : } else {
395 0 : id = nsIFrame::kPrincipalList;
396 : }
397 : }
398 :
399 : #ifdef NS_DEBUG
400 : // Verify that the frame is actually in that child list or in the
401 : // corresponding overflow list.
402 0 : nsIFrame* parent = aChildFrame->GetParent();
403 0 : bool found = parent->GetChildList(id).ContainsFrame(aChildFrame);
404 0 : if (!found) {
405 0 : if (!(aChildFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
406 0 : found = parent->GetChildList(nsIFrame::kOverflowList)
407 0 : .ContainsFrame(aChildFrame);
408 : }
409 0 : else if (aChildFrame->GetStyleDisplay()->IsFloating()) {
410 0 : found = parent->GetChildList(nsIFrame::kOverflowOutOfFlowList)
411 0 : .ContainsFrame(aChildFrame);
412 : }
413 : // else it's positioned and should have been on the 'id' child list.
414 0 : NS_POSTCONDITION(found, "not in child list");
415 : }
416 : #endif
417 :
418 0 : return id;
419 : }
420 :
421 : // static
422 : nsIFrame*
423 0 : nsLayoutUtils::GetBeforeFrame(nsIFrame* aFrame)
424 : {
425 0 : NS_PRECONDITION(aFrame, "NULL frame pointer");
426 0 : NS_ASSERTION(!aFrame->GetPrevContinuation(),
427 : "aFrame must be first continuation");
428 :
429 0 : nsIFrame* firstFrame = GetFirstChildFrame(aFrame, aFrame->GetContent());
430 :
431 0 : if (firstFrame && IsGeneratedContentFor(nsnull, firstFrame,
432 0 : nsCSSPseudoElements::before)) {
433 0 : return firstFrame;
434 : }
435 :
436 0 : return nsnull;
437 : }
438 :
439 : // static
440 : nsIFrame*
441 0 : nsLayoutUtils::GetAfterFrame(nsIFrame* aFrame)
442 : {
443 0 : NS_PRECONDITION(aFrame, "NULL frame pointer");
444 :
445 0 : nsIFrame* lastFrame = GetLastChildFrame(aFrame, aFrame->GetContent());
446 :
447 0 : if (lastFrame && IsGeneratedContentFor(nsnull, lastFrame,
448 0 : nsCSSPseudoElements::after)) {
449 0 : return lastFrame;
450 : }
451 :
452 0 : return nsnull;
453 : }
454 :
455 : // static
456 : nsIFrame*
457 0 : nsLayoutUtils::GetClosestFrameOfType(nsIFrame* aFrame, nsIAtom* aFrameType)
458 : {
459 0 : for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
460 0 : if (frame->GetType() == aFrameType) {
461 0 : return frame;
462 : }
463 : }
464 0 : return nsnull;
465 : }
466 :
467 : // static
468 : nsIFrame*
469 0 : nsLayoutUtils::GetStyleFrame(nsIFrame* aFrame)
470 : {
471 0 : if (aFrame->GetType() == nsGkAtoms::tableOuterFrame) {
472 0 : nsIFrame* inner = aFrame->GetFirstPrincipalChild();
473 0 : NS_ASSERTION(inner, "Outer table must have an inner");
474 0 : return inner;
475 : }
476 :
477 0 : return aFrame;
478 : }
479 :
480 : nsIFrame*
481 0 : nsLayoutUtils::GetFloatFromPlaceholder(nsIFrame* aFrame) {
482 0 : NS_ASSERTION(nsGkAtoms::placeholderFrame == aFrame->GetType(),
483 : "Must have a placeholder here");
484 0 : if (aFrame->GetStateBits() & PLACEHOLDER_FOR_FLOAT) {
485 : nsIFrame *outOfFlowFrame =
486 0 : nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
487 0 : NS_ASSERTION(outOfFlowFrame->GetStyleDisplay()->IsFloating(),
488 : "How did that happen?");
489 0 : return outOfFlowFrame;
490 : }
491 :
492 0 : return nsnull;
493 : }
494 :
495 : // static
496 : bool
497 0 : nsLayoutUtils::IsGeneratedContentFor(nsIContent* aContent,
498 : nsIFrame* aFrame,
499 : nsIAtom* aPseudoElement)
500 : {
501 0 : NS_PRECONDITION(aFrame, "Must have a frame");
502 0 : NS_PRECONDITION(aPseudoElement, "Must have a pseudo name");
503 :
504 0 : if (!aFrame->IsGeneratedContentFrame()) {
505 0 : return false;
506 : }
507 0 : nsIFrame* parent = aFrame->GetParent();
508 0 : NS_ASSERTION(parent, "Generated content can't be root frame");
509 0 : if (parent->IsGeneratedContentFrame()) {
510 : // Not the root of the generated content
511 0 : return false;
512 : }
513 :
514 0 : if (aContent && parent->GetContent() != aContent) {
515 0 : return false;
516 : }
517 :
518 0 : return (aFrame->GetContent()->Tag() == nsGkAtoms::mozgeneratedcontentbefore) ==
519 0 : (aPseudoElement == nsCSSPseudoElements::before);
520 : }
521 :
522 : // static
523 : nsIFrame*
524 0 : nsLayoutUtils::GetCrossDocParentFrame(const nsIFrame* aFrame,
525 : nsPoint* aExtraOffset)
526 : {
527 0 : nsIFrame* p = aFrame->GetParent();
528 0 : if (p)
529 0 : return p;
530 :
531 0 : nsIView* v = aFrame->GetView();
532 0 : if (!v)
533 0 : return nsnull;
534 0 : v = v->GetParent(); // anonymous inner view
535 0 : if (!v)
536 0 : return nsnull;
537 0 : if (aExtraOffset) {
538 0 : *aExtraOffset += v->GetPosition();
539 : }
540 0 : v = v->GetParent(); // subdocumentframe's view
541 0 : return v ? v->GetFrame() : nsnull;
542 : }
543 :
544 : // static
545 : bool
546 0 : nsLayoutUtils::IsProperAncestorFrameCrossDoc(nsIFrame* aAncestorFrame, nsIFrame* aFrame,
547 : nsIFrame* aCommonAncestor)
548 : {
549 0 : if (aFrame == aCommonAncestor)
550 0 : return false;
551 :
552 0 : nsIFrame* parentFrame = GetCrossDocParentFrame(aFrame);
553 :
554 0 : while (parentFrame != aCommonAncestor) {
555 0 : if (parentFrame == aAncestorFrame)
556 0 : return true;
557 :
558 0 : parentFrame = GetCrossDocParentFrame(parentFrame);
559 : }
560 :
561 0 : return false;
562 : }
563 :
564 : // static
565 : bool
566 0 : nsLayoutUtils::IsAncestorFrameCrossDoc(nsIFrame* aAncestorFrame, nsIFrame* aFrame,
567 : nsIFrame* aCommonAncestor)
568 : {
569 0 : if (aFrame == aAncestorFrame)
570 0 : return true;
571 0 : return IsProperAncestorFrameCrossDoc(aAncestorFrame, aFrame, aCommonAncestor);
572 : }
573 :
574 : // static
575 : bool
576 0 : nsLayoutUtils::IsProperAncestorFrame(nsIFrame* aAncestorFrame, nsIFrame* aFrame,
577 : nsIFrame* aCommonAncestor)
578 : {
579 0 : if (aFrame == aCommonAncestor) {
580 0 : return false;
581 : }
582 :
583 0 : nsIFrame* parentFrame = aFrame->GetParent();
584 :
585 0 : while (parentFrame != aCommonAncestor) {
586 0 : if (parentFrame == aAncestorFrame) {
587 0 : return true;
588 : }
589 :
590 0 : parentFrame = parentFrame->GetParent();
591 : }
592 :
593 0 : return false;
594 : }
595 :
596 : // static
597 : PRInt32
598 0 : nsLayoutUtils::DoCompareTreePosition(nsIContent* aContent1,
599 : nsIContent* aContent2,
600 : PRInt32 aIf1Ancestor,
601 : PRInt32 aIf2Ancestor,
602 : const nsIContent* aCommonAncestor)
603 : {
604 0 : NS_PRECONDITION(aContent1, "aContent1 must not be null");
605 0 : NS_PRECONDITION(aContent2, "aContent2 must not be null");
606 :
607 0 : nsAutoTArray<nsINode*, 32> content1Ancestors;
608 : nsINode* c1;
609 0 : for (c1 = aContent1; c1 && c1 != aCommonAncestor; c1 = c1->GetNodeParent()) {
610 0 : content1Ancestors.AppendElement(c1);
611 : }
612 0 : if (!c1 && aCommonAncestor) {
613 : // So, it turns out aCommonAncestor was not an ancestor of c1. Oops.
614 : // Never mind. We can continue as if aCommonAncestor was null.
615 0 : aCommonAncestor = nsnull;
616 : }
617 :
618 0 : nsAutoTArray<nsINode*, 32> content2Ancestors;
619 : nsINode* c2;
620 0 : for (c2 = aContent2; c2 && c2 != aCommonAncestor; c2 = c2->GetNodeParent()) {
621 0 : content2Ancestors.AppendElement(c2);
622 : }
623 0 : if (!c2 && aCommonAncestor) {
624 : // So, it turns out aCommonAncestor was not an ancestor of c2.
625 : // We need to retry with no common ancestor hint.
626 : return DoCompareTreePosition(aContent1, aContent2,
627 0 : aIf1Ancestor, aIf2Ancestor, nsnull);
628 : }
629 :
630 0 : int last1 = content1Ancestors.Length() - 1;
631 0 : int last2 = content2Ancestors.Length() - 1;
632 0 : nsINode* content1Ancestor = nsnull;
633 0 : nsINode* content2Ancestor = nsnull;
634 0 : while (last1 >= 0 && last2 >= 0
635 0 : && ((content1Ancestor = content1Ancestors.ElementAt(last1)) ==
636 0 : (content2Ancestor = content2Ancestors.ElementAt(last2)))) {
637 0 : last1--;
638 0 : last2--;
639 : }
640 :
641 0 : if (last1 < 0) {
642 0 : if (last2 < 0) {
643 0 : NS_ASSERTION(aContent1 == aContent2, "internal error?");
644 0 : return 0;
645 : }
646 : // aContent1 is an ancestor of aContent2
647 0 : return aIf1Ancestor;
648 : }
649 :
650 0 : if (last2 < 0) {
651 : // aContent2 is an ancestor of aContent1
652 0 : return aIf2Ancestor;
653 : }
654 :
655 : // content1Ancestor != content2Ancestor, so they must be siblings with the same parent
656 0 : nsINode* parent = content1Ancestor->GetNodeParent();
657 : #ifdef DEBUG
658 : // TODO: remove the uglyness, see bug 598468.
659 0 : NS_ASSERTION(gPreventAssertInCompareTreePosition || parent,
660 : "no common ancestor at all???");
661 : #endif // DEBUG
662 0 : if (!parent) { // different documents??
663 0 : return 0;
664 : }
665 :
666 0 : PRInt32 index1 = parent->IndexOf(content1Ancestor);
667 0 : PRInt32 index2 = parent->IndexOf(content2Ancestor);
668 0 : if (index1 < 0 || index2 < 0) {
669 : // one of them must be anonymous; we can't determine the order
670 0 : return 0;
671 : }
672 :
673 0 : return index1 - index2;
674 : }
675 :
676 0 : static nsIFrame* FillAncestors(nsIFrame* aFrame,
677 : nsIFrame* aStopAtAncestor, nsFrameManager* aFrameManager,
678 : nsTArray<nsIFrame*>* aAncestors)
679 : {
680 0 : while (aFrame && aFrame != aStopAtAncestor) {
681 0 : aAncestors->AppendElement(aFrame);
682 0 : aFrame = nsLayoutUtils::GetParentOrPlaceholderFor(aFrameManager, aFrame);
683 : }
684 0 : return aFrame;
685 : }
686 :
687 : // Return true if aFrame1 is after aFrame2
688 0 : static bool IsFrameAfter(nsIFrame* aFrame1, nsIFrame* aFrame2)
689 : {
690 0 : nsIFrame* f = aFrame2;
691 0 : do {
692 0 : f = f->GetNextSibling();
693 0 : if (f == aFrame1)
694 0 : return true;
695 : } while (f);
696 0 : return false;
697 : }
698 :
699 : // static
700 : PRInt32
701 0 : nsLayoutUtils::DoCompareTreePosition(nsIFrame* aFrame1,
702 : nsIFrame* aFrame2,
703 : PRInt32 aIf1Ancestor,
704 : PRInt32 aIf2Ancestor,
705 : nsIFrame* aCommonAncestor)
706 : {
707 0 : NS_PRECONDITION(aFrame1, "aFrame1 must not be null");
708 0 : NS_PRECONDITION(aFrame2, "aFrame2 must not be null");
709 :
710 0 : nsPresContext* presContext = aFrame1->PresContext();
711 0 : if (presContext != aFrame2->PresContext()) {
712 0 : NS_ERROR("no common ancestor at all, different documents");
713 0 : return 0;
714 : }
715 0 : nsFrameManager* frameManager = presContext->PresShell()->FrameManager();
716 :
717 0 : nsAutoTArray<nsIFrame*,20> frame1Ancestors;
718 0 : if (!FillAncestors(aFrame1, aCommonAncestor, frameManager, &frame1Ancestors)) {
719 : // We reached the root of the frame tree ... if aCommonAncestor was set,
720 : // it is wrong
721 0 : aCommonAncestor = nsnull;
722 : }
723 :
724 0 : nsAutoTArray<nsIFrame*,20> frame2Ancestors;
725 0 : if (!FillAncestors(aFrame2, aCommonAncestor, frameManager, &frame2Ancestors) &&
726 : aCommonAncestor) {
727 : // We reached the root of the frame tree ... aCommonAncestor was wrong.
728 : // Try again with no hint.
729 : return DoCompareTreePosition(aFrame1, aFrame2,
730 0 : aIf1Ancestor, aIf2Ancestor, nsnull);
731 : }
732 :
733 0 : PRInt32 last1 = PRInt32(frame1Ancestors.Length()) - 1;
734 0 : PRInt32 last2 = PRInt32(frame2Ancestors.Length()) - 1;
735 0 : while (last1 >= 0 && last2 >= 0 &&
736 0 : frame1Ancestors[last1] == frame2Ancestors[last2]) {
737 0 : last1--;
738 0 : last2--;
739 : }
740 :
741 0 : if (last1 < 0) {
742 0 : if (last2 < 0) {
743 0 : NS_ASSERTION(aFrame1 == aFrame2, "internal error?");
744 0 : return 0;
745 : }
746 : // aFrame1 is an ancestor of aFrame2
747 0 : return aIf1Ancestor;
748 : }
749 :
750 0 : if (last2 < 0) {
751 : // aFrame2 is an ancestor of aFrame1
752 0 : return aIf2Ancestor;
753 : }
754 :
755 0 : nsIFrame* ancestor1 = frame1Ancestors[last1];
756 0 : nsIFrame* ancestor2 = frame2Ancestors[last2];
757 : // Now we should be able to walk sibling chains to find which one is first
758 0 : if (IsFrameAfter(ancestor2, ancestor1))
759 0 : return -1;
760 0 : if (IsFrameAfter(ancestor1, ancestor2))
761 0 : return 1;
762 0 : NS_WARNING("Frames were in different child lists???");
763 0 : return 0;
764 : }
765 :
766 : // static
767 0 : nsIFrame* nsLayoutUtils::GetLastSibling(nsIFrame* aFrame) {
768 0 : if (!aFrame) {
769 0 : return nsnull;
770 : }
771 :
772 : nsIFrame* next;
773 0 : while ((next = aFrame->GetNextSibling()) != nsnull) {
774 0 : aFrame = next;
775 : }
776 0 : return aFrame;
777 : }
778 :
779 : // static
780 : nsIView*
781 0 : nsLayoutUtils::FindSiblingViewFor(nsIView* aParentView, nsIFrame* aFrame) {
782 0 : nsIFrame* parentViewFrame = aParentView->GetFrame();
783 0 : nsIContent* parentViewContent = parentViewFrame ? parentViewFrame->GetContent() : nsnull;
784 0 : for (nsIView* insertBefore = aParentView->GetFirstChild(); insertBefore;
785 : insertBefore = insertBefore->GetNextSibling()) {
786 0 : nsIFrame* f = insertBefore->GetFrame();
787 0 : if (!f) {
788 : // this view could be some anonymous view attached to a meaningful parent
789 0 : for (nsIView* searchView = insertBefore->GetParent(); searchView;
790 : searchView = searchView->GetParent()) {
791 0 : f = searchView->GetFrame();
792 0 : if (f) {
793 0 : break;
794 : }
795 : }
796 0 : NS_ASSERTION(f, "Can't find a frame anywhere!");
797 : }
798 0 : if (!f || !aFrame->GetContent() || !f->GetContent() ||
799 0 : CompareTreePosition(aFrame->GetContent(), f->GetContent(), parentViewContent) > 0) {
800 : // aFrame's content is after f's content (or we just don't know),
801 : // so put our view before f's view
802 0 : return insertBefore;
803 : }
804 : }
805 0 : return nsnull;
806 : }
807 :
808 : //static
809 : nsIScrollableFrame*
810 0 : nsLayoutUtils::GetScrollableFrameFor(nsIFrame *aScrolledFrame)
811 : {
812 0 : nsIFrame *frame = aScrolledFrame->GetParent();
813 0 : if (!frame) {
814 0 : return nsnull;
815 : }
816 0 : nsIScrollableFrame *sf = do_QueryFrame(frame);
817 0 : return sf;
818 : }
819 :
820 : nsIFrame*
821 0 : nsLayoutUtils::GetActiveScrolledRootFor(nsIFrame* aFrame,
822 : nsIFrame* aStopAtAncestor)
823 : {
824 0 : nsIFrame* f = aFrame;
825 0 : while (f != aStopAtAncestor) {
826 0 : if (IsPopup(f))
827 0 : break;
828 0 : nsIFrame* parent = GetCrossDocParentFrame(f);
829 0 : if (!parent)
830 0 : break;
831 0 : nsIScrollableFrame* sf = do_QueryFrame(parent);
832 0 : if (sf && sf->IsScrollingActive() && sf->GetScrolledFrame() == f)
833 0 : break;
834 0 : f = parent;
835 : }
836 0 : return f;
837 : }
838 :
839 : nsIFrame*
840 0 : nsLayoutUtils::GetActiveScrolledRootFor(nsDisplayItem* aItem,
841 : nsDisplayListBuilder* aBuilder,
842 : bool* aShouldFixToViewport)
843 : {
844 0 : nsIFrame* f = aItem->GetUnderlyingFrame();
845 0 : if (aShouldFixToViewport) {
846 0 : *aShouldFixToViewport = false;
847 : }
848 0 : if (!f) {
849 0 : return nsnull;
850 : }
851 0 : if (aItem->ShouldFixToViewport(aBuilder)) {
852 0 : if (aShouldFixToViewport) {
853 0 : *aShouldFixToViewport = true;
854 : }
855 : // Make its active scrolled root be the active scrolled root of
856 : // the enclosing viewport, since it shouldn't be scrolled by scrolled
857 : // frames in its document. InvalidateFixedBackgroundFramesFromList in
858 : // nsGfxScrollFrame will not repaint this item when scrolling occurs.
859 : nsIFrame* viewportFrame =
860 0 : nsLayoutUtils::GetClosestFrameOfType(f, nsGkAtoms::viewportFrame);
861 0 : NS_ASSERTION(viewportFrame, "no viewport???");
862 0 : return nsLayoutUtils::GetActiveScrolledRootFor(viewportFrame, aBuilder->ReferenceFrame());
863 : } else {
864 0 : return nsLayoutUtils::GetActiveScrolledRootFor(f, aBuilder->ReferenceFrame());
865 : }
866 : }
867 :
868 : bool
869 0 : nsLayoutUtils::ScrolledByViewportScrolling(nsIFrame* aActiveScrolledRoot,
870 : nsDisplayListBuilder* aBuilder)
871 : {
872 : nsIFrame* rootScrollFrame =
873 0 : aBuilder->ReferenceFrame()->PresContext()->GetPresShell()->GetRootScrollFrame();
874 0 : return nsLayoutUtils::IsAncestorFrameCrossDoc(rootScrollFrame, aActiveScrolledRoot);
875 : }
876 :
877 : // static
878 : nsIScrollableFrame*
879 0 : nsLayoutUtils::GetNearestScrollableFrameForDirection(nsIFrame* aFrame,
880 : Direction aDirection)
881 : {
882 0 : NS_ASSERTION(aFrame, "GetNearestScrollableFrameForDirection expects a non-null frame");
883 0 : for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
884 0 : nsIScrollableFrame* scrollableFrame = do_QueryFrame(f);
885 0 : if (scrollableFrame) {
886 0 : nsPresContext::ScrollbarStyles ss = scrollableFrame->GetScrollbarStyles();
887 0 : PRUint32 scrollbarVisibility = scrollableFrame->GetScrollbarVisibility();
888 0 : nsRect scrollRange = scrollableFrame->GetScrollRange();
889 : // Require visible scrollbars or something to scroll to in
890 : // the given direction.
891 0 : if (aDirection == eVertical ?
892 : (ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN &&
893 : ((scrollbarVisibility & nsIScrollableFrame::VERTICAL) ||
894 : scrollRange.height > 0)) :
895 : (ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN &&
896 : ((scrollbarVisibility & nsIScrollableFrame::HORIZONTAL) ||
897 : scrollRange.width > 0)))
898 0 : return scrollableFrame;
899 : }
900 : }
901 0 : return nsnull;
902 : }
903 :
904 : // static
905 : nsIScrollableFrame*
906 0 : nsLayoutUtils::GetNearestScrollableFrame(nsIFrame* aFrame)
907 : {
908 0 : NS_ASSERTION(aFrame, "GetNearestScrollableFrame expects a non-null frame");
909 0 : for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
910 0 : nsIScrollableFrame* scrollableFrame = do_QueryFrame(f);
911 0 : if (scrollableFrame) {
912 0 : nsPresContext::ScrollbarStyles ss = scrollableFrame->GetScrollbarStyles();
913 0 : if (ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN ||
914 : ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN)
915 0 : return scrollableFrame;
916 : }
917 : }
918 0 : return nsnull;
919 : }
920 :
921 : //static
922 : bool
923 0 : nsLayoutUtils::HasPseudoStyle(nsIContent* aContent,
924 : nsStyleContext* aStyleContext,
925 : nsCSSPseudoElements::Type aPseudoElement,
926 : nsPresContext* aPresContext)
927 : {
928 0 : NS_PRECONDITION(aPresContext, "Must have a prescontext");
929 :
930 0 : nsRefPtr<nsStyleContext> pseudoContext;
931 0 : if (aContent) {
932 : pseudoContext = aPresContext->StyleSet()->
933 : ProbePseudoElementStyle(aContent->AsElement(), aPseudoElement,
934 0 : aStyleContext);
935 : }
936 0 : return pseudoContext != nsnull;
937 : }
938 :
939 : nsPoint
940 0 : nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(nsIDOMEvent* aDOMEvent, nsIFrame* aFrame)
941 : {
942 0 : nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(aDOMEvent));
943 0 : NS_ASSERTION(privateEvent, "bad implementation");
944 0 : if (!privateEvent)
945 0 : return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
946 0 : nsEvent *event = privateEvent->GetInternalNSEvent();
947 0 : if (!event)
948 0 : return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
949 0 : return GetEventCoordinatesRelativeTo(event, aFrame);
950 : }
951 :
952 : nsPoint
953 0 : nsLayoutUtils::GetEventCoordinatesRelativeTo(const nsEvent* aEvent, nsIFrame* aFrame)
954 : {
955 0 : if (!aEvent || (aEvent->eventStructType != NS_MOUSE_EVENT &&
956 : aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT &&
957 : aEvent->eventStructType != NS_DRAG_EVENT &&
958 : aEvent->eventStructType != NS_SIMPLE_GESTURE_EVENT &&
959 : aEvent->eventStructType != NS_GESTURENOTIFY_EVENT &&
960 : aEvent->eventStructType != NS_MOZTOUCH_EVENT &&
961 : #ifdef MOZ_TOUCH
962 : aEvent->eventStructType != NS_TOUCH_EVENT &&
963 : #endif
964 : aEvent->eventStructType != NS_QUERY_CONTENT_EVENT))
965 0 : return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
966 :
967 0 : const nsGUIEvent* GUIEvent = static_cast<const nsGUIEvent*>(aEvent);
968 : #ifdef MOZ_TOUCH
969 : return GetEventCoordinatesRelativeTo(aEvent,
970 : GUIEvent->refPoint,
971 : aFrame);
972 : #else
973 0 : if (!GUIEvent->widget)
974 0 : return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
975 :
976 0 : nsIView* view = aFrame->GetView();
977 0 : if (view) {
978 0 : nsIWidget* widget = view->GetWidget();
979 0 : if (widget && widget == GUIEvent->widget) {
980 : // Special case this cause it happens a lot.
981 : // This also fixes bug 664707, events in the extra-special case of select
982 : // dropdown popups that are transformed.
983 0 : nsPresContext* presContext = aFrame->PresContext();
984 : nsPoint pt(presContext->DevPixelsToAppUnits(GUIEvent->refPoint.x),
985 0 : presContext->DevPixelsToAppUnits(GUIEvent->refPoint.y));
986 0 : return pt - view->ViewToWidgetOffset();
987 : }
988 : }
989 :
990 : /* If we walk up the frame tree and discover that any of the frames are
991 : * transformed, we need to do extra work to convert from the global
992 : * space to the local space.
993 : */
994 0 : nsIFrame* rootFrame = aFrame;
995 0 : bool transformFound = false;
996 :
997 0 : for (nsIFrame* f = aFrame; f; f = GetCrossDocParentFrame(f)) {
998 0 : if (f->IsTransformed())
999 0 : transformFound = true;
1000 0 : rootFrame = f;
1001 : }
1002 :
1003 0 : nsIView* rootView = rootFrame->GetView();
1004 0 : if (!rootView)
1005 0 : return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
1006 :
1007 : nsPoint widgetToView = TranslateWidgetToView(rootFrame->PresContext(),
1008 : GUIEvent->widget, GUIEvent->refPoint,
1009 0 : rootView);
1010 :
1011 0 : if (widgetToView == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE))
1012 0 : return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
1013 :
1014 : // Convert from root document app units to app units of the document aFrame
1015 : // is in.
1016 0 : PRInt32 rootAPD = rootFrame->PresContext()->AppUnitsPerDevPixel();
1017 0 : PRInt32 localAPD = aFrame->PresContext()->AppUnitsPerDevPixel();
1018 0 : widgetToView = widgetToView.ConvertAppUnits(rootAPD, localAPD);
1019 :
1020 : /* If we encountered a transform, we can't do simple arithmetic to figure
1021 : * out how to convert back to aFrame's coordinates and must use the CTM.
1022 : */
1023 0 : if (transformFound)
1024 0 : return TransformRootPointToFrame(aFrame, widgetToView);
1025 :
1026 : /* Otherwise, all coordinate systems are translations of one another,
1027 : * so we can just subtract out the different.
1028 : */
1029 0 : return widgetToView - aFrame->GetOffsetToCrossDoc(rootFrame);
1030 : #endif
1031 : }
1032 :
1033 : nsPoint
1034 0 : nsLayoutUtils::GetEventCoordinatesRelativeTo(const nsEvent* aEvent,
1035 : const nsIntPoint aPoint,
1036 : nsIFrame* aFrame)
1037 : {
1038 0 : if (!aFrame) {
1039 0 : return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
1040 : }
1041 :
1042 0 : const nsGUIEvent* GUIEvent = static_cast<const nsGUIEvent*>(aEvent);
1043 0 : nsIWidget* widget = GUIEvent->widget;
1044 0 : if (!widget) {
1045 0 : return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
1046 : }
1047 :
1048 0 : nsIView* view = aFrame->GetView();
1049 0 : if (view) {
1050 0 : nsIWidget* frameWidget = view->GetWidget();
1051 0 : if (frameWidget && frameWidget == GUIEvent->widget) {
1052 : // Special case this cause it happens a lot.
1053 : // This also fixes bug 664707, events in the extra-special case of select
1054 : // dropdown popups that are transformed.
1055 0 : nsPresContext* presContext = aFrame->PresContext();
1056 : nsPoint pt(presContext->DevPixelsToAppUnits(aPoint.x),
1057 0 : presContext->DevPixelsToAppUnits(aPoint.y));
1058 0 : return pt - view->ViewToWidgetOffset();
1059 : }
1060 : }
1061 :
1062 : /* If we walk up the frame tree and discover that any of the frames are
1063 : * transformed, we need to do extra work to convert from the global
1064 : * space to the local space.
1065 : */
1066 0 : nsIFrame* rootFrame = aFrame;
1067 0 : bool transformFound = false;
1068 0 : for (nsIFrame* f = aFrame; f; f = GetCrossDocParentFrame(f)) {
1069 0 : if (f->IsTransformed()) {
1070 0 : transformFound = true;
1071 : }
1072 :
1073 0 : rootFrame = f;
1074 : }
1075 :
1076 0 : nsIView* rootView = rootFrame->GetView();
1077 0 : if (!rootView) {
1078 0 : return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
1079 : }
1080 :
1081 : nsPoint widgetToView = TranslateWidgetToView(rootFrame->PresContext(),
1082 0 : widget, aPoint, rootView);
1083 :
1084 0 : if (widgetToView == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) {
1085 0 : return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
1086 : }
1087 :
1088 : // Convert from root document app units to app units of the document aFrame
1089 : // is in.
1090 0 : PRInt32 rootAPD = rootFrame->PresContext()->AppUnitsPerDevPixel();
1091 0 : PRInt32 localAPD = aFrame->PresContext()->AppUnitsPerDevPixel();
1092 0 : widgetToView = widgetToView.ConvertAppUnits(rootAPD, localAPD);
1093 :
1094 : /* If we encountered a transform, we can't do simple arithmetic to figure
1095 : * out how to convert back to aFrame's coordinates and must use the CTM.
1096 : */
1097 0 : if (transformFound) {
1098 0 : return TransformRootPointToFrame(aFrame, widgetToView);
1099 : }
1100 :
1101 : /* Otherwise, all coordinate systems are translations of one another,
1102 : * so we can just subtract out the different.
1103 : */
1104 0 : nsPoint offset = aFrame->GetOffsetToCrossDoc(rootFrame);
1105 0 : return widgetToView - offset;
1106 : }
1107 :
1108 : nsIFrame*
1109 0 : nsLayoutUtils::GetPopupFrameForEventCoordinates(nsPresContext* aPresContext,
1110 : const nsEvent* aEvent)
1111 : {
1112 : #ifdef MOZ_XUL
1113 0 : nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
1114 0 : if (!pm) {
1115 0 : return nsnull;
1116 : }
1117 0 : nsTArray<nsIFrame*> popups = pm->GetVisiblePopups();
1118 : PRUint32 i;
1119 : // Search from top to bottom
1120 0 : for (i = 0; i < popups.Length(); i++) {
1121 0 : nsIFrame* popup = popups[i];
1122 0 : if (popup->PresContext()->GetRootPresContext() == aPresContext &&
1123 0 : popup->GetScrollableOverflowRect().Contains(
1124 0 : GetEventCoordinatesRelativeTo(aEvent, popup))) {
1125 0 : return popup;
1126 : }
1127 : }
1128 : #endif
1129 0 : return nsnull;
1130 : }
1131 :
1132 : gfx3DMatrix
1133 0 : nsLayoutUtils::ChangeMatrixBasis(const gfxPoint3D &aOrigin,
1134 : const gfx3DMatrix &aMatrix)
1135 : {
1136 0 : gfx3DMatrix result = aMatrix;
1137 :
1138 : /* Translate to the origin before aMatrix */
1139 0 : result.Translate(-aOrigin);
1140 :
1141 : /* Translate back into position after aMatrix */
1142 0 : result.TranslatePost(aOrigin);
1143 :
1144 : return result;
1145 : }
1146 :
1147 : /**
1148 : * Given a gfxFloat, constrains its value to be between nscoord_MIN and nscoord_MAX.
1149 : *
1150 : * @param aVal The value to constrain (in/out)
1151 : */
1152 0 : static void ConstrainToCoordValues(gfxFloat &aVal)
1153 : {
1154 0 : if (aVal <= nscoord_MIN)
1155 0 : aVal = nscoord_MIN;
1156 0 : else if (aVal >= nscoord_MAX)
1157 0 : aVal = nscoord_MAX;
1158 0 : }
1159 :
1160 : nsRect
1161 0 : nsLayoutUtils::RoundGfxRectToAppRect(const gfxRect &aRect, float aFactor)
1162 : {
1163 : /* Get a new gfxRect whose units are app units by scaling by the specified factor. */
1164 0 : gfxRect scaledRect = aRect;
1165 0 : scaledRect.ScaleRoundOut(aFactor);
1166 :
1167 : /* We now need to constrain our results to the max and min values for coords. */
1168 0 : ConstrainToCoordValues(scaledRect.x);
1169 0 : ConstrainToCoordValues(scaledRect.y);
1170 0 : ConstrainToCoordValues(scaledRect.width);
1171 0 : ConstrainToCoordValues(scaledRect.height);
1172 :
1173 : /* Now typecast everything back. This is guaranteed to be safe. */
1174 0 : return nsRect(nscoord(scaledRect.X()), nscoord(scaledRect.Y()),
1175 0 : nscoord(scaledRect.Width()), nscoord(scaledRect.Height()));
1176 : }
1177 :
1178 :
1179 : nsRegion
1180 0 : nsLayoutUtils::RoundedRectIntersectRect(const nsRect& aRoundedRect,
1181 : const nscoord aRadii[8],
1182 : const nsRect& aContainedRect)
1183 : {
1184 : // rectFullHeight and rectFullWidth together will approximately contain
1185 : // the total area of the frame minus the rounded corners.
1186 0 : nsRect rectFullHeight = aRoundedRect;
1187 0 : nscoord xDiff = NS_MAX(aRadii[NS_CORNER_TOP_LEFT_X], aRadii[NS_CORNER_BOTTOM_LEFT_X]);
1188 0 : rectFullHeight.x += xDiff;
1189 0 : rectFullHeight.width -= NS_MAX(aRadii[NS_CORNER_TOP_RIGHT_X],
1190 0 : aRadii[NS_CORNER_BOTTOM_RIGHT_X]) + xDiff;
1191 0 : nsRect r1;
1192 0 : r1.IntersectRect(rectFullHeight, aContainedRect);
1193 :
1194 0 : nsRect rectFullWidth = aRoundedRect;
1195 0 : nscoord yDiff = NS_MAX(aRadii[NS_CORNER_TOP_LEFT_Y], aRadii[NS_CORNER_TOP_RIGHT_Y]);
1196 0 : rectFullWidth.y += yDiff;
1197 0 : rectFullWidth.height -= NS_MAX(aRadii[NS_CORNER_BOTTOM_LEFT_Y],
1198 0 : aRadii[NS_CORNER_BOTTOM_RIGHT_Y]) + yDiff;
1199 0 : nsRect r2;
1200 0 : r2.IntersectRect(rectFullWidth, aContainedRect);
1201 :
1202 0 : nsRegion result;
1203 0 : result.Or(r1, r2);
1204 : return result;
1205 : }
1206 :
1207 : nsRect
1208 0 : nsLayoutUtils::MatrixTransformRectOut(const nsRect &aBounds,
1209 : const gfx3DMatrix &aMatrix, float aFactor)
1210 : {
1211 0 : nsRect outside = aBounds;
1212 0 : outside.ScaleRoundOut(1/aFactor);
1213 : gfxRect image = aMatrix.TransformBounds(gfxRect(outside.x,
1214 : outside.y,
1215 : outside.width,
1216 0 : outside.height));
1217 0 : return RoundGfxRectToAppRect(image, aFactor);
1218 : }
1219 :
1220 : nsRect
1221 0 : nsLayoutUtils::MatrixTransformRect(const nsRect &aBounds,
1222 : const gfx3DMatrix &aMatrix, float aFactor)
1223 : {
1224 : gfxRect image = aMatrix.TransformBounds(gfxRect(NSAppUnitsToDoublePixels(aBounds.x, aFactor),
1225 : NSAppUnitsToDoublePixels(aBounds.y, aFactor),
1226 : NSAppUnitsToDoublePixels(aBounds.width, aFactor),
1227 0 : NSAppUnitsToDoublePixels(aBounds.height, aFactor)));
1228 :
1229 0 : return RoundGfxRectToAppRect(image, aFactor);
1230 : }
1231 :
1232 : nsPoint
1233 0 : nsLayoutUtils::MatrixTransformPoint(const nsPoint &aPoint,
1234 : const gfx3DMatrix &aMatrix, float aFactor)
1235 : {
1236 0 : gfxPoint image = aMatrix.Transform(gfxPoint(NSAppUnitsToFloatPixels(aPoint.x, aFactor),
1237 0 : NSAppUnitsToFloatPixels(aPoint.y, aFactor)));
1238 : return nsPoint(NSFloatPixelsToAppUnits(float(image.x), aFactor),
1239 0 : NSFloatPixelsToAppUnits(float(image.y), aFactor));
1240 : }
1241 :
1242 : gfx3DMatrix
1243 0 : nsLayoutUtils::GetTransformToAncestor(nsIFrame *aFrame, nsIFrame *aAncestor)
1244 : {
1245 : nsIFrame* parent;
1246 0 : gfx3DMatrix ctm = aFrame->GetTransformMatrix(aAncestor, &parent);
1247 0 : while (parent && parent != aAncestor) {
1248 0 : if (!parent->Preserves3DChildren()) {
1249 0 : ctm.ProjectTo2D();
1250 : }
1251 0 : ctm = ctm * parent->GetTransformMatrix(aAncestor, &parent);
1252 : }
1253 : return ctm;
1254 : }
1255 :
1256 : static gfxPoint
1257 0 : TransformGfxPointFromAncestor(nsIFrame *aFrame,
1258 : const gfxPoint &aPoint,
1259 : nsIFrame *aAncestor)
1260 : {
1261 0 : gfx3DMatrix ctm = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor);
1262 0 : return ctm.Inverse().ProjectPoint(aPoint);
1263 : }
1264 :
1265 : static gfxRect
1266 0 : TransformGfxRectFromAncestor(nsIFrame *aFrame,
1267 : const gfxRect &aRect,
1268 : nsIFrame *aAncestor)
1269 : {
1270 0 : gfx3DMatrix ctm = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor);
1271 0 : return ctm.Inverse().ProjectRectBounds(aRect);
1272 : }
1273 :
1274 : static gfxRect
1275 0 : TransformGfxRectToAncestor(nsIFrame *aFrame,
1276 : const gfxRect &aRect,
1277 : nsIFrame *aAncestor)
1278 : {
1279 0 : gfx3DMatrix ctm = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor);
1280 0 : return ctm.TransformBounds(aRect);
1281 : }
1282 :
1283 : nsPoint
1284 0 : nsLayoutUtils::TransformRootPointToFrame(nsIFrame *aFrame,
1285 : const nsPoint &aPoint)
1286 : {
1287 0 : float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
1288 0 : gfxPoint result(NSAppUnitsToFloatPixels(aPoint.x, factor),
1289 0 : NSAppUnitsToFloatPixels(aPoint.y, factor));
1290 :
1291 0 : result = TransformGfxPointFromAncestor(aFrame, result, nsnull);
1292 :
1293 : return nsPoint(NSFloatPixelsToAppUnits(float(result.x), factor),
1294 0 : NSFloatPixelsToAppUnits(float(result.y), factor));
1295 : }
1296 :
1297 : nsRect
1298 0 : nsLayoutUtils::TransformAncestorRectToFrame(nsIFrame* aFrame,
1299 : const nsRect &aRect,
1300 : nsIFrame* aAncestor)
1301 : {
1302 0 : float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
1303 0 : gfxRect result(NSAppUnitsToFloatPixels(aRect.x, factor),
1304 0 : NSAppUnitsToFloatPixels(aRect.y, factor),
1305 0 : NSAppUnitsToFloatPixels(aRect.width, factor),
1306 0 : NSAppUnitsToFloatPixels(aRect.height, factor));
1307 :
1308 0 : result = TransformGfxRectFromAncestor(aFrame, result, aAncestor);
1309 :
1310 : return nsRect(NSFloatPixelsToAppUnits(float(result.x), factor),
1311 : NSFloatPixelsToAppUnits(float(result.y), factor),
1312 : NSFloatPixelsToAppUnits(float(result.width), factor),
1313 0 : NSFloatPixelsToAppUnits(float(result.height), factor));
1314 : }
1315 :
1316 : nsRect
1317 0 : nsLayoutUtils::TransformFrameRectToAncestor(nsIFrame* aFrame,
1318 : const nsRect& aRect,
1319 : nsIFrame* aAncestor)
1320 : {
1321 0 : float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
1322 0 : gfxRect result(NSAppUnitsToFloatPixels(aRect.x, factor),
1323 0 : NSAppUnitsToFloatPixels(aRect.y, factor),
1324 0 : NSAppUnitsToFloatPixels(aRect.width, factor),
1325 0 : NSAppUnitsToFloatPixels(aRect.height, factor));
1326 :
1327 0 : result = TransformGfxRectToAncestor(aFrame, result, aAncestor);
1328 :
1329 : return nsRect(NSFloatPixelsToAppUnits(float(result.x), factor),
1330 : NSFloatPixelsToAppUnits(float(result.y), factor),
1331 : NSFloatPixelsToAppUnits(float(result.width), factor),
1332 0 : NSFloatPixelsToAppUnits(float(result.height), factor));
1333 : }
1334 :
1335 0 : static nsIntPoint GetWidgetOffset(nsIWidget* aWidget, nsIWidget*& aRootWidget) {
1336 0 : nsIntPoint offset(0, 0);
1337 0 : nsIWidget* parent = aWidget->GetParent();
1338 0 : while (parent) {
1339 0 : nsIntRect bounds;
1340 0 : aWidget->GetBounds(bounds);
1341 0 : offset += bounds.TopLeft();
1342 0 : aWidget = parent;
1343 0 : parent = aWidget->GetParent();
1344 : }
1345 0 : aRootWidget = aWidget;
1346 : return offset;
1347 : }
1348 :
1349 : nsPoint
1350 0 : nsLayoutUtils::TranslateWidgetToView(nsPresContext* aPresContext,
1351 : nsIWidget* aWidget, nsIntPoint aPt,
1352 : nsIView* aView)
1353 : {
1354 0 : nsPoint viewOffset;
1355 0 : nsIWidget* viewWidget = aView->GetNearestWidget(&viewOffset);
1356 0 : if (!viewWidget) {
1357 0 : return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
1358 : }
1359 :
1360 : nsIWidget* fromRoot;
1361 0 : nsIntPoint fromOffset = GetWidgetOffset(aWidget, fromRoot);
1362 : nsIWidget* toRoot;
1363 0 : nsIntPoint toOffset = GetWidgetOffset(viewWidget, toRoot);
1364 :
1365 0 : nsIntPoint widgetPoint;
1366 0 : if (fromRoot == toRoot) {
1367 0 : widgetPoint = aPt + fromOffset - toOffset;
1368 : } else {
1369 0 : nsIntPoint screenPoint = aWidget->WidgetToScreenOffset();
1370 0 : widgetPoint = aPt + screenPoint - viewWidget->WidgetToScreenOffset();
1371 : }
1372 :
1373 : nsPoint widgetAppUnits(aPresContext->DevPixelsToAppUnits(widgetPoint.x),
1374 0 : aPresContext->DevPixelsToAppUnits(widgetPoint.y));
1375 0 : return widgetAppUnits - viewOffset;
1376 : }
1377 :
1378 : // Combine aNewBreakType with aOrigBreakType, but limit the break types
1379 : // to NS_STYLE_CLEAR_LEFT, RIGHT, LEFT_AND_RIGHT.
1380 : PRUint8
1381 0 : nsLayoutUtils::CombineBreakType(PRUint8 aOrigBreakType,
1382 : PRUint8 aNewBreakType)
1383 : {
1384 0 : PRUint8 breakType = aOrigBreakType;
1385 0 : switch(breakType) {
1386 : case NS_STYLE_CLEAR_LEFT:
1387 0 : if ((NS_STYLE_CLEAR_RIGHT == aNewBreakType) ||
1388 : (NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) {
1389 0 : breakType = NS_STYLE_CLEAR_LEFT_AND_RIGHT;
1390 : }
1391 0 : break;
1392 : case NS_STYLE_CLEAR_RIGHT:
1393 0 : if ((NS_STYLE_CLEAR_LEFT == aNewBreakType) ||
1394 : (NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) {
1395 0 : breakType = NS_STYLE_CLEAR_LEFT_AND_RIGHT;
1396 : }
1397 0 : break;
1398 : case NS_STYLE_CLEAR_NONE:
1399 0 : if ((NS_STYLE_CLEAR_LEFT == aNewBreakType) ||
1400 : (NS_STYLE_CLEAR_RIGHT == aNewBreakType) ||
1401 : (NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) {
1402 0 : breakType = aNewBreakType;
1403 : }
1404 : }
1405 0 : return breakType;
1406 : }
1407 :
1408 : #ifdef MOZ_DUMP_PAINTING
1409 : #include <stdio.h>
1410 :
1411 : static bool gDumpEventList = false;
1412 : int gPaintCount = 0;
1413 : #endif
1414 :
1415 : nsresult
1416 0 : nsLayoutUtils::GetRemoteContentIds(nsIFrame* aFrame,
1417 : const nsRect& aTarget,
1418 : nsTArray<ViewID> &aOutIDs,
1419 : bool aIgnoreRootScrollFrame)
1420 : {
1421 : nsDisplayListBuilder builder(aFrame, nsDisplayListBuilder::EVENT_DELIVERY,
1422 0 : false);
1423 0 : nsDisplayList list;
1424 :
1425 0 : if (aIgnoreRootScrollFrame) {
1426 : nsIFrame* rootScrollFrame =
1427 0 : aFrame->PresContext()->PresShell()->GetRootScrollFrame();
1428 0 : if (rootScrollFrame) {
1429 0 : builder.SetIgnoreScrollFrame(rootScrollFrame);
1430 : }
1431 : }
1432 :
1433 0 : builder.EnterPresShell(aFrame, aTarget);
1434 :
1435 : nsresult rv =
1436 0 : aFrame->BuildDisplayListForStackingContext(&builder, aTarget, &list);
1437 :
1438 0 : builder.LeavePresShell(aFrame, aTarget);
1439 0 : NS_ENSURE_SUCCESS(rv, rv);
1440 :
1441 0 : nsAutoTArray<nsIFrame*,8> outFrames;
1442 0 : nsDisplayItem::HitTestState hitTestState(&aOutIDs);
1443 0 : list.HitTest(&builder, aTarget, &hitTestState, &outFrames);
1444 0 : list.DeleteAll();
1445 :
1446 0 : return NS_OK;
1447 : }
1448 :
1449 : nsIFrame*
1450 0 : nsLayoutUtils::GetFrameForPoint(nsIFrame* aFrame, nsPoint aPt,
1451 : bool aShouldIgnoreSuppression,
1452 : bool aIgnoreRootScrollFrame)
1453 : {
1454 : nsresult rv;
1455 0 : nsAutoTArray<nsIFrame*,8> outFrames;
1456 0 : rv = GetFramesForArea(aFrame, nsRect(aPt, nsSize(1, 1)), outFrames,
1457 0 : aShouldIgnoreSuppression, aIgnoreRootScrollFrame);
1458 0 : NS_ENSURE_SUCCESS(rv, nsnull);
1459 0 : return outFrames.Length() ? outFrames.ElementAt(0) : nsnull;
1460 : }
1461 :
1462 : nsresult
1463 0 : nsLayoutUtils::GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect,
1464 : nsTArray<nsIFrame*> &aOutFrames,
1465 : bool aShouldIgnoreSuppression,
1466 : bool aIgnoreRootScrollFrame)
1467 : {
1468 : nsDisplayListBuilder builder(aFrame, nsDisplayListBuilder::EVENT_DELIVERY,
1469 0 : false);
1470 0 : nsDisplayList list;
1471 0 : nsRect target(aRect);
1472 :
1473 0 : if (aShouldIgnoreSuppression) {
1474 0 : builder.IgnorePaintSuppression();
1475 : }
1476 :
1477 0 : if (aIgnoreRootScrollFrame) {
1478 : nsIFrame* rootScrollFrame =
1479 0 : aFrame->PresContext()->PresShell()->GetRootScrollFrame();
1480 0 : if (rootScrollFrame) {
1481 0 : builder.SetIgnoreScrollFrame(rootScrollFrame);
1482 : }
1483 : }
1484 :
1485 0 : builder.EnterPresShell(aFrame, target);
1486 :
1487 : nsresult rv =
1488 0 : aFrame->BuildDisplayListForStackingContext(&builder, target, &list);
1489 :
1490 0 : builder.LeavePresShell(aFrame, target);
1491 0 : NS_ENSURE_SUCCESS(rv, rv);
1492 :
1493 : #ifdef MOZ_DUMP_PAINTING
1494 0 : if (gDumpEventList) {
1495 0 : fprintf(stdout, "Event handling --- (%d,%d):\n", aRect.x, aRect.y);
1496 0 : nsFrame::PrintDisplayList(&builder, list);
1497 : }
1498 : #endif
1499 :
1500 0 : nsDisplayItem::HitTestState hitTestState;
1501 0 : list.HitTest(&builder, target, &hitTestState, &aOutFrames);
1502 0 : list.DeleteAll();
1503 0 : return NS_OK;
1504 : }
1505 :
1506 : /**
1507 : * Remove all leaf display items that are not for descendants of
1508 : * aBuilder->GetReferenceFrame() from aList, and move all nsDisplayClip
1509 : * wrappers to their correct locations.
1510 : * @param aExtraPage the page we constructed aList for
1511 : * @param aY the Y-coordinate where aPage would be positioned relative
1512 : * to the main page (aBuilder->GetReferenceFrame()), considering only
1513 : * the content and ignoring page margins and dead space
1514 : * @param aList the list that is modified in-place
1515 : */
1516 : static void
1517 0 : PruneDisplayListForExtraPage(nsDisplayListBuilder* aBuilder,
1518 : nsIFrame* aExtraPage, nscoord aY, nsDisplayList* aList)
1519 : {
1520 0 : nsDisplayList newList;
1521 : // The page which we're really constructing a display list for
1522 0 : nsIFrame* mainPage = aBuilder->ReferenceFrame();
1523 :
1524 0 : while (true) {
1525 0 : nsDisplayItem* i = aList->RemoveBottom();
1526 0 : if (!i)
1527 : break;
1528 0 : nsDisplayList* subList = i->GetList();
1529 0 : if (subList) {
1530 0 : PruneDisplayListForExtraPage(aBuilder, aExtraPage, aY, subList);
1531 0 : nsDisplayItem::Type type = i->GetType();
1532 0 : if (type == nsDisplayItem::TYPE_CLIP ||
1533 : type == nsDisplayItem::TYPE_CLIP_ROUNDED_RECT) {
1534 : // This might clip an element which should appear on the first
1535 : // page, and that element might be visible if this uses a 'clip'
1536 : // property with a negative top.
1537 : // The clip area needs to be moved because the frame geometry doesn't
1538 : // put page content frames for adjacent pages vertically adjacent,
1539 : // there are page margins and dead space between them in print
1540 : // preview, and in printing all pages are at (0,0)...
1541 : // XXX we have no way to test this right now that I know of;
1542 : // the 'clip' property requires an abs-pos element and we never
1543 : // paint abs-pos elements that start after the main page
1544 : // (bug 426909).
1545 0 : nsDisplayClip* clip = static_cast<nsDisplayClip*>(i);
1546 0 : clip->SetClipRect(clip->GetClipRect() + nsPoint(0, aY) -
1547 0 : aExtraPage->GetOffsetTo(mainPage));
1548 : }
1549 0 : newList.AppendToTop(i);
1550 : } else {
1551 0 : nsIFrame* f = i->GetUnderlyingFrame();
1552 0 : if (f && nsLayoutUtils::IsProperAncestorFrameCrossDoc(mainPage, f)) {
1553 : // This one is in the page we care about, keep it
1554 0 : newList.AppendToTop(i);
1555 : } else {
1556 : // We're throwing this away so call its destructor now. The memory
1557 : // is owned by aBuilder which destroys all items at once.
1558 0 : i->~nsDisplayItem();
1559 : }
1560 : }
1561 : }
1562 0 : aList->AppendToTop(&newList);
1563 0 : }
1564 :
1565 : static nsresult
1566 0 : BuildDisplayListForExtraPage(nsDisplayListBuilder* aBuilder,
1567 : nsIFrame* aPage, nscoord aY, nsDisplayList* aList)
1568 : {
1569 0 : nsDisplayList list;
1570 : // Pass an empty dirty rect since we're only interested in finding
1571 : // placeholders whose out-of-flows are in the page
1572 : // aBuilder->GetReferenceFrame(), and the paths to those placeholders
1573 : // have already been marked as NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO.
1574 : // Note that we should still do a prune step since we don't want to
1575 : // rely on dirty-rect checking for correctness.
1576 0 : nsresult rv = aPage->BuildDisplayListForStackingContext(aBuilder, nsRect(), &list);
1577 0 : if (NS_FAILED(rv))
1578 0 : return rv;
1579 0 : PruneDisplayListForExtraPage(aBuilder, aPage, aY, &list);
1580 0 : aList->AppendToTop(&list);
1581 0 : return NS_OK;
1582 : }
1583 :
1584 : static nsIFrame*
1585 0 : GetNextPage(nsIFrame* aPageContentFrame)
1586 : {
1587 : // XXX ugh
1588 0 : nsIFrame* pageFrame = aPageContentFrame->GetParent();
1589 0 : NS_ASSERTION(pageFrame->GetType() == nsGkAtoms::pageFrame,
1590 : "pageContentFrame has unexpected parent");
1591 0 : nsIFrame* nextPageFrame = pageFrame->GetNextSibling();
1592 0 : if (!nextPageFrame)
1593 0 : return nsnull;
1594 0 : NS_ASSERTION(nextPageFrame->GetType() == nsGkAtoms::pageFrame,
1595 : "pageFrame's sibling is not a page frame...");
1596 0 : nsIFrame* f = nextPageFrame->GetFirstPrincipalChild();
1597 0 : NS_ASSERTION(f, "pageFrame has no page content frame!");
1598 0 : NS_ASSERTION(f->GetType() == nsGkAtoms::pageContentFrame,
1599 : "pageFrame's child is not page content!");
1600 0 : return f;
1601 : }
1602 :
1603 : nsresult
1604 0 : nsLayoutUtils::PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFrame,
1605 : const nsRegion& aDirtyRegion, nscolor aBackstop,
1606 : PRUint32 aFlags)
1607 : {
1608 0 : if (aFlags & PAINT_WIDGET_LAYERS) {
1609 0 : nsIView* view = aFrame->GetView();
1610 0 : if (!(view && view->GetWidget() && GetDisplayRootFrame(aFrame) == aFrame)) {
1611 0 : aFlags &= ~PAINT_WIDGET_LAYERS;
1612 0 : NS_ASSERTION(aRenderingContext, "need a rendering context");
1613 : }
1614 : }
1615 :
1616 0 : nsPresContext* presContext = aFrame->PresContext();
1617 0 : nsIPresShell* presShell = presContext->PresShell();
1618 :
1619 0 : nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
1620 0 : bool usingDisplayPort = false;
1621 0 : nsRect displayport;
1622 0 : if (rootScrollFrame) {
1623 0 : nsIContent* content = rootScrollFrame->GetContent();
1624 0 : if (content) {
1625 0 : usingDisplayPort = nsLayoutUtils::GetDisplayPort(content, &displayport);
1626 : }
1627 : }
1628 :
1629 0 : bool ignoreViewportScrolling = presShell->IgnoringViewportScrolling();
1630 0 : nsRegion visibleRegion;
1631 0 : if (aFlags & PAINT_WIDGET_LAYERS) {
1632 : // This layer tree will be reused, so we'll need to calculate it
1633 : // for the whole "visible" area of the window
1634 : //
1635 : // |ignoreViewportScrolling| and |usingDisplayPort| are persistent
1636 : // document-rendering state. We rely on PresShell to flush
1637 : // retained layers as needed when that persistent state changes.
1638 0 : if (!usingDisplayPort) {
1639 0 : visibleRegion = aFrame->GetVisualOverflowRectRelativeToSelf();
1640 : } else {
1641 0 : visibleRegion = displayport;
1642 : }
1643 : } else {
1644 0 : visibleRegion = aDirtyRegion;
1645 : }
1646 :
1647 : // If we're going to display something different from what we'd normally
1648 : // paint in a window then we will flush out any retained layer trees before
1649 : // *and after* we draw.
1650 0 : bool willFlushRetainedLayers = (aFlags & PAINT_HIDE_CARET) != 0;
1651 :
1652 : nsDisplayListBuilder builder(aFrame, nsDisplayListBuilder::PAINTING,
1653 0 : !(aFlags & PAINT_HIDE_CARET));
1654 0 : if (usingDisplayPort) {
1655 0 : builder.SetDisplayPort(displayport);
1656 : }
1657 :
1658 0 : nsDisplayList list;
1659 0 : if (aFlags & PAINT_IN_TRANSFORM) {
1660 0 : builder.SetInTransform(true);
1661 : }
1662 0 : if (aFlags & PAINT_SYNC_DECODE_IMAGES) {
1663 0 : builder.SetSyncDecodeImages(true);
1664 : }
1665 0 : if (aFlags & (PAINT_WIDGET_LAYERS | PAINT_TO_WINDOW)) {
1666 0 : builder.SetPaintingToWindow(true);
1667 : }
1668 0 : if (aFlags & PAINT_IGNORE_SUPPRESSION) {
1669 0 : builder.IgnorePaintSuppression();
1670 : }
1671 0 : if (aRenderingContext &&
1672 0 : aRenderingContext->ThebesContext()->GetFlags() & gfxContext::FLAG_DISABLE_SNAPPING) {
1673 0 : builder.SetSnappingEnabled(false);
1674 : }
1675 0 : nsRect canvasArea(nsPoint(0, 0), aFrame->GetSize());
1676 :
1677 : #ifdef DEBUG
1678 0 : if (ignoreViewportScrolling) {
1679 0 : nsIDocument* doc = aFrame->GetContent() ?
1680 0 : aFrame->GetContent()->GetCurrentDoc() : nsnull;
1681 0 : NS_ASSERTION(!aFrame->GetParent() ||
1682 : (doc && doc->IsBeingUsedAsImage()),
1683 : "Only expecting ignoreViewportScrolling for root frames and "
1684 : "for image documents.");
1685 : }
1686 : #endif
1687 :
1688 0 : if (ignoreViewportScrolling && !aFrame->GetParent()) {
1689 0 : nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
1690 0 : if (rootScrollFrame) {
1691 : nsIScrollableFrame* rootScrollableFrame =
1692 0 : presShell->GetRootScrollFrameAsScrollable();
1693 0 : if (aFlags & PAINT_DOCUMENT_RELATIVE) {
1694 : // Make visibleRegion and aRenderingContext relative to the
1695 : // scrolled frame instead of the root frame.
1696 0 : nsPoint pos = rootScrollableFrame->GetScrollPosition();
1697 0 : visibleRegion.MoveBy(-pos);
1698 0 : if (aRenderingContext) {
1699 0 : aRenderingContext->Translate(pos);
1700 : }
1701 : }
1702 0 : builder.SetIgnoreScrollFrame(rootScrollFrame);
1703 :
1704 : nsCanvasFrame* canvasFrame =
1705 0 : do_QueryFrame(rootScrollableFrame->GetScrolledFrame());
1706 0 : if (canvasFrame) {
1707 : // Use UnionRect here to ensure that areas where the scrollbars
1708 : // were are still filled with the background color.
1709 : canvasArea.UnionRect(canvasArea,
1710 0 : canvasFrame->CanvasArea() + builder.ToReferenceFrame(canvasFrame));
1711 : }
1712 : }
1713 : }
1714 : nsresult rv;
1715 :
1716 0 : nsRect dirtyRect = visibleRegion.GetBounds();
1717 0 : builder.EnterPresShell(aFrame, dirtyRect);
1718 :
1719 0 : rv = aFrame->BuildDisplayListForStackingContext(&builder, dirtyRect, &list);
1720 :
1721 0 : const bool paintAllContinuations = aFlags & PAINT_ALL_CONTINUATIONS;
1722 0 : NS_ASSERTION(!paintAllContinuations || !aFrame->GetPrevContinuation(),
1723 : "If painting all continuations, the frame must be "
1724 : "first-continuation");
1725 :
1726 0 : nsIAtom* frameType = aFrame->GetType();
1727 0 : if (NS_SUCCEEDED(rv) && !paintAllContinuations &&
1728 : frameType == nsGkAtoms::pageContentFrame) {
1729 0 : NS_ASSERTION(!(aFlags & PAINT_WIDGET_LAYERS),
1730 : "shouldn't be painting with widget layers for page content frames");
1731 : // We may need to paint out-of-flow frames whose placeholders are
1732 : // on other pages. Add those pages to our display list. Note that
1733 : // out-of-flow frames can't be placed after their placeholders so
1734 : // we don't have to process earlier pages. The display lists for
1735 : // these extra pages are pruned so that only display items for the
1736 : // page we currently care about (which we would have reached by
1737 : // following placeholders to their out-of-flows) end up on the list.
1738 0 : nsIFrame* page = aFrame;
1739 0 : nscoord y = aFrame->GetSize().height;
1740 0 : while ((page = GetNextPage(page)) != nsnull) {
1741 0 : rv = BuildDisplayListForExtraPage(&builder, page, y, &list);
1742 0 : if (NS_FAILED(rv))
1743 0 : break;
1744 0 : y += page->GetSize().height;
1745 : }
1746 : }
1747 :
1748 0 : if (paintAllContinuations) {
1749 0 : nsIFrame* currentFrame = aFrame;
1750 0 : while (NS_SUCCEEDED(rv) &&
1751 0 : (currentFrame = currentFrame->GetNextContinuation()) != nsnull) {
1752 0 : nsRect frameDirty = dirtyRect - builder.ToReferenceFrame(currentFrame);
1753 : rv = currentFrame->BuildDisplayListForStackingContext(&builder,
1754 0 : frameDirty, &list);
1755 : }
1756 : }
1757 :
1758 : // For the viewport frame in print preview/page layout we want to paint
1759 : // the grey background behind the page, not the canvas color.
1760 0 : if (frameType == nsGkAtoms::viewportFrame &&
1761 0 : nsLayoutUtils::NeedsPrintPreviewBackground(presContext)) {
1762 : nsRect bounds = nsRect(builder.ToReferenceFrame(aFrame),
1763 0 : aFrame->GetSize());
1764 0 : rv = presShell->AddPrintPreviewBackgroundItem(builder, list, aFrame, bounds);
1765 0 : } else if (frameType != nsGkAtoms::pageFrame) {
1766 : // For printing, this function is first called on an nsPageFrame, which
1767 : // creates a display list with a PageContent item. The PageContent item's
1768 : // paint function calls this function on the nsPageFrame's child which is
1769 : // an nsPageContentFrame. We only want to add the canvas background color
1770 : // item once, for the nsPageContentFrame.
1771 :
1772 : // Add the canvas background color to the bottom of the list. This
1773 : // happens after we've built the list so that AddCanvasBackgroundColorItem
1774 : // can monkey with the contents if necessary.
1775 0 : canvasArea.IntersectRect(canvasArea, visibleRegion.GetBounds());
1776 : rv = presShell->AddCanvasBackgroundColorItem(
1777 0 : builder, list, aFrame, canvasArea, aBackstop);
1778 :
1779 : // If the passed in backstop color makes us draw something different from
1780 : // normal, we need to flush layers.
1781 0 : if ((aFlags & PAINT_WIDGET_LAYERS) && !willFlushRetainedLayers) {
1782 0 : nsIView* view = aFrame->GetView();
1783 0 : if (view) {
1784 0 : nscolor backstop = presShell->ComputeBackstopColor(view);
1785 : // The PresShell's canvas background color doesn't get updated until
1786 : // EnterPresShell, so this check has to be done after that.
1787 0 : nscolor canvasColor = presShell->GetCanvasBackground();
1788 0 : if (NS_ComposeColors(aBackstop, canvasColor) !=
1789 0 : NS_ComposeColors(backstop, canvasColor)) {
1790 0 : willFlushRetainedLayers = true;
1791 : }
1792 : }
1793 : }
1794 : }
1795 :
1796 0 : builder.LeavePresShell(aFrame, dirtyRect);
1797 0 : NS_ENSURE_SUCCESS(rv, rv);
1798 :
1799 0 : if (builder.GetHadToIgnorePaintSuppression()) {
1800 0 : willFlushRetainedLayers = true;
1801 : }
1802 :
1803 : #ifdef MOZ_DUMP_PAINTING
1804 0 : if (gfxUtils::sDumpPainting) {
1805 0 : if (gfxUtils::sDumpPaintingToFile) {
1806 0 : nsCString string("dump-");
1807 0 : string.AppendInt(gPaintCount);
1808 0 : string.Append(".html");
1809 0 : gfxUtils::sDumpPaintFile = fopen(string.BeginReading(), "w");
1810 : } else {
1811 0 : gfxUtils::sDumpPaintFile = stdout;
1812 : }
1813 0 : fprintf(gfxUtils::sDumpPaintFile, "<html><head><script>var array = {}; function ViewImage(index) { window.location = array[index]; }</script></head><body>");
1814 : fprintf(gfxUtils::sDumpPaintFile, "Painting --- before optimization (dirty %d,%d,%d,%d):\n",
1815 0 : dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
1816 0 : nsFrame::PrintDisplayList(&builder, list, gfxUtils::sDumpPaintFile);
1817 0 : if (gfxUtils::sDumpPaintingToFile) {
1818 0 : fprintf(gfxUtils::sDumpPaintFile, "<script>");
1819 : }
1820 : }
1821 : #endif
1822 :
1823 0 : list.ComputeVisibilityForRoot(&builder, &visibleRegion);
1824 :
1825 0 : PRUint32 flags = nsDisplayList::PAINT_DEFAULT;
1826 0 : if (aFlags & PAINT_WIDGET_LAYERS) {
1827 0 : flags |= nsDisplayList::PAINT_USE_WIDGET_LAYERS;
1828 0 : if (willFlushRetainedLayers) {
1829 : // The caller wanted to paint from retained layers, but set up
1830 : // the paint in such a way that we can't use them. We're going
1831 : // to display something different from what we'd normally paint
1832 : // in a window, so make sure we flush out any retained layer
1833 : // trees before *and after* we draw. Callers should be fixed to
1834 : // not do this.
1835 0 : NS_WARNING("Flushing retained layers!");
1836 0 : flags |= nsDisplayList::PAINT_FLUSH_LAYERS;
1837 0 : } else if (!(aFlags & PAINT_DOCUMENT_RELATIVE)) {
1838 0 : nsIWidget *widget = aFrame->GetNearestWidget();
1839 0 : if (widget) {
1840 0 : builder.SetFinalTransparentRegion(visibleRegion);
1841 : // If we're finished building display list items for painting of the outermost
1842 : // pres shell, notify the widget about any toolbars we've encountered.
1843 0 : widget->UpdateThemeGeometries(builder.GetThemeGeometries());
1844 : }
1845 : }
1846 : }
1847 0 : if (aFlags & PAINT_EXISTING_TRANSACTION) {
1848 0 : flags |= nsDisplayList::PAINT_EXISTING_TRANSACTION;
1849 : }
1850 :
1851 0 : list.PaintRoot(&builder, aRenderingContext, flags);
1852 :
1853 : // Update the widget's opaque region information. This sets
1854 : // glass boundaries on Windows.
1855 0 : if ((aFlags & PAINT_WIDGET_LAYERS) &&
1856 0 : !willFlushRetainedLayers &&
1857 0 : !(aFlags & PAINT_DOCUMENT_RELATIVE)) {
1858 0 : nsIWidget *widget = aFrame->GetNearestWidget();
1859 0 : if (widget) {
1860 0 : nsRegion excludedRegion = builder.GetExcludedGlassRegion();
1861 0 : excludedRegion.Sub(excludedRegion, visibleRegion);
1862 0 : nsIntRegion windowRegion(excludedRegion.ToNearestPixels(presContext->AppUnitsPerDevPixel()));
1863 0 : widget->UpdateOpaqueRegion(windowRegion);
1864 : }
1865 : }
1866 :
1867 : #ifdef MOZ_DUMP_PAINTING
1868 0 : if (gfxUtils::sDumpPainting) {
1869 0 : fprintf(gfxUtils::sDumpPaintFile, "</script>Painting --- after optimization:\n");
1870 0 : nsFrame::PrintDisplayList(&builder, list, gfxUtils::sDumpPaintFile);
1871 :
1872 0 : fprintf(gfxUtils::sDumpPaintFile, "Painting --- retained layer tree:\n");
1873 0 : builder.LayerBuilder()->DumpRetainedLayerTree(gfxUtils::sDumpPaintFile);
1874 0 : fprintf(gfxUtils::sDumpPaintFile, "</body></html>");
1875 :
1876 0 : if (gfxUtils::sDumpPaintingToFile) {
1877 0 : fclose(gfxUtils::sDumpPaintFile);
1878 : }
1879 0 : gfxUtils::sDumpPaintFile = NULL;
1880 0 : gPaintCount++;
1881 : }
1882 : #endif
1883 :
1884 : // Flush the list so we don't trigger the IsEmpty-on-destruction assertion
1885 0 : list.DeleteAll();
1886 0 : return NS_OK;
1887 : }
1888 :
1889 : PRInt32
1890 0 : nsLayoutUtils::GetZIndex(nsIFrame* aFrame) {
1891 0 : if (!aFrame->GetStyleDisplay()->IsPositioned())
1892 0 : return 0;
1893 :
1894 : const nsStylePosition* position =
1895 0 : aFrame->GetStylePosition();
1896 0 : if (position->mZIndex.GetUnit() == eStyleUnit_Integer)
1897 0 : return position->mZIndex.GetIntValue();
1898 :
1899 : // sort the auto and 0 elements together
1900 0 : return 0;
1901 : }
1902 :
1903 : /**
1904 : * Uses a binary search for find where the cursor falls in the line of text
1905 : * It also keeps track of the part of the string that has already been measured
1906 : * so it doesn't have to keep measuring the same text over and over
1907 : *
1908 : * @param "aBaseWidth" contains the width in twips of the portion
1909 : * of the text that has already been measured, and aBaseInx contains
1910 : * the index of the text that has already been measured.
1911 : *
1912 : * @param aTextWidth returns the (in twips) the length of the text that falls
1913 : * before the cursor aIndex contains the index of the text where the cursor falls
1914 : */
1915 : bool
1916 0 : nsLayoutUtils::BinarySearchForPosition(nsRenderingContext* aRendContext,
1917 : const PRUnichar* aText,
1918 : PRInt32 aBaseWidth,
1919 : PRInt32 aBaseInx,
1920 : PRInt32 aStartInx,
1921 : PRInt32 aEndInx,
1922 : PRInt32 aCursorPos,
1923 : PRInt32& aIndex,
1924 : PRInt32& aTextWidth)
1925 : {
1926 0 : PRInt32 range = aEndInx - aStartInx;
1927 0 : if ((range == 1) || (range == 2 && NS_IS_HIGH_SURROGATE(aText[aStartInx]))) {
1928 0 : aIndex = aStartInx + aBaseInx;
1929 0 : aTextWidth = aRendContext->GetWidth(aText, aIndex);
1930 0 : return true;
1931 : }
1932 :
1933 0 : PRInt32 inx = aStartInx + (range / 2);
1934 :
1935 : // Make sure we don't leave a dangling low surrogate
1936 0 : if (NS_IS_HIGH_SURROGATE(aText[inx-1]))
1937 0 : inx++;
1938 :
1939 0 : PRInt32 textWidth = aRendContext->GetWidth(aText, inx);
1940 :
1941 0 : PRInt32 fullWidth = aBaseWidth + textWidth;
1942 0 : if (fullWidth == aCursorPos) {
1943 0 : aTextWidth = textWidth;
1944 0 : aIndex = inx;
1945 0 : return true;
1946 0 : } else if (aCursorPos < fullWidth) {
1947 0 : aTextWidth = aBaseWidth;
1948 0 : if (BinarySearchForPosition(aRendContext, aText, aBaseWidth, aBaseInx, aStartInx, inx, aCursorPos, aIndex, aTextWidth)) {
1949 0 : return true;
1950 : }
1951 : } else {
1952 0 : aTextWidth = fullWidth;
1953 0 : if (BinarySearchForPosition(aRendContext, aText, aBaseWidth, aBaseInx, inx, aEndInx, aCursorPos, aIndex, aTextWidth)) {
1954 0 : return true;
1955 : }
1956 : }
1957 0 : return false;
1958 : }
1959 :
1960 : static void
1961 0 : AddBoxesForFrame(nsIFrame* aFrame,
1962 : nsLayoutUtils::BoxCallback* aCallback)
1963 : {
1964 0 : nsIAtom* pseudoType = aFrame->GetStyleContext()->GetPseudo();
1965 :
1966 0 : if (pseudoType == nsCSSAnonBoxes::tableOuter) {
1967 0 : AddBoxesForFrame(aFrame->GetFirstPrincipalChild(), aCallback);
1968 0 : nsIFrame* kid = aFrame->GetFirstChild(nsIFrame::kCaptionList);
1969 0 : if (kid) {
1970 0 : AddBoxesForFrame(kid, aCallback);
1971 : }
1972 0 : } else if (pseudoType == nsCSSAnonBoxes::mozAnonymousBlock ||
1973 : pseudoType == nsCSSAnonBoxes::mozAnonymousPositionedBlock ||
1974 : pseudoType == nsCSSAnonBoxes::mozMathMLAnonymousBlock ||
1975 : pseudoType == nsCSSAnonBoxes::mozXULAnonymousBlock) {
1976 0 : for (nsIFrame* kid = aFrame->GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) {
1977 0 : AddBoxesForFrame(kid, aCallback);
1978 0 : }
1979 : } else {
1980 0 : aCallback->AddBox(aFrame);
1981 : }
1982 0 : }
1983 :
1984 : void
1985 0 : nsLayoutUtils::GetAllInFlowBoxes(nsIFrame* aFrame, BoxCallback* aCallback)
1986 : {
1987 0 : while (aFrame) {
1988 0 : AddBoxesForFrame(aFrame, aCallback);
1989 0 : aFrame = nsLayoutUtils::GetNextContinuationOrSpecialSibling(aFrame);
1990 : }
1991 0 : }
1992 :
1993 : struct BoxToBorderRect : public nsLayoutUtils::BoxCallback {
1994 : nsIFrame* mRelativeTo;
1995 : nsLayoutUtils::RectCallback* mCallback;
1996 : PRUint32 mFlags;
1997 :
1998 0 : BoxToBorderRect(nsIFrame* aRelativeTo, nsLayoutUtils::RectCallback* aCallback,
1999 : PRUint32 aFlags)
2000 0 : : mRelativeTo(aRelativeTo), mCallback(aCallback), mFlags(aFlags) {}
2001 :
2002 0 : virtual void AddBox(nsIFrame* aFrame) {
2003 0 : nsRect r;
2004 0 : nsIFrame* outer = nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(aFrame, &r);
2005 0 : if (!outer) {
2006 0 : outer = aFrame;
2007 0 : r = nsRect(nsPoint(0, 0), aFrame->GetSize());
2008 : }
2009 0 : if (mFlags & nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS) {
2010 0 : r = nsLayoutUtils::TransformFrameRectToAncestor(outer, r, mRelativeTo);
2011 : } else {
2012 0 : r += outer->GetOffsetTo(mRelativeTo);
2013 : }
2014 0 : mCallback->AddRect(r);
2015 0 : }
2016 : };
2017 :
2018 : void
2019 0 : nsLayoutUtils::GetAllInFlowRects(nsIFrame* aFrame, nsIFrame* aRelativeTo,
2020 : RectCallback* aCallback, PRUint32 aFlags)
2021 : {
2022 0 : BoxToBorderRect converter(aRelativeTo, aCallback, aFlags);
2023 0 : GetAllInFlowBoxes(aFrame, &converter);
2024 0 : }
2025 :
2026 0 : nsLayoutUtils::RectAccumulator::RectAccumulator() : mSeenFirstRect(false) {}
2027 :
2028 0 : void nsLayoutUtils::RectAccumulator::AddRect(const nsRect& aRect) {
2029 0 : mResultRect.UnionRect(mResultRect, aRect);
2030 0 : if (!mSeenFirstRect) {
2031 0 : mSeenFirstRect = true;
2032 0 : mFirstRect = aRect;
2033 : }
2034 0 : }
2035 :
2036 0 : nsLayoutUtils::RectListBuilder::RectListBuilder(nsClientRectList* aList)
2037 0 : : mRectList(aList), mRV(NS_OK) {}
2038 :
2039 0 : void nsLayoutUtils::RectListBuilder::AddRect(const nsRect& aRect) {
2040 0 : nsRefPtr<nsClientRect> rect = new nsClientRect();
2041 :
2042 0 : rect->SetLayoutRect(aRect);
2043 0 : mRectList->Append(rect);
2044 0 : }
2045 :
2046 0 : nsIFrame* nsLayoutUtils::GetContainingBlockForClientRect(nsIFrame* aFrame)
2047 : {
2048 0 : return aFrame->PresContext()->PresShell()->GetRootFrame();
2049 : }
2050 :
2051 : nsRect
2052 0 : nsLayoutUtils::GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo,
2053 : PRUint32 aFlags) {
2054 0 : RectAccumulator accumulator;
2055 0 : GetAllInFlowRects(aFrame, aRelativeTo, &accumulator, aFlags);
2056 0 : return accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect
2057 0 : : accumulator.mResultRect;
2058 : }
2059 :
2060 : nsRect
2061 0 : nsLayoutUtils::GetTextShadowRectsUnion(const nsRect& aTextAndDecorationsRect,
2062 : nsIFrame* aFrame,
2063 : PRUint32 aFlags)
2064 : {
2065 0 : const nsStyleText* textStyle = aFrame->GetStyleText();
2066 0 : if (!textStyle->mTextShadow)
2067 0 : return aTextAndDecorationsRect;
2068 :
2069 0 : nsRect resultRect = aTextAndDecorationsRect;
2070 0 : PRInt32 A2D = aFrame->PresContext()->AppUnitsPerDevPixel();
2071 0 : for (PRUint32 i = 0; i < textStyle->mTextShadow->Length(); ++i) {
2072 0 : nsCSSShadowItem* shadow = textStyle->mTextShadow->ShadowAt(i);
2073 0 : nsMargin blur = nsContextBoxBlur::GetBlurRadiusMargin(shadow->mRadius, A2D);
2074 0 : if ((aFlags & EXCLUDE_BLUR_SHADOWS) && blur != nsMargin(0, 0, 0, 0))
2075 0 : continue;
2076 :
2077 0 : nsRect tmpRect(aTextAndDecorationsRect);
2078 :
2079 0 : tmpRect.MoveBy(nsPoint(shadow->mXOffset, shadow->mYOffset));
2080 0 : tmpRect.Inflate(blur);
2081 :
2082 0 : resultRect.UnionRect(resultRect, tmpRect);
2083 : }
2084 0 : return resultRect;
2085 : }
2086 :
2087 : nsresult
2088 0 : nsLayoutUtils::GetFontMetricsForFrame(const nsIFrame* aFrame,
2089 : nsFontMetrics** aFontMetrics,
2090 : float aInflation)
2091 : {
2092 : return nsLayoutUtils::GetFontMetricsForStyleContext(aFrame->GetStyleContext(),
2093 : aFontMetrics,
2094 0 : aInflation);
2095 : }
2096 :
2097 : nsresult
2098 0 : nsLayoutUtils::GetFontMetricsForStyleContext(nsStyleContext* aStyleContext,
2099 : nsFontMetrics** aFontMetrics,
2100 : float aInflation)
2101 : {
2102 : // pass the user font set object into the device context to pass along to CreateFontGroup
2103 0 : gfxUserFontSet* fs = aStyleContext->PresContext()->GetUserFontSet();
2104 :
2105 0 : nsFont font = aStyleContext->GetStyleFont()->mFont;
2106 : // We need to not run font.size through floats when it's large since
2107 : // doing so would be lossy. Fortunately, in such cases, aInflation is
2108 : // guaranteed to be 1.0f.
2109 0 : if (aInflation != 1.0f) {
2110 0 : font.size = NSToCoordRound(font.size * aInflation);
2111 : }
2112 : return aStyleContext->PresContext()->DeviceContext()->GetMetricsFor(
2113 0 : font, aStyleContext->GetStyleFont()->mLanguage,
2114 0 : fs, *aFontMetrics);
2115 : }
2116 :
2117 : nsIFrame*
2118 0 : nsLayoutUtils::FindChildContainingDescendant(nsIFrame* aParent, nsIFrame* aDescendantFrame)
2119 : {
2120 0 : nsIFrame* result = aDescendantFrame;
2121 :
2122 0 : while (result) {
2123 0 : nsIFrame* parent = result->GetParent();
2124 0 : if (parent == aParent) {
2125 0 : break;
2126 : }
2127 :
2128 : // The frame is not an immediate child of aParent so walk up another level
2129 0 : result = parent;
2130 : }
2131 :
2132 0 : return result;
2133 : }
2134 :
2135 : nsBlockFrame*
2136 0 : nsLayoutUtils::GetAsBlock(nsIFrame* aFrame)
2137 : {
2138 0 : nsBlockFrame* block = do_QueryFrame(aFrame);
2139 0 : return block;
2140 : }
2141 :
2142 : nsBlockFrame*
2143 0 : nsLayoutUtils::FindNearestBlockAncestor(nsIFrame* aFrame)
2144 : {
2145 : nsIFrame* nextAncestor;
2146 0 : for (nextAncestor = aFrame->GetParent(); nextAncestor;
2147 : nextAncestor = nextAncestor->GetParent()) {
2148 0 : nsBlockFrame* block = GetAsBlock(nextAncestor);
2149 0 : if (block)
2150 0 : return block;
2151 : }
2152 0 : return nsnull;
2153 : }
2154 :
2155 : nsIFrame*
2156 0 : nsLayoutUtils::GetNonGeneratedAncestor(nsIFrame* aFrame)
2157 : {
2158 0 : if (!(aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT))
2159 0 : return aFrame;
2160 :
2161 0 : nsFrameManager* frameManager = aFrame->PresContext()->FrameManager();
2162 0 : nsIFrame* f = aFrame;
2163 0 : do {
2164 0 : f = GetParentOrPlaceholderFor(frameManager, f);
2165 0 : } while (f->GetStateBits() & NS_FRAME_GENERATED_CONTENT);
2166 0 : return f;
2167 : }
2168 :
2169 : nsIFrame*
2170 0 : nsLayoutUtils::GetParentOrPlaceholderFor(nsFrameManager* aFrameManager,
2171 : nsIFrame* aFrame)
2172 : {
2173 0 : if ((aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
2174 0 : && !aFrame->GetPrevInFlow()) {
2175 0 : return aFrameManager->GetPlaceholderFrameFor(aFrame);
2176 : }
2177 0 : return aFrame->GetParent();
2178 : }
2179 :
2180 : nsIFrame*
2181 0 : nsLayoutUtils::GetNextContinuationOrSpecialSibling(nsIFrame *aFrame)
2182 : {
2183 0 : nsIFrame *result = aFrame->GetNextContinuation();
2184 0 : if (result)
2185 0 : return result;
2186 :
2187 0 : if ((aFrame->GetStateBits() & NS_FRAME_IS_SPECIAL) != 0) {
2188 : // We only store the "special sibling" annotation with the first
2189 : // frame in the continuation chain. Walk back to find that frame now.
2190 0 : aFrame = aFrame->GetFirstContinuation();
2191 :
2192 0 : void* value = aFrame->Properties().Get(nsIFrame::IBSplitSpecialSibling());
2193 0 : return static_cast<nsIFrame*>(value);
2194 : }
2195 :
2196 0 : return nsnull;
2197 : }
2198 :
2199 : nsIFrame*
2200 0 : nsLayoutUtils::GetFirstContinuationOrSpecialSibling(nsIFrame *aFrame)
2201 : {
2202 0 : nsIFrame *result = aFrame->GetFirstContinuation();
2203 0 : if (result->GetStateBits() & NS_FRAME_IS_SPECIAL) {
2204 0 : while (true) {
2205 : nsIFrame *f = static_cast<nsIFrame*>
2206 0 : (result->Properties().Get(nsIFrame::IBSplitSpecialPrevSibling()));
2207 0 : if (!f)
2208 : break;
2209 0 : result = f;
2210 : }
2211 : }
2212 :
2213 0 : return result;
2214 : }
2215 :
2216 : bool
2217 0 : nsLayoutUtils::IsViewportScrollbarFrame(nsIFrame* aFrame)
2218 : {
2219 0 : if (!aFrame)
2220 0 : return false;
2221 :
2222 : nsIFrame* rootScrollFrame =
2223 0 : aFrame->PresContext()->PresShell()->GetRootScrollFrame();
2224 0 : if (!rootScrollFrame)
2225 0 : return false;
2226 :
2227 0 : nsIScrollableFrame* rootScrollableFrame = do_QueryFrame(rootScrollFrame);
2228 0 : NS_ASSERTION(rootScrollableFrame, "The root scorollable frame is null");
2229 :
2230 0 : if (!IsProperAncestorFrame(rootScrollFrame, aFrame))
2231 0 : return false;
2232 :
2233 0 : nsIFrame* rootScrolledFrame = rootScrollableFrame->GetScrolledFrame();
2234 : return !(rootScrolledFrame == aFrame ||
2235 0 : IsProperAncestorFrame(rootScrolledFrame, aFrame));
2236 : }
2237 :
2238 0 : static nscoord AddPercents(nsLayoutUtils::IntrinsicWidthType aType,
2239 : nscoord aCurrent, float aPercent)
2240 : {
2241 0 : nscoord result = aCurrent;
2242 0 : if (aPercent > 0.0f && aType == nsLayoutUtils::PREF_WIDTH) {
2243 : // XXX Should we also consider percentages for min widths, up to a
2244 : // limit?
2245 0 : if (aPercent >= 1.0f)
2246 0 : result = nscoord_MAX;
2247 : else
2248 0 : result = NSToCoordRound(float(result) / (1.0f - aPercent));
2249 : }
2250 0 : return result;
2251 : }
2252 :
2253 : // Use only for widths/heights (or their min/max), since it clamps
2254 : // negative calc() results to 0.
2255 0 : static bool GetAbsoluteCoord(const nsStyleCoord& aStyle, nscoord& aResult)
2256 : {
2257 0 : if (aStyle.IsCalcUnit()) {
2258 0 : if (aStyle.CalcHasPercent()) {
2259 0 : return false;
2260 : }
2261 : // If it has no percents, we can pass 0 for the percentage basis.
2262 0 : aResult = nsRuleNode::ComputeComputedCalc(aStyle, 0);
2263 0 : if (aResult < 0)
2264 0 : aResult = 0;
2265 0 : return true;
2266 : }
2267 :
2268 0 : if (eStyleUnit_Coord != aStyle.GetUnit())
2269 0 : return false;
2270 :
2271 0 : aResult = aStyle.GetCoordValue();
2272 0 : NS_ASSERTION(aResult >= 0, "negative widths not allowed");
2273 0 : return true;
2274 : }
2275 :
2276 : static bool
2277 0 : GetPercentHeight(const nsStyleCoord& aStyle,
2278 : nsIFrame* aFrame,
2279 : nscoord& aResult)
2280 : {
2281 0 : if (eStyleUnit_Percent != aStyle.GetUnit())
2282 0 : return false;
2283 :
2284 0 : nsIFrame *f = aFrame->GetContainingBlock();
2285 0 : if (!f) {
2286 0 : NS_NOTREACHED("top of frame tree not a containing block");
2287 0 : return false;
2288 : }
2289 :
2290 0 : const nsStylePosition *pos = f->GetStylePosition();
2291 : nscoord h;
2292 0 : if (!GetAbsoluteCoord(pos->mHeight, h) &&
2293 0 : !GetPercentHeight(pos->mHeight, f, h)) {
2294 0 : NS_ASSERTION(pos->mHeight.GetUnit() == eStyleUnit_Auto ||
2295 : pos->mHeight.HasPercent(),
2296 : "unknown height unit");
2297 0 : nsIAtom* fType = f->GetType();
2298 0 : if (fType != nsGkAtoms::viewportFrame && fType != nsGkAtoms::canvasFrame &&
2299 : fType != nsGkAtoms::pageContentFrame) {
2300 : // There's no basis for the percentage height, so it acts like auto.
2301 : // Should we consider a max-height < min-height pair a basis for
2302 : // percentage heights? The spec is somewhat unclear, and not doing
2303 : // so is simpler and avoids troubling discontinuities in behavior,
2304 : // so I'll choose not to. -LDB
2305 0 : return false;
2306 : }
2307 :
2308 0 : NS_ASSERTION(pos->mHeight.GetUnit() == eStyleUnit_Auto,
2309 : "Unexpected height unit for viewport or canvas or page-content");
2310 : // For the viewport, canvas, and page-content kids, the percentage
2311 : // basis is just the parent height.
2312 0 : h = f->GetSize().height;
2313 0 : if (h == NS_UNCONSTRAINEDSIZE) {
2314 : // We don't have a percentage basis after all
2315 0 : return false;
2316 : }
2317 : }
2318 :
2319 : nscoord maxh;
2320 0 : if (GetAbsoluteCoord(pos->mMaxHeight, maxh) ||
2321 0 : GetPercentHeight(pos->mMaxHeight, f, maxh)) {
2322 0 : if (maxh < h)
2323 0 : h = maxh;
2324 : } else {
2325 0 : NS_ASSERTION(pos->mMaxHeight.GetUnit() == eStyleUnit_None ||
2326 : pos->mMaxHeight.HasPercent(),
2327 : "unknown max-height unit");
2328 : }
2329 :
2330 : nscoord minh;
2331 0 : if (GetAbsoluteCoord(pos->mMinHeight, minh) ||
2332 0 : GetPercentHeight(pos->mMinHeight, f, minh)) {
2333 0 : if (minh > h)
2334 0 : h = minh;
2335 : } else {
2336 0 : NS_ASSERTION(pos->mMinHeight.HasPercent(),
2337 : "unknown min-height unit");
2338 : }
2339 :
2340 0 : aResult = NSToCoordRound(aStyle.GetPercentValue() * h);
2341 0 : return true;
2342 : }
2343 :
2344 : // Handles only -moz-max-content and -moz-min-content, and
2345 : // -moz-fit-content for min-width and max-width, since the others
2346 : // (-moz-fit-content for width, and -moz-available) have no effect on
2347 : // intrinsic widths.
2348 : enum eWidthProperty { PROP_WIDTH, PROP_MAX_WIDTH, PROP_MIN_WIDTH };
2349 : static bool
2350 0 : GetIntrinsicCoord(const nsStyleCoord& aStyle,
2351 : nsRenderingContext* aRenderingContext,
2352 : nsIFrame* aFrame,
2353 : eWidthProperty aProperty,
2354 : nscoord& aResult)
2355 : {
2356 0 : NS_PRECONDITION(aProperty == PROP_WIDTH || aProperty == PROP_MAX_WIDTH ||
2357 : aProperty == PROP_MIN_WIDTH, "unexpected property");
2358 0 : if (aStyle.GetUnit() != eStyleUnit_Enumerated)
2359 0 : return false;
2360 0 : PRInt32 val = aStyle.GetIntValue();
2361 0 : NS_ASSERTION(val == NS_STYLE_WIDTH_MAX_CONTENT ||
2362 : val == NS_STYLE_WIDTH_MIN_CONTENT ||
2363 : val == NS_STYLE_WIDTH_FIT_CONTENT ||
2364 : val == NS_STYLE_WIDTH_AVAILABLE,
2365 : "unexpected enumerated value for width property");
2366 0 : if (val == NS_STYLE_WIDTH_AVAILABLE)
2367 0 : return false;
2368 0 : if (val == NS_STYLE_WIDTH_FIT_CONTENT) {
2369 0 : if (aProperty == PROP_WIDTH)
2370 0 : return false; // handle like 'width: auto'
2371 0 : if (aProperty == PROP_MAX_WIDTH)
2372 : // constrain large 'width' values down to -moz-max-content
2373 0 : val = NS_STYLE_WIDTH_MAX_CONTENT;
2374 : else
2375 : // constrain small 'width' or 'max-width' values up to -moz-min-content
2376 0 : val = NS_STYLE_WIDTH_MIN_CONTENT;
2377 : }
2378 :
2379 0 : NS_ASSERTION(val == NS_STYLE_WIDTH_MAX_CONTENT ||
2380 : val == NS_STYLE_WIDTH_MIN_CONTENT,
2381 : "should have reduced everything remaining to one of these");
2382 :
2383 : // If aFrame is a container for font size inflation, then shrink
2384 : // wrapping inside of it should not apply font size inflation.
2385 0 : AutoMaybeNullInflationContainer an(aFrame);
2386 :
2387 0 : if (val == NS_STYLE_WIDTH_MAX_CONTENT)
2388 0 : aResult = aFrame->GetPrefWidth(aRenderingContext);
2389 : else
2390 0 : aResult = aFrame->GetMinWidth(aRenderingContext);
2391 0 : return true;
2392 : }
2393 :
2394 : #undef DEBUG_INTRINSIC_WIDTH
2395 :
2396 : #ifdef DEBUG_INTRINSIC_WIDTH
2397 : static PRInt32 gNoiseIndent = 0;
2398 : #endif
2399 :
2400 : /* static */ nscoord
2401 0 : nsLayoutUtils::IntrinsicForContainer(nsRenderingContext *aRenderingContext,
2402 : nsIFrame *aFrame,
2403 : IntrinsicWidthType aType)
2404 : {
2405 0 : NS_PRECONDITION(aFrame, "null frame");
2406 0 : NS_PRECONDITION(aType == MIN_WIDTH || aType == PREF_WIDTH, "bad type");
2407 :
2408 : #ifdef DEBUG_INTRINSIC_WIDTH
2409 : nsFrame::IndentBy(stdout, gNoiseIndent);
2410 : static_cast<nsFrame*>(aFrame)->ListTag(stdout);
2411 : printf(" %s intrinsic width for container:\n",
2412 : aType == MIN_WIDTH ? "min" : "pref");
2413 : #endif
2414 :
2415 : // If aFrame is a container for font size inflation, then shrink
2416 : // wrapping inside of it should not apply font size inflation.
2417 0 : AutoMaybeNullInflationContainer an(aFrame);
2418 :
2419 : nsIFrame::IntrinsicWidthOffsetData offsets =
2420 0 : aFrame->IntrinsicWidthOffsets(aRenderingContext);
2421 :
2422 0 : const nsStylePosition *stylePos = aFrame->GetStylePosition();
2423 0 : PRUint8 boxSizing = stylePos->mBoxSizing;
2424 0 : const nsStyleCoord &styleWidth = stylePos->mWidth;
2425 0 : const nsStyleCoord &styleMinWidth = stylePos->mMinWidth;
2426 0 : const nsStyleCoord &styleMaxWidth = stylePos->mMaxWidth;
2427 :
2428 : // We build up two values starting with the content box, and then
2429 : // adding padding, border and margin. The result is normally
2430 : // |result|. Then, when we handle 'width', 'min-width', and
2431 : // 'max-width', we use the results we've been building in |min| as a
2432 : // minimum, overriding 'min-width'. This ensures two things:
2433 : // * that we don't let a value of 'box-sizing' specifying a width
2434 : // smaller than the padding/border inside the box-sizing box give
2435 : // a content width less than zero
2436 : // * that we prevent tables from becoming smaller than their
2437 : // intrinsic minimum width
2438 0 : nscoord result = 0, min = 0;
2439 :
2440 : nscoord maxw;
2441 0 : bool haveFixedMaxWidth = GetAbsoluteCoord(styleMaxWidth, maxw);
2442 : nscoord minw;
2443 0 : bool haveFixedMinWidth = GetAbsoluteCoord(styleMinWidth, minw);
2444 :
2445 : // If we have a specified width (or a specified 'min-width' greater
2446 : // than the specified 'max-width', which works out to the same thing),
2447 : // don't even bother getting the frame's intrinsic width.
2448 0 : if (styleWidth.GetUnit() == eStyleUnit_Enumerated &&
2449 0 : (styleWidth.GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT ||
2450 0 : styleWidth.GetIntValue() == NS_STYLE_WIDTH_MIN_CONTENT)) {
2451 : // -moz-fit-content and -moz-available enumerated widths compute intrinsic
2452 : // widths just like auto.
2453 : // For -moz-max-content and -moz-min-content, we handle them like
2454 : // specified widths, but ignore -moz-box-sizing.
2455 0 : boxSizing = NS_STYLE_BOX_SIZING_CONTENT;
2456 0 : } else if (styleWidth.GetUnit() != eStyleUnit_Coord &&
2457 0 : !(haveFixedMinWidth && haveFixedMaxWidth && maxw <= minw)) {
2458 : #ifdef DEBUG_INTRINSIC_WIDTH
2459 : ++gNoiseIndent;
2460 : #endif
2461 0 : if (aType == MIN_WIDTH)
2462 0 : result = aFrame->GetMinWidth(aRenderingContext);
2463 : else
2464 0 : result = aFrame->GetPrefWidth(aRenderingContext);
2465 : #ifdef DEBUG_INTRINSIC_WIDTH
2466 : --gNoiseIndent;
2467 : nsFrame::IndentBy(stdout, gNoiseIndent);
2468 : static_cast<nsFrame*>(aFrame)->ListTag(stdout);
2469 : printf(" %s intrinsic width from frame is %d.\n",
2470 : aType == MIN_WIDTH ? "min" : "pref", result);
2471 : #endif
2472 :
2473 : // Handle elements with an intrinsic ratio (or size) and a specified
2474 : // height, min-height, or max-height.
2475 0 : const nsStyleCoord &styleHeight = stylePos->mHeight;
2476 0 : const nsStyleCoord &styleMinHeight = stylePos->mMinHeight;
2477 0 : const nsStyleCoord &styleMaxHeight = stylePos->mMaxHeight;
2478 0 : if (styleHeight.GetUnit() != eStyleUnit_Auto ||
2479 0 : !(styleMinHeight.GetUnit() == eStyleUnit_Coord &&
2480 0 : styleMinHeight.GetCoordValue() == 0) ||
2481 0 : styleMaxHeight.GetUnit() != eStyleUnit_None) {
2482 :
2483 0 : nsSize ratio = aFrame->GetIntrinsicRatio();
2484 :
2485 0 : if (ratio.height != 0) {
2486 :
2487 : nscoord h;
2488 0 : if (GetAbsoluteCoord(styleHeight, h) ||
2489 0 : GetPercentHeight(styleHeight, aFrame, h)) {
2490 : result =
2491 0 : NSToCoordRound(h * (float(ratio.width) / float(ratio.height)));
2492 : }
2493 :
2494 0 : if (GetAbsoluteCoord(styleMaxHeight, h) ||
2495 0 : GetPercentHeight(styleMaxHeight, aFrame, h)) {
2496 0 : h = NSToCoordRound(h * (float(ratio.width) / float(ratio.height)));
2497 0 : if (h < result)
2498 0 : result = h;
2499 : }
2500 :
2501 0 : if (GetAbsoluteCoord(styleMinHeight, h) ||
2502 0 : GetPercentHeight(styleMinHeight, aFrame, h)) {
2503 0 : h = NSToCoordRound(h * (float(ratio.width) / float(ratio.height)));
2504 0 : if (h > result)
2505 0 : result = h;
2506 : }
2507 : }
2508 : }
2509 : }
2510 :
2511 0 : if (aFrame->GetType() == nsGkAtoms::tableFrame) {
2512 : // Tables can't shrink smaller than their intrinsic minimum width,
2513 : // no matter what.
2514 0 : min = aFrame->GetMinWidth(aRenderingContext);
2515 : }
2516 :
2517 : // We also need to track what has been added on outside of the box
2518 : // (controlled by 'box-sizing') where 'width', 'min-width' and
2519 : // 'max-width' are applied. We have to account for these properties
2520 : // after getting all the offsets (margin, border, padding) because
2521 : // percentages do not operate linearly.
2522 : // Doing this is ok because although percentages aren't handled
2523 : // linearly, they are handled monotonically.
2524 0 : nscoord coordOutsideWidth = offsets.hPadding;
2525 0 : float pctOutsideWidth = offsets.hPctPadding;
2526 :
2527 0 : float pctTotal = 0.0f;
2528 :
2529 0 : if (boxSizing == NS_STYLE_BOX_SIZING_PADDING) {
2530 0 : min += coordOutsideWidth;
2531 0 : result = NSCoordSaturatingAdd(result, coordOutsideWidth);
2532 0 : pctTotal += pctOutsideWidth;
2533 :
2534 0 : coordOutsideWidth = 0;
2535 0 : pctOutsideWidth = 0.0f;
2536 : }
2537 :
2538 0 : coordOutsideWidth += offsets.hBorder;
2539 :
2540 0 : if (boxSizing == NS_STYLE_BOX_SIZING_BORDER) {
2541 0 : min += coordOutsideWidth;
2542 0 : result = NSCoordSaturatingAdd(result, coordOutsideWidth);
2543 0 : pctTotal += pctOutsideWidth;
2544 :
2545 0 : coordOutsideWidth = 0;
2546 0 : pctOutsideWidth = 0.0f;
2547 : }
2548 :
2549 0 : coordOutsideWidth += offsets.hMargin;
2550 0 : pctOutsideWidth += offsets.hPctMargin;
2551 :
2552 0 : min += coordOutsideWidth;
2553 0 : result = NSCoordSaturatingAdd(result, coordOutsideWidth);
2554 0 : pctTotal += pctOutsideWidth;
2555 :
2556 : nscoord w;
2557 0 : if (GetAbsoluteCoord(styleWidth, w) ||
2558 : GetIntrinsicCoord(styleWidth, aRenderingContext, aFrame,
2559 0 : PROP_WIDTH, w)) {
2560 0 : result = AddPercents(aType, w + coordOutsideWidth, pctOutsideWidth);
2561 : }
2562 0 : else if (aType == MIN_WIDTH &&
2563 : // The only cases of coord-percent-calc() units that
2564 : // GetAbsoluteCoord didn't handle are percent and calc()s
2565 : // containing percent.
2566 0 : styleWidth.IsCoordPercentCalcUnit() &&
2567 0 : aFrame->IsFrameOfType(nsIFrame::eReplaced)) {
2568 : // A percentage width on replaced elements means they can shrink to 0.
2569 0 : result = 0; // let |min| handle padding/border/margin
2570 : }
2571 : else {
2572 : // NOTE: We could really do a lot better for percents and for some
2573 : // cases of calc() containing percent (certainly including any where
2574 : // the coefficient on the percent is positive and there are no max()
2575 : // expressions). However, doing better for percents wouldn't be
2576 : // backwards compatible.
2577 0 : result = AddPercents(aType, result, pctTotal);
2578 : }
2579 :
2580 0 : if (haveFixedMaxWidth ||
2581 : GetIntrinsicCoord(styleMaxWidth, aRenderingContext, aFrame,
2582 0 : PROP_MAX_WIDTH, maxw)) {
2583 0 : maxw = AddPercents(aType, maxw + coordOutsideWidth, pctOutsideWidth);
2584 0 : if (result > maxw)
2585 0 : result = maxw;
2586 : }
2587 :
2588 0 : if (haveFixedMinWidth ||
2589 : GetIntrinsicCoord(styleMinWidth, aRenderingContext, aFrame,
2590 0 : PROP_MIN_WIDTH, minw)) {
2591 0 : minw = AddPercents(aType, minw + coordOutsideWidth, pctOutsideWidth);
2592 0 : if (result < minw)
2593 0 : result = minw;
2594 : }
2595 :
2596 0 : min = AddPercents(aType, min, pctTotal);
2597 0 : if (result < min)
2598 0 : result = min;
2599 :
2600 0 : const nsStyleDisplay *disp = aFrame->GetStyleDisplay();
2601 0 : if (aFrame->IsThemed(disp)) {
2602 0 : nsIntSize size(0, 0);
2603 0 : bool canOverride = true;
2604 0 : nsPresContext *presContext = aFrame->PresContext();
2605 0 : presContext->GetTheme()->
2606 : GetMinimumWidgetSize(aRenderingContext, aFrame, disp->mAppearance,
2607 0 : &size, &canOverride);
2608 :
2609 0 : nscoord themeWidth = presContext->DevPixelsToAppUnits(size.width);
2610 :
2611 : // GMWS() returns a border-box width
2612 0 : themeWidth += offsets.hMargin;
2613 0 : themeWidth = AddPercents(aType, themeWidth, offsets.hPctMargin);
2614 :
2615 0 : if (themeWidth > result || !canOverride)
2616 0 : result = themeWidth;
2617 : }
2618 :
2619 : #ifdef DEBUG_INTRINSIC_WIDTH
2620 : nsFrame::IndentBy(stdout, gNoiseIndent);
2621 : static_cast<nsFrame*>(aFrame)->ListTag(stdout);
2622 : printf(" %s intrinsic width for container is %d twips.\n",
2623 : aType == MIN_WIDTH ? "min" : "pref", result);
2624 : #endif
2625 :
2626 0 : return result;
2627 : }
2628 :
2629 : /* static */ nscoord
2630 0 : nsLayoutUtils::ComputeWidthDependentValue(
2631 : nscoord aContainingBlockWidth,
2632 : const nsStyleCoord& aCoord)
2633 : {
2634 0 : NS_WARN_IF_FALSE(aContainingBlockWidth != NS_UNCONSTRAINEDSIZE,
2635 : "have unconstrained width; this should only result from "
2636 : "very large sizes, not attempts at intrinsic width "
2637 : "calculation");
2638 :
2639 0 : if (aCoord.IsCoordPercentCalcUnit()) {
2640 0 : return nsRuleNode::ComputeCoordPercentCalc(aCoord, aContainingBlockWidth);
2641 : }
2642 0 : NS_ASSERTION(aCoord.GetUnit() == eStyleUnit_None ||
2643 : aCoord.GetUnit() == eStyleUnit_Auto,
2644 : "unexpected width value");
2645 0 : return 0;
2646 : }
2647 :
2648 : /* static */ nscoord
2649 0 : nsLayoutUtils::ComputeWidthValue(
2650 : nsRenderingContext* aRenderingContext,
2651 : nsIFrame* aFrame,
2652 : nscoord aContainingBlockWidth,
2653 : nscoord aContentEdgeToBoxSizing,
2654 : nscoord aBoxSizingToMarginEdge,
2655 : const nsStyleCoord& aCoord)
2656 : {
2657 0 : NS_PRECONDITION(aFrame, "non-null frame expected");
2658 0 : NS_PRECONDITION(aRenderingContext, "non-null rendering context expected");
2659 0 : NS_WARN_IF_FALSE(aContainingBlockWidth != NS_UNCONSTRAINEDSIZE,
2660 : "have unconstrained width; this should only result from "
2661 : "very large sizes, not attempts at intrinsic width "
2662 : "calculation");
2663 0 : NS_PRECONDITION(aContainingBlockWidth >= 0,
2664 : "width less than zero");
2665 :
2666 : nscoord result;
2667 0 : if (aCoord.IsCoordPercentCalcUnit()) {
2668 0 : result = nsRuleNode::ComputeCoordPercentCalc(aCoord, aContainingBlockWidth);
2669 : // The result of a calc() expression might be less than 0; we
2670 : // should clamp at runtime (below). (Percentages and coords that
2671 : // are less than 0 have already been dropped by the parser.)
2672 0 : result -= aContentEdgeToBoxSizing;
2673 0 : } else if (eStyleUnit_Enumerated == aCoord.GetUnit()) {
2674 : // If aFrame is a container for font size inflation, then shrink
2675 : // wrapping inside of it should not apply font size inflation.
2676 0 : AutoMaybeNullInflationContainer an(aFrame);
2677 :
2678 0 : PRInt32 val = aCoord.GetIntValue();
2679 0 : switch (val) {
2680 : case NS_STYLE_WIDTH_MAX_CONTENT:
2681 0 : result = aFrame->GetPrefWidth(aRenderingContext);
2682 0 : NS_ASSERTION(result >= 0, "width less than zero");
2683 0 : break;
2684 : case NS_STYLE_WIDTH_MIN_CONTENT:
2685 0 : result = aFrame->GetMinWidth(aRenderingContext);
2686 0 : NS_ASSERTION(result >= 0, "width less than zero");
2687 0 : break;
2688 : case NS_STYLE_WIDTH_FIT_CONTENT:
2689 : {
2690 0 : nscoord pref = aFrame->GetPrefWidth(aRenderingContext),
2691 0 : min = aFrame->GetMinWidth(aRenderingContext),
2692 : fill = aContainingBlockWidth -
2693 0 : (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing);
2694 0 : result = NS_MAX(min, NS_MIN(pref, fill));
2695 0 : NS_ASSERTION(result >= 0, "width less than zero");
2696 : }
2697 0 : break;
2698 : case NS_STYLE_WIDTH_AVAILABLE:
2699 : result = aContainingBlockWidth -
2700 0 : (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing);
2701 : }
2702 : } else {
2703 0 : NS_NOTREACHED("unexpected width value");
2704 0 : result = 0;
2705 : }
2706 0 : if (result < 0)
2707 0 : result = 0;
2708 0 : return result;
2709 : }
2710 :
2711 :
2712 : /* static */ nscoord
2713 0 : nsLayoutUtils::ComputeHeightDependentValue(
2714 : nscoord aContainingBlockHeight,
2715 : const nsStyleCoord& aCoord)
2716 : {
2717 : // XXXldb Some callers explicitly check aContainingBlockHeight
2718 : // against NS_AUTOHEIGHT *and* unit against eStyleUnit_Percent or
2719 : // calc()s containing percents before calling this function.
2720 : // However, it would be much more likely to catch problems without
2721 : // the unit conditions.
2722 : // XXXldb Many callers pass a non-'auto' containing block height when
2723 : // according to CSS2.1 they should be passing 'auto'.
2724 0 : NS_PRECONDITION(NS_AUTOHEIGHT != aContainingBlockHeight ||
2725 : !aCoord.HasPercent(),
2726 : "unexpected containing block height");
2727 :
2728 0 : if (aCoord.IsCoordPercentCalcUnit()) {
2729 0 : return nsRuleNode::ComputeCoordPercentCalc(aCoord, aContainingBlockHeight);
2730 : }
2731 :
2732 0 : NS_ASSERTION(aCoord.GetUnit() == eStyleUnit_None ||
2733 : aCoord.GetUnit() == eStyleUnit_Auto,
2734 : "unexpected height value");
2735 0 : return 0;
2736 : }
2737 :
2738 : #define MULDIV(a,b,c) (nscoord(PRInt64(a) * PRInt64(b) / PRInt64(c)))
2739 :
2740 : /* static */ nsSize
2741 0 : nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(
2742 : nsRenderingContext* aRenderingContext, nsIFrame* aFrame,
2743 : const nsIFrame::IntrinsicSize& aIntrinsicSize,
2744 : nsSize aIntrinsicRatio, nsSize aCBSize,
2745 : nsSize aMargin, nsSize aBorder, nsSize aPadding)
2746 : {
2747 0 : const nsStylePosition *stylePos = aFrame->GetStylePosition();
2748 : // Handle intrinsic sizes and their interaction with
2749 : // {min-,max-,}{width,height} according to the rules in
2750 : // http://www.w3.org/TR/CSS21/visudet.html#min-max-widths
2751 :
2752 : // Note: throughout the following section of the function, I avoid
2753 : // a * (b / c) because of its reduced accuracy relative to a * b / c
2754 : // or (a * b) / c (which are equivalent).
2755 :
2756 0 : const bool isAutoWidth = stylePos->mWidth.GetUnit() == eStyleUnit_Auto;
2757 0 : const bool isAutoHeight = IsAutoHeight(stylePos->mHeight, aCBSize.height);
2758 :
2759 0 : nsSize boxSizingAdjust(0,0);
2760 0 : switch (stylePos->mBoxSizing) {
2761 : case NS_STYLE_BOX_SIZING_BORDER:
2762 0 : boxSizingAdjust += aBorder;
2763 : // fall through
2764 : case NS_STYLE_BOX_SIZING_PADDING:
2765 0 : boxSizingAdjust += aPadding;
2766 : }
2767 : nscoord boxSizingToMarginEdgeWidth =
2768 0 : aMargin.width + aBorder.width + aPadding.width - boxSizingAdjust.width;
2769 :
2770 : nscoord width, minWidth, maxWidth, height, minHeight, maxHeight;
2771 :
2772 0 : if (!isAutoWidth) {
2773 : width = nsLayoutUtils::ComputeWidthValue(aRenderingContext,
2774 : aFrame, aCBSize.width, boxSizingAdjust.width,
2775 0 : boxSizingToMarginEdgeWidth, stylePos->mWidth);
2776 0 : NS_ASSERTION(width >= 0, "negative result from ComputeWidthValue");
2777 : }
2778 :
2779 0 : if (stylePos->mMaxWidth.GetUnit() != eStyleUnit_None) {
2780 : maxWidth = nsLayoutUtils::ComputeWidthValue(aRenderingContext,
2781 : aFrame, aCBSize.width, boxSizingAdjust.width,
2782 0 : boxSizingToMarginEdgeWidth, stylePos->mMaxWidth);
2783 0 : NS_ASSERTION(maxWidth >= 0, "negative result from ComputeWidthValue");
2784 : } else {
2785 0 : maxWidth = nscoord_MAX;
2786 : }
2787 :
2788 : minWidth = nsLayoutUtils::ComputeWidthValue(aRenderingContext,
2789 : aFrame, aCBSize.width, boxSizingAdjust.width,
2790 0 : boxSizingToMarginEdgeWidth, stylePos->mMinWidth);
2791 0 : NS_ASSERTION(minWidth >= 0, "negative result from ComputeWidthValue");
2792 :
2793 0 : if (!isAutoHeight) {
2794 : height = nsLayoutUtils::
2795 0 : ComputeHeightValue(aCBSize.height, stylePos->mHeight) -
2796 0 : boxSizingAdjust.height;
2797 0 : if (height < 0)
2798 0 : height = 0;
2799 : }
2800 :
2801 0 : if (!IsAutoHeight(stylePos->mMaxHeight, aCBSize.height)) {
2802 : maxHeight = nsLayoutUtils::
2803 0 : ComputeHeightValue(aCBSize.height, stylePos->mMaxHeight) -
2804 0 : boxSizingAdjust.height;
2805 0 : if (maxHeight < 0)
2806 0 : maxHeight = 0;
2807 : } else {
2808 0 : maxHeight = nscoord_MAX;
2809 : }
2810 :
2811 0 : if (!IsAutoHeight(stylePos->mMinHeight, aCBSize.height)) {
2812 : minHeight = nsLayoutUtils::
2813 0 : ComputeHeightValue(aCBSize.height, stylePos->mMinHeight) -
2814 0 : boxSizingAdjust.height;
2815 0 : if (minHeight < 0)
2816 0 : minHeight = 0;
2817 : } else {
2818 0 : minHeight = 0;
2819 : }
2820 :
2821 : // Resolve percentage intrinsic width/height as necessary:
2822 :
2823 0 : NS_ASSERTION(aCBSize.width != NS_UNCONSTRAINEDSIZE,
2824 : "Our containing block must not have unconstrained width!");
2825 :
2826 : bool hasIntrinsicWidth, hasIntrinsicHeight;
2827 : nscoord intrinsicWidth, intrinsicHeight;
2828 :
2829 0 : if (aIntrinsicSize.width.GetUnit() == eStyleUnit_Coord) {
2830 0 : hasIntrinsicWidth = true;
2831 0 : intrinsicWidth = aIntrinsicSize.width.GetCoordValue();
2832 0 : if (intrinsicWidth < 0)
2833 0 : intrinsicWidth = 0;
2834 : } else {
2835 0 : NS_ASSERTION(aIntrinsicSize.width.GetUnit() == eStyleUnit_None,
2836 : "unexpected unit");
2837 0 : hasIntrinsicWidth = false;
2838 0 : intrinsicWidth = 0;
2839 : }
2840 :
2841 0 : if (aIntrinsicSize.height.GetUnit() == eStyleUnit_Coord) {
2842 0 : hasIntrinsicHeight = true;
2843 0 : intrinsicHeight = aIntrinsicSize.height.GetCoordValue();
2844 0 : if (intrinsicHeight < 0)
2845 0 : intrinsicHeight = 0;
2846 : } else {
2847 0 : NS_ASSERTION(aIntrinsicSize.height.GetUnit() == eStyleUnit_None,
2848 : "unexpected unit");
2849 0 : hasIntrinsicHeight = false;
2850 0 : intrinsicHeight = 0;
2851 : }
2852 :
2853 0 : NS_ASSERTION(aIntrinsicRatio.width >= 0 && aIntrinsicRatio.height >= 0,
2854 : "Intrinsic ratio has a negative component!");
2855 :
2856 : // Now calculate the used values for width and height:
2857 :
2858 0 : if (isAutoWidth) {
2859 0 : if (isAutoHeight) {
2860 :
2861 : // 'auto' width, 'auto' height
2862 :
2863 : // Get tentative values - CSS 2.1 sections 10.3.2 and 10.6.2:
2864 :
2865 : nscoord tentWidth, tentHeight;
2866 :
2867 0 : if (hasIntrinsicWidth) {
2868 0 : tentWidth = intrinsicWidth;
2869 0 : } else if (hasIntrinsicHeight && aIntrinsicRatio.height > 0) {
2870 0 : tentWidth = MULDIV(intrinsicHeight, aIntrinsicRatio.width, aIntrinsicRatio.height);
2871 0 : } else if (aIntrinsicRatio.width > 0) {
2872 0 : tentWidth = aCBSize.width - boxSizingToMarginEdgeWidth; // XXX scrollbar?
2873 0 : if (tentWidth < 0) tentWidth = 0;
2874 : } else {
2875 0 : tentWidth = nsPresContext::CSSPixelsToAppUnits(300);
2876 : }
2877 :
2878 0 : if (hasIntrinsicHeight) {
2879 0 : tentHeight = intrinsicHeight;
2880 0 : } else if (aIntrinsicRatio.width > 0) {
2881 0 : tentHeight = MULDIV(tentWidth, aIntrinsicRatio.height, aIntrinsicRatio.width);
2882 : } else {
2883 0 : tentHeight = nsPresContext::CSSPixelsToAppUnits(150);
2884 : }
2885 :
2886 : return ComputeAutoSizeWithIntrinsicDimensions(minWidth, minHeight,
2887 : maxWidth, maxHeight,
2888 0 : tentWidth, tentHeight);
2889 : } else {
2890 :
2891 : // 'auto' width, non-'auto' height
2892 0 : height = NS_CSS_MINMAX(height, minHeight, maxHeight);
2893 0 : if (aIntrinsicRatio.height > 0) {
2894 0 : width = MULDIV(height, aIntrinsicRatio.width, aIntrinsicRatio.height);
2895 0 : } else if (hasIntrinsicWidth) {
2896 0 : width = intrinsicWidth;
2897 : } else {
2898 0 : width = nsPresContext::CSSPixelsToAppUnits(300);
2899 : }
2900 0 : width = NS_CSS_MINMAX(width, minWidth, maxWidth);
2901 :
2902 : }
2903 : } else {
2904 0 : if (isAutoHeight) {
2905 :
2906 : // non-'auto' width, 'auto' height
2907 0 : width = NS_CSS_MINMAX(width, minWidth, maxWidth);
2908 0 : if (aIntrinsicRatio.width > 0) {
2909 0 : height = MULDIV(width, aIntrinsicRatio.height, aIntrinsicRatio.width);
2910 0 : } else if (hasIntrinsicHeight) {
2911 0 : height = intrinsicHeight;
2912 : } else {
2913 0 : height = nsPresContext::CSSPixelsToAppUnits(150);
2914 : }
2915 0 : height = NS_CSS_MINMAX(height, minHeight, maxHeight);
2916 :
2917 : } else {
2918 :
2919 : // non-'auto' width, non-'auto' height
2920 0 : width = NS_CSS_MINMAX(width, minWidth, maxWidth);
2921 0 : height = NS_CSS_MINMAX(height, minHeight, maxHeight);
2922 :
2923 : }
2924 : }
2925 :
2926 0 : return nsSize(width, height);
2927 : }
2928 :
2929 : nsSize
2930 0 : nsLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions(nscoord minWidth, nscoord minHeight,
2931 : nscoord maxWidth, nscoord maxHeight,
2932 : nscoord tentWidth, nscoord tentHeight)
2933 : {
2934 : // Now apply min/max-width/height - CSS 2.1 sections 10.4 and 10.7:
2935 :
2936 0 : if (minWidth > maxWidth)
2937 0 : maxWidth = minWidth;
2938 0 : if (minHeight > maxHeight)
2939 0 : maxHeight = minHeight;
2940 :
2941 : nscoord heightAtMaxWidth, heightAtMinWidth,
2942 : widthAtMaxHeight, widthAtMinHeight;
2943 :
2944 0 : if (tentWidth > 0) {
2945 0 : heightAtMaxWidth = MULDIV(maxWidth, tentHeight, tentWidth);
2946 0 : if (heightAtMaxWidth < minHeight)
2947 0 : heightAtMaxWidth = minHeight;
2948 0 : heightAtMinWidth = MULDIV(minWidth, tentHeight, tentWidth);
2949 0 : if (heightAtMinWidth > maxHeight)
2950 0 : heightAtMinWidth = maxHeight;
2951 : } else {
2952 0 : heightAtMaxWidth = heightAtMinWidth = NS_CSS_MINMAX(tentHeight, minHeight, maxHeight);
2953 : }
2954 :
2955 0 : if (tentHeight > 0) {
2956 0 : widthAtMaxHeight = MULDIV(maxHeight, tentWidth, tentHeight);
2957 0 : if (widthAtMaxHeight < minWidth)
2958 0 : widthAtMaxHeight = minWidth;
2959 0 : widthAtMinHeight = MULDIV(minHeight, tentWidth, tentHeight);
2960 0 : if (widthAtMinHeight > maxWidth)
2961 0 : widthAtMinHeight = maxWidth;
2962 : } else {
2963 0 : widthAtMaxHeight = widthAtMinHeight = NS_CSS_MINMAX(tentWidth, minWidth, maxWidth);
2964 : }
2965 :
2966 : // The table at http://www.w3.org/TR/CSS21/visudet.html#min-max-widths :
2967 :
2968 : nscoord width, height;
2969 :
2970 0 : if (tentWidth > maxWidth) {
2971 0 : if (tentHeight > maxHeight) {
2972 0 : if (PRInt64(maxWidth) * PRInt64(tentHeight) <=
2973 : PRInt64(maxHeight) * PRInt64(tentWidth)) {
2974 0 : width = maxWidth;
2975 0 : height = heightAtMaxWidth;
2976 : } else {
2977 0 : width = widthAtMaxHeight;
2978 0 : height = maxHeight;
2979 : }
2980 : } else {
2981 : // This also covers "(w > max-width) and (h < min-height)" since in
2982 : // that case (max-width/w < 1), and with (h < min-height):
2983 : // max(max-width * h/w, min-height) == min-height
2984 0 : width = maxWidth;
2985 0 : height = heightAtMaxWidth;
2986 : }
2987 0 : } else if (tentWidth < minWidth) {
2988 0 : if (tentHeight < minHeight) {
2989 0 : if (PRInt64(minWidth) * PRInt64(tentHeight) <=
2990 : PRInt64(minHeight) * PRInt64(tentWidth)) {
2991 0 : width = widthAtMinHeight;
2992 0 : height = minHeight;
2993 : } else {
2994 0 : width = minWidth;
2995 0 : height = heightAtMinWidth;
2996 : }
2997 : } else {
2998 : // This also covers "(w < min-width) and (h > max-height)" since in
2999 : // that case (min-width/w > 1), and with (h > max-height):
3000 : // min(min-width * h/w, max-height) == max-height
3001 0 : width = minWidth;
3002 0 : height = heightAtMinWidth;
3003 : }
3004 : } else {
3005 0 : if (tentHeight > maxHeight) {
3006 0 : width = widthAtMaxHeight;
3007 0 : height = maxHeight;
3008 0 : } else if (tentHeight < minHeight) {
3009 0 : width = widthAtMinHeight;
3010 0 : height = minHeight;
3011 : } else {
3012 0 : width = tentWidth;
3013 0 : height = tentHeight;
3014 : }
3015 : }
3016 :
3017 0 : return nsSize(width, height);
3018 : }
3019 :
3020 : /* static */ nscoord
3021 0 : nsLayoutUtils::MinWidthFromInline(nsIFrame* aFrame,
3022 : nsRenderingContext* aRenderingContext)
3023 : {
3024 0 : NS_ASSERTION(!nsLayoutUtils::IsContainerForFontSizeInflation(aFrame),
3025 : "should not be container for font size inflation");
3026 :
3027 0 : nsIFrame::InlineMinWidthData data;
3028 0 : DISPLAY_MIN_WIDTH(aFrame, data.prevLines);
3029 0 : aFrame->AddInlineMinWidth(aRenderingContext, &data);
3030 0 : data.ForceBreak(aRenderingContext);
3031 0 : return data.prevLines;
3032 : }
3033 :
3034 : /* static */ nscoord
3035 0 : nsLayoutUtils::PrefWidthFromInline(nsIFrame* aFrame,
3036 : nsRenderingContext* aRenderingContext)
3037 : {
3038 0 : NS_ASSERTION(!nsLayoutUtils::IsContainerForFontSizeInflation(aFrame),
3039 : "should not be container for font size inflation");
3040 :
3041 0 : nsIFrame::InlinePrefWidthData data;
3042 0 : DISPLAY_PREF_WIDTH(aFrame, data.prevLines);
3043 0 : aFrame->AddInlinePrefWidth(aRenderingContext, &data);
3044 0 : data.ForceBreak(aRenderingContext);
3045 0 : return data.prevLines;
3046 : }
3047 :
3048 : static nscolor
3049 0 : DarkenColor(nscolor aColor)
3050 : {
3051 : PRUint16 hue, sat, value;
3052 : PRUint8 alpha;
3053 :
3054 : // convert the RBG to HSV so we can get the lightness (which is the v)
3055 0 : NS_RGB2HSV(aColor, hue, sat, value, alpha);
3056 :
3057 : // The goal here is to send white to black while letting colored
3058 : // stuff stay colored... So we adopt the following approach.
3059 : // Something with sat = 0 should end up with value = 0. Something
3060 : // with a high sat can end up with a high value and it's ok.... At
3061 : // the same time, we don't want to make things lighter. Do
3062 : // something simple, since it seems to work.
3063 0 : if (value > sat) {
3064 0 : value = sat;
3065 : // convert this color back into the RGB color space.
3066 0 : NS_HSV2RGB(aColor, hue, sat, value, alpha);
3067 : }
3068 0 : return aColor;
3069 : }
3070 :
3071 : // Check whether we should darken text/decoration colors. We need to do this if
3072 : // background images and colors are being suppressed, because that means
3073 : // light text will not be visible against the (presumed light-colored) background.
3074 : static bool
3075 0 : ShouldDarkenColors(nsPresContext* aPresContext)
3076 : {
3077 0 : return !aPresContext->GetBackgroundColorDraw() &&
3078 0 : !aPresContext->GetBackgroundImageDraw();
3079 : }
3080 :
3081 : nscolor
3082 0 : nsLayoutUtils::GetColor(nsIFrame* aFrame, nsCSSProperty aProperty)
3083 : {
3084 0 : nscolor color = aFrame->GetVisitedDependentColor(aProperty);
3085 0 : if (ShouldDarkenColors(aFrame->PresContext())) {
3086 0 : color = DarkenColor(color);
3087 : }
3088 0 : return color;
3089 : }
3090 :
3091 : gfxFloat
3092 0 : nsLayoutUtils::GetSnappedBaselineY(nsIFrame* aFrame, gfxContext* aContext,
3093 : nscoord aY, nscoord aAscent)
3094 : {
3095 0 : gfxFloat appUnitsPerDevUnit = aFrame->PresContext()->AppUnitsPerDevPixel();
3096 0 : gfxFloat baseline = gfxFloat(aY) + aAscent;
3097 0 : gfxRect putativeRect(0, baseline/appUnitsPerDevUnit, 1, 1);
3098 0 : if (!aContext->UserToDevicePixelSnapped(putativeRect, true))
3099 0 : return baseline;
3100 0 : return aContext->DeviceToUser(putativeRect.TopLeft()).y * appUnitsPerDevUnit;
3101 : }
3102 :
3103 : void
3104 0 : nsLayoutUtils::DrawString(const nsIFrame* aFrame,
3105 : nsRenderingContext* aContext,
3106 : const PRUnichar* aString,
3107 : PRInt32 aLength,
3108 : nsPoint aPoint,
3109 : PRUint8 aDirection)
3110 : {
3111 : #ifdef IBMBIDI
3112 0 : nsresult rv = NS_ERROR_FAILURE;
3113 0 : nsPresContext* presContext = aFrame->PresContext();
3114 0 : if (presContext->BidiEnabled()) {
3115 0 : if (aDirection == NS_STYLE_DIRECTION_INHERIT) {
3116 0 : aDirection = aFrame->GetStyleVisibility()->mDirection;
3117 : }
3118 : nsBidiDirection direction =
3119 : (NS_STYLE_DIRECTION_RTL == aDirection) ?
3120 0 : NSBIDI_RTL : NSBIDI_LTR;
3121 : rv = nsBidiPresUtils::RenderText(aString, aLength, direction,
3122 : presContext, *aContext, *aContext,
3123 0 : aPoint.x, aPoint.y);
3124 : }
3125 0 : if (NS_FAILED(rv))
3126 : #endif // IBMBIDI
3127 : {
3128 0 : aContext->SetTextRunRTL(false);
3129 0 : aContext->DrawString(aString, aLength, aPoint.x, aPoint.y);
3130 : }
3131 0 : }
3132 :
3133 : nscoord
3134 0 : nsLayoutUtils::GetStringWidth(const nsIFrame* aFrame,
3135 : nsRenderingContext* aContext,
3136 : const PRUnichar* aString,
3137 : PRInt32 aLength)
3138 : {
3139 : #ifdef IBMBIDI
3140 0 : nsPresContext* presContext = aFrame->PresContext();
3141 0 : if (presContext->BidiEnabled()) {
3142 0 : const nsStyleVisibility* vis = aFrame->GetStyleVisibility();
3143 : nsBidiDirection direction =
3144 : (NS_STYLE_DIRECTION_RTL == vis->mDirection) ?
3145 0 : NSBIDI_RTL : NSBIDI_LTR;
3146 : return nsBidiPresUtils::MeasureTextWidth(aString, aLength,
3147 0 : direction, presContext, *aContext);
3148 : }
3149 : #endif // IBMBIDI
3150 0 : aContext->SetTextRunRTL(false);
3151 0 : return aContext->GetWidth(aString, aLength);
3152 : }
3153 :
3154 : /* static */ void
3155 0 : nsLayoutUtils::PaintTextShadow(const nsIFrame* aFrame,
3156 : nsRenderingContext* aContext,
3157 : const nsRect& aTextRect,
3158 : const nsRect& aDirtyRect,
3159 : const nscolor& aForegroundColor,
3160 : TextShadowCallback aCallback,
3161 : void* aCallbackData)
3162 : {
3163 0 : const nsStyleText* textStyle = aFrame->GetStyleText();
3164 0 : if (!textStyle->mTextShadow)
3165 0 : return;
3166 :
3167 : // Text shadow happens with the last value being painted at the back,
3168 : // ie. it is painted first.
3169 0 : gfxContext* aDestCtx = aContext->ThebesContext();
3170 0 : for (PRUint32 i = textStyle->mTextShadow->Length(); i > 0; --i) {
3171 0 : nsCSSShadowItem* shadowDetails = textStyle->mTextShadow->ShadowAt(i - 1);
3172 : nsPoint shadowOffset(shadowDetails->mXOffset,
3173 0 : shadowDetails->mYOffset);
3174 0 : nscoord blurRadius = NS_MAX(shadowDetails->mRadius, 0);
3175 :
3176 0 : nsRect shadowRect(aTextRect);
3177 0 : shadowRect.MoveBy(shadowOffset);
3178 :
3179 0 : nsPresContext* presCtx = aFrame->PresContext();
3180 0 : nsContextBoxBlur contextBoxBlur;
3181 : gfxContext* shadowContext = contextBoxBlur.Init(shadowRect, 0, blurRadius,
3182 0 : presCtx->AppUnitsPerDevPixel(),
3183 0 : aDestCtx, aDirtyRect, nsnull);
3184 0 : if (!shadowContext)
3185 0 : continue;
3186 :
3187 : nscolor shadowColor;
3188 0 : if (shadowDetails->mHasColor)
3189 0 : shadowColor = shadowDetails->mColor;
3190 : else
3191 0 : shadowColor = aForegroundColor;
3192 :
3193 : // Conjure an nsRenderingContext from a gfxContext for drawing the text
3194 : // to blur.
3195 0 : nsRefPtr<nsRenderingContext> renderingContext = new nsRenderingContext();
3196 0 : renderingContext->Init(presCtx->DeviceContext(), shadowContext);
3197 :
3198 0 : aDestCtx->Save();
3199 0 : aDestCtx->NewPath();
3200 0 : aDestCtx->SetColor(gfxRGBA(shadowColor));
3201 :
3202 : // The callback will draw whatever we want to blur as a shadow.
3203 0 : aCallback(renderingContext, shadowOffset, shadowColor, aCallbackData);
3204 :
3205 0 : contextBoxBlur.DoPaint();
3206 0 : aDestCtx->Restore();
3207 : }
3208 : }
3209 :
3210 : /* static */ nscoord
3211 0 : nsLayoutUtils::GetCenteredFontBaseline(nsFontMetrics* aFontMetrics,
3212 : nscoord aLineHeight)
3213 : {
3214 0 : nscoord fontAscent = aFontMetrics->MaxAscent();
3215 0 : nscoord fontHeight = aFontMetrics->MaxHeight();
3216 :
3217 0 : nscoord leading = aLineHeight - fontHeight;
3218 0 : return fontAscent + leading/2;
3219 : }
3220 :
3221 :
3222 : /* static */ bool
3223 0 : nsLayoutUtils::GetFirstLineBaseline(const nsIFrame* aFrame, nscoord* aResult)
3224 : {
3225 : LinePosition position;
3226 0 : if (!GetFirstLinePosition(aFrame, &position))
3227 0 : return false;
3228 0 : *aResult = position.mBaseline;
3229 0 : return true;
3230 : }
3231 :
3232 : /* static */ bool
3233 0 : nsLayoutUtils::GetFirstLinePosition(const nsIFrame* aFrame,
3234 : LinePosition* aResult)
3235 : {
3236 0 : const nsBlockFrame* block = nsLayoutUtils::GetAsBlock(const_cast<nsIFrame*>(aFrame));
3237 0 : if (!block) {
3238 : // For the first-line baseline we also have to check for a table, and if
3239 : // so, use the baseline of its first row.
3240 0 : nsIAtom* fType = aFrame->GetType();
3241 0 : if (fType == nsGkAtoms::tableOuterFrame) {
3242 0 : aResult->mTop = 0;
3243 0 : aResult->mBaseline = aFrame->GetBaseline();
3244 : // This is what we want for the list bullet caller; not sure if
3245 : // other future callers will want the same.
3246 0 : aResult->mBottom = aFrame->GetSize().height;
3247 0 : return true;
3248 : }
3249 :
3250 : // For first-line baselines, we have to consider scroll frames.
3251 0 : if (fType == nsGkAtoms::scrollFrame) {
3252 0 : nsIScrollableFrame *sFrame = do_QueryFrame(const_cast<nsIFrame*>(aFrame));
3253 0 : if (!sFrame) {
3254 0 : NS_NOTREACHED("not scroll frame");
3255 : }
3256 : LinePosition kidPosition;
3257 0 : if (GetFirstLinePosition(sFrame->GetScrolledFrame(), &kidPosition)) {
3258 : // Consider only the border and padding that contributes to the
3259 : // kid's position, not the scrolling, so we get the initial
3260 : // position.
3261 0 : *aResult = kidPosition + aFrame->GetUsedBorderAndPadding().top;
3262 0 : return true;
3263 : }
3264 0 : return false;
3265 : }
3266 :
3267 0 : if (fType == nsGkAtoms::fieldSetFrame) {
3268 : LinePosition kidPosition;
3269 0 : nsIFrame* kid = aFrame->GetFirstPrincipalChild();
3270 : // kid might be a legend frame here, but that's ok.
3271 0 : if (GetFirstLinePosition(kid, &kidPosition)) {
3272 0 : *aResult = kidPosition + kid->GetPosition().y;
3273 0 : return true;
3274 : }
3275 0 : return false;
3276 : }
3277 :
3278 : // No baseline.
3279 0 : return false;
3280 : }
3281 :
3282 0 : for (nsBlockFrame::const_line_iterator line = block->begin_lines(),
3283 0 : line_end = block->end_lines();
3284 : line != line_end; ++line) {
3285 0 : if (line->IsBlock()) {
3286 0 : nsIFrame *kid = line->mFirstChild;
3287 : LinePosition kidPosition;
3288 0 : if (GetFirstLinePosition(kid, &kidPosition)) {
3289 0 : *aResult = kidPosition + kid->GetPosition().y;
3290 0 : return true;
3291 : }
3292 : } else {
3293 : // XXX Is this the right test? We have some bogus empty lines
3294 : // floating around, but IsEmpty is perhaps too weak.
3295 0 : if (line->GetHeight() != 0 || !line->IsEmpty()) {
3296 0 : nscoord top = line->mBounds.y;
3297 0 : aResult->mTop = top;
3298 0 : aResult->mBaseline = top + line->GetAscent();
3299 0 : aResult->mBottom = top + line->GetHeight();
3300 0 : return true;
3301 : }
3302 : }
3303 : }
3304 0 : return false;
3305 : }
3306 :
3307 : /* static */ bool
3308 0 : nsLayoutUtils::GetLastLineBaseline(const nsIFrame* aFrame, nscoord* aResult)
3309 : {
3310 0 : const nsBlockFrame* block = nsLayoutUtils::GetAsBlock(const_cast<nsIFrame*>(aFrame));
3311 0 : if (!block)
3312 : // No baseline. (We intentionally don't descend into scroll frames.)
3313 0 : return false;
3314 :
3315 0 : for (nsBlockFrame::const_reverse_line_iterator line = block->rbegin_lines(),
3316 0 : line_end = block->rend_lines();
3317 : line != line_end; ++line) {
3318 0 : if (line->IsBlock()) {
3319 0 : nsIFrame *kid = line->mFirstChild;
3320 : nscoord kidBaseline;
3321 0 : if (GetLastLineBaseline(kid, &kidBaseline)) {
3322 0 : *aResult = kidBaseline + kid->GetPosition().y;
3323 0 : return true;
3324 0 : } else if (kid->GetType() == nsGkAtoms::scrollFrame) {
3325 : // Use the bottom of the scroll frame.
3326 : // XXX CSS2.1 really doesn't say what to do here.
3327 0 : *aResult = kid->GetRect().YMost();
3328 0 : return true;
3329 : }
3330 : } else {
3331 : // XXX Is this the right test? We have some bogus empty lines
3332 : // floating around, but IsEmpty is perhaps too weak.
3333 0 : if (line->GetHeight() != 0 || !line->IsEmpty()) {
3334 0 : *aResult = line->mBounds.y + line->GetAscent();
3335 0 : return true;
3336 : }
3337 : }
3338 : }
3339 0 : return false;
3340 : }
3341 :
3342 : static nscoord
3343 0 : CalculateBlockContentBottom(nsBlockFrame* aFrame)
3344 : {
3345 0 : NS_PRECONDITION(aFrame, "null ptr");
3346 :
3347 0 : nscoord contentBottom = 0;
3348 :
3349 0 : for (nsBlockFrame::line_iterator line = aFrame->begin_lines(),
3350 0 : line_end = aFrame->end_lines();
3351 : line != line_end; ++line) {
3352 0 : if (line->IsBlock()) {
3353 0 : nsIFrame* child = line->mFirstChild;
3354 0 : nscoord offset = child->GetRect().y - child->GetRelativeOffset().y;
3355 : contentBottom = NS_MAX(contentBottom,
3356 0 : nsLayoutUtils::CalculateContentBottom(child) + offset);
3357 : }
3358 : else {
3359 0 : contentBottom = NS_MAX(contentBottom, line->mBounds.YMost());
3360 : }
3361 : }
3362 0 : return contentBottom;
3363 : }
3364 :
3365 : /* static */ nscoord
3366 0 : nsLayoutUtils::CalculateContentBottom(nsIFrame* aFrame)
3367 : {
3368 0 : NS_PRECONDITION(aFrame, "null ptr");
3369 :
3370 0 : nscoord contentBottom = aFrame->GetRect().height;
3371 :
3372 : // We want scrollable overflow rather than visual because this
3373 : // calculation is intended to affect layout.
3374 0 : if (aFrame->GetScrollableOverflowRect().height > contentBottom) {
3375 : nsIFrame::ChildListIDs skip(nsIFrame::kOverflowList |
3376 : nsIFrame::kExcessOverflowContainersList |
3377 0 : nsIFrame::kOverflowOutOfFlowList);
3378 0 : nsBlockFrame* blockFrame = GetAsBlock(aFrame);
3379 0 : if (blockFrame) {
3380 : contentBottom =
3381 0 : NS_MAX(contentBottom, CalculateBlockContentBottom(blockFrame));
3382 0 : skip |= nsIFrame::kPrincipalList;
3383 : }
3384 0 : nsIFrame::ChildListIterator lists(aFrame);
3385 0 : for (; !lists.IsDone(); lists.Next()) {
3386 0 : if (!skip.Contains(lists.CurrentID())) {
3387 0 : nsFrameList::Enumerator childFrames(lists.CurrentList());
3388 0 : for (; !childFrames.AtEnd(); childFrames.Next()) {
3389 0 : nsIFrame* child = childFrames.get();
3390 0 : nscoord offset = child->GetRect().y - child->GetRelativeOffset().y;
3391 : contentBottom = NS_MAX(contentBottom,
3392 0 : CalculateContentBottom(child) + offset);
3393 : }
3394 : }
3395 : }
3396 : }
3397 0 : return contentBottom;
3398 : }
3399 :
3400 : /* static */ nsIFrame*
3401 0 : nsLayoutUtils::GetClosestLayer(nsIFrame* aFrame)
3402 : {
3403 : nsIFrame* layer;
3404 0 : for (layer = aFrame; layer; layer = layer->GetParent()) {
3405 0 : if (layer->GetStyleDisplay()->IsPositioned() ||
3406 0 : (layer->GetParent() &&
3407 0 : layer->GetParent()->GetType() == nsGkAtoms::scrollFrame))
3408 0 : break;
3409 : }
3410 0 : if (layer)
3411 0 : return layer;
3412 0 : return aFrame->PresContext()->PresShell()->FrameManager()->GetRootFrame();
3413 : }
3414 :
3415 : GraphicsFilter
3416 0 : nsLayoutUtils::GetGraphicsFilterForFrame(nsIFrame* aForFrame)
3417 : {
3418 0 : GraphicsFilter defaultFilter = gfxPattern::FILTER_GOOD;
3419 0 : nsIFrame *frame = nsCSSRendering::IsCanvasFrame(aForFrame) ?
3420 0 : nsCSSRendering::FindBackgroundStyleFrame(aForFrame) : aForFrame;
3421 :
3422 0 : switch (frame->GetStyleSVG()->mImageRendering) {
3423 : case NS_STYLE_IMAGE_RENDERING_OPTIMIZESPEED:
3424 0 : return gfxPattern::FILTER_FAST;
3425 : case NS_STYLE_IMAGE_RENDERING_OPTIMIZEQUALITY:
3426 0 : return gfxPattern::FILTER_BEST;
3427 : case NS_STYLE_IMAGE_RENDERING_CRISPEDGES:
3428 0 : return gfxPattern::FILTER_NEAREST;
3429 : default:
3430 0 : return defaultFilter;
3431 : }
3432 : }
3433 :
3434 : /**
3435 : * Given an image being drawn into an appunit coordinate system, and
3436 : * a point in that coordinate system, map the point back into image
3437 : * pixel space.
3438 : * @param aSize the size of the image, in pixels
3439 : * @param aDest the rectangle that the image is being mapped into
3440 : * @param aPt a point in the same coordinate system as the rectangle
3441 : */
3442 : static gfxPoint
3443 0 : MapToFloatImagePixels(const gfxSize& aSize,
3444 : const gfxRect& aDest, const gfxPoint& aPt)
3445 : {
3446 0 : return gfxPoint(((aPt.x - aDest.X())*aSize.width)/aDest.Width(),
3447 0 : ((aPt.y - aDest.Y())*aSize.height)/aDest.Height());
3448 : }
3449 :
3450 : /**
3451 : * Given an image being drawn into an pixel-based coordinate system, and
3452 : * a point in image space, map the point into the pixel-based coordinate
3453 : * system.
3454 : * @param aSize the size of the image, in pixels
3455 : * @param aDest the rectangle that the image is being mapped into
3456 : * @param aPt a point in image space
3457 : */
3458 : static gfxPoint
3459 0 : MapToFloatUserPixels(const gfxSize& aSize,
3460 : const gfxRect& aDest, const gfxPoint& aPt)
3461 : {
3462 0 : return gfxPoint(aPt.x*aDest.Width()/aSize.width + aDest.X(),
3463 0 : aPt.y*aDest.Height()/aSize.height + aDest.Y());
3464 : }
3465 :
3466 : /* static */ gfxRect
3467 0 : nsLayoutUtils::RectToGfxRect(const nsRect& aRect, PRInt32 aAppUnitsPerDevPixel)
3468 : {
3469 : return gfxRect(gfxFloat(aRect.x) / aAppUnitsPerDevPixel,
3470 : gfxFloat(aRect.y) / aAppUnitsPerDevPixel,
3471 : gfxFloat(aRect.width) / aAppUnitsPerDevPixel,
3472 0 : gfxFloat(aRect.height) / aAppUnitsPerDevPixel);
3473 : }
3474 :
3475 : struct SnappedImageDrawingParameters {
3476 : // A transform from either device space or user space (depending on mResetCTM)
3477 : // to image space
3478 : gfxMatrix mUserSpaceToImageSpace;
3479 : // A device-space, pixel-aligned rectangle to fill
3480 : gfxRect mFillRect;
3481 : // A pixel rectangle in tiled image space outside of which gfx should not
3482 : // sample (using EXTEND_PAD as necessary)
3483 : nsIntRect mSubimage;
3484 : // Whether there's anything to draw at all
3485 : bool mShouldDraw;
3486 : // true iff the CTM of the rendering context needs to be reset to the
3487 : // identity matrix before drawing
3488 : bool mResetCTM;
3489 :
3490 0 : SnappedImageDrawingParameters()
3491 : : mShouldDraw(false)
3492 0 : , mResetCTM(false)
3493 0 : {}
3494 :
3495 0 : SnappedImageDrawingParameters(const gfxMatrix& aUserSpaceToImageSpace,
3496 : const gfxRect& aFillRect,
3497 : const nsIntRect& aSubimage,
3498 : bool aResetCTM)
3499 : : mUserSpaceToImageSpace(aUserSpaceToImageSpace)
3500 : , mFillRect(aFillRect)
3501 : , mSubimage(aSubimage)
3502 : , mShouldDraw(true)
3503 0 : , mResetCTM(aResetCTM)
3504 0 : {}
3505 : };
3506 :
3507 : /**
3508 : * Given a set of input parameters, compute certain output parameters
3509 : * for drawing an image with the image snapping algorithm.
3510 : * See https://wiki.mozilla.org/Gecko:Image_Snapping_and_Rendering
3511 : *
3512 : * @see nsLayoutUtils::DrawImage() for the descriptions of input parameters
3513 : */
3514 : static SnappedImageDrawingParameters
3515 0 : ComputeSnappedImageDrawingParameters(gfxContext* aCtx,
3516 : PRInt32 aAppUnitsPerDevPixel,
3517 : const nsRect aDest,
3518 : const nsRect aFill,
3519 : const nsPoint aAnchor,
3520 : const nsRect aDirty,
3521 : const nsIntSize aImageSize)
3522 :
3523 : {
3524 0 : if (aDest.IsEmpty() || aFill.IsEmpty())
3525 0 : return SnappedImageDrawingParameters();
3526 :
3527 : gfxRect devPixelDest =
3528 0 : nsLayoutUtils::RectToGfxRect(aDest, aAppUnitsPerDevPixel);
3529 : gfxRect devPixelFill =
3530 0 : nsLayoutUtils::RectToGfxRect(aFill, aAppUnitsPerDevPixel);
3531 : gfxRect devPixelDirty =
3532 0 : nsLayoutUtils::RectToGfxRect(aDirty, aAppUnitsPerDevPixel);
3533 :
3534 0 : bool ignoreScale = false;
3535 : #ifdef MOZ_GFX_OPTIMIZE_MOBILE
3536 : ignoreScale = true;
3537 : #endif
3538 0 : gfxRect fill = devPixelFill;
3539 0 : bool didSnap = aCtx->UserToDevicePixelSnapped(fill, ignoreScale);
3540 :
3541 0 : gfxSize imageSize(aImageSize.width, aImageSize.height);
3542 :
3543 : // Compute the set of pixels that would be sampled by an ideal rendering
3544 : gfxPoint subimageTopLeft =
3545 0 : MapToFloatImagePixels(imageSize, devPixelDest, devPixelFill.TopLeft());
3546 : gfxPoint subimageBottomRight =
3547 0 : MapToFloatImagePixels(imageSize, devPixelDest, devPixelFill.BottomRight());
3548 0 : nsIntRect intSubimage;
3549 : intSubimage.MoveTo(NSToIntFloor(subimageTopLeft.x),
3550 0 : NSToIntFloor(subimageTopLeft.y));
3551 0 : intSubimage.SizeTo(NSToIntCeil(subimageBottomRight.x) - intSubimage.x,
3552 0 : NSToIntCeil(subimageBottomRight.y) - intSubimage.y);
3553 :
3554 : // Compute the anchor point and compute final fill rect.
3555 : // This code assumes that pixel-based devices have one pixel per
3556 : // device unit!
3557 : gfxPoint anchorPoint(gfxFloat(aAnchor.x)/aAppUnitsPerDevPixel,
3558 0 : gfxFloat(aAnchor.y)/aAppUnitsPerDevPixel);
3559 : gfxPoint imageSpaceAnchorPoint =
3560 0 : MapToFloatImagePixels(imageSize, devPixelDest, anchorPoint);
3561 0 : gfxMatrix currentMatrix = aCtx->CurrentMatrix();
3562 :
3563 0 : if (didSnap) {
3564 0 : NS_ASSERTION(!currentMatrix.HasNonAxisAlignedTransform(),
3565 : "How did we snap, then?");
3566 0 : imageSpaceAnchorPoint.Round();
3567 0 : anchorPoint = imageSpaceAnchorPoint;
3568 0 : anchorPoint = MapToFloatUserPixels(imageSize, devPixelDest, anchorPoint);
3569 0 : anchorPoint = currentMatrix.Transform(anchorPoint);
3570 0 : anchorPoint.Round();
3571 :
3572 : // This form of Transform is safe to call since non-axis-aligned
3573 : // transforms wouldn't be snapped.
3574 0 : devPixelDirty = currentMatrix.Transform(devPixelDirty);
3575 : }
3576 :
3577 0 : gfxFloat scaleX = imageSize.width*aAppUnitsPerDevPixel/aDest.width;
3578 0 : gfxFloat scaleY = imageSize.height*aAppUnitsPerDevPixel/aDest.height;
3579 0 : if (didSnap) {
3580 : // We'll reset aCTX to the identity matrix before drawing, so we need to
3581 : // adjust our scales to match.
3582 0 : scaleX /= currentMatrix.xx;
3583 0 : scaleY /= currentMatrix.yy;
3584 : }
3585 0 : gfxFloat translateX = imageSpaceAnchorPoint.x - anchorPoint.x*scaleX;
3586 0 : gfxFloat translateY = imageSpaceAnchorPoint.y - anchorPoint.y*scaleY;
3587 0 : gfxMatrix transform(scaleX, 0, 0, scaleY, translateX, translateY);
3588 :
3589 0 : gfxRect finalFillRect = fill;
3590 : // If the user-space-to-image-space transform is not a straight
3591 : // translation by integers, then filtering will occur, and
3592 : // restricting the fill rect to the dirty rect would change the values
3593 : // computed for edge pixels, which we can't allow.
3594 : // Also, if didSnap is false then rounding out 'devPixelDirty' might not
3595 : // produce pixel-aligned coordinates, which would also break the values
3596 : // computed for edge pixels.
3597 0 : if (didSnap && !transform.HasNonIntegerTranslation()) {
3598 0 : devPixelDirty.RoundOut();
3599 0 : finalFillRect = fill.Intersect(devPixelDirty);
3600 : }
3601 0 : if (finalFillRect.IsEmpty())
3602 0 : return SnappedImageDrawingParameters();
3603 :
3604 : return SnappedImageDrawingParameters(transform, finalFillRect, intSubimage,
3605 0 : didSnap);
3606 : }
3607 :
3608 :
3609 : static nsresult
3610 0 : DrawImageInternal(nsRenderingContext* aRenderingContext,
3611 : imgIContainer* aImage,
3612 : GraphicsFilter aGraphicsFilter,
3613 : const nsRect& aDest,
3614 : const nsRect& aFill,
3615 : const nsPoint& aAnchor,
3616 : const nsRect& aDirty,
3617 : const nsIntSize& aImageSize,
3618 : PRUint32 aImageFlags)
3619 : {
3620 0 : PRInt32 appUnitsPerDevPixel = aRenderingContext->AppUnitsPerDevPixel();
3621 0 : gfxContext* ctx = aRenderingContext->ThebesContext();
3622 :
3623 : SnappedImageDrawingParameters drawingParams =
3624 : ComputeSnappedImageDrawingParameters(ctx, appUnitsPerDevPixel, aDest, aFill,
3625 0 : aAnchor, aDirty, aImageSize);
3626 :
3627 0 : if (!drawingParams.mShouldDraw)
3628 0 : return NS_OK;
3629 :
3630 0 : gfxContextMatrixAutoSaveRestore saveMatrix(ctx);
3631 0 : if (drawingParams.mResetCTM) {
3632 0 : ctx->IdentityMatrix();
3633 : }
3634 :
3635 : aImage->Draw(ctx, aGraphicsFilter, drawingParams.mUserSpaceToImageSpace,
3636 : drawingParams.mFillRect, drawingParams.mSubimage, aImageSize,
3637 0 : aImageFlags);
3638 0 : return NS_OK;
3639 : }
3640 :
3641 : /* static */ void
3642 0 : nsLayoutUtils::DrawPixelSnapped(nsRenderingContext* aRenderingContext,
3643 : gfxDrawable* aDrawable,
3644 : GraphicsFilter aFilter,
3645 : const nsRect& aDest,
3646 : const nsRect& aFill,
3647 : const nsPoint& aAnchor,
3648 : const nsRect& aDirty)
3649 : {
3650 0 : PRInt32 appUnitsPerDevPixel = aRenderingContext->AppUnitsPerDevPixel();
3651 0 : gfxContext* ctx = aRenderingContext->ThebesContext();
3652 0 : gfxIntSize drawableSize = aDrawable->Size();
3653 0 : nsIntSize imageSize(drawableSize.width, drawableSize.height);
3654 :
3655 : SnappedImageDrawingParameters drawingParams =
3656 : ComputeSnappedImageDrawingParameters(ctx, appUnitsPerDevPixel, aDest, aFill,
3657 0 : aAnchor, aDirty, imageSize);
3658 :
3659 0 : if (!drawingParams.mShouldDraw)
3660 0 : return;
3661 :
3662 0 : gfxContextMatrixAutoSaveRestore saveMatrix(ctx);
3663 0 : if (drawingParams.mResetCTM) {
3664 0 : ctx->IdentityMatrix();
3665 : }
3666 :
3667 : gfxRect sourceRect =
3668 0 : drawingParams.mUserSpaceToImageSpace.Transform(drawingParams.mFillRect);
3669 0 : gfxRect imageRect(0, 0, imageSize.width, imageSize.height);
3670 : gfxRect subimage(drawingParams.mSubimage.x, drawingParams.mSubimage.y,
3671 0 : drawingParams.mSubimage.width, drawingParams.mSubimage.height);
3672 :
3673 0 : NS_ASSERTION(!sourceRect.Intersect(subimage).IsEmpty(),
3674 : "We must be allowed to sample *some* source pixels!");
3675 :
3676 : gfxUtils::DrawPixelSnapped(ctx, aDrawable,
3677 : drawingParams.mUserSpaceToImageSpace, subimage,
3678 : sourceRect, imageRect, drawingParams.mFillRect,
3679 0 : gfxASurface::ImageFormatARGB32, aFilter);
3680 : }
3681 :
3682 : /* static */ nsresult
3683 0 : nsLayoutUtils::DrawSingleUnscaledImage(nsRenderingContext* aRenderingContext,
3684 : imgIContainer* aImage,
3685 : GraphicsFilter aGraphicsFilter,
3686 : const nsPoint& aDest,
3687 : const nsRect* aDirty,
3688 : PRUint32 aImageFlags,
3689 : const nsRect* aSourceArea)
3690 : {
3691 0 : nsIntSize imageSize;
3692 0 : aImage->GetWidth(&imageSize.width);
3693 0 : aImage->GetHeight(&imageSize.height);
3694 0 : NS_ENSURE_TRUE(imageSize.width > 0 && imageSize.height > 0, NS_ERROR_FAILURE);
3695 :
3696 0 : nscoord appUnitsPerCSSPixel = nsDeviceContext::AppUnitsPerCSSPixel();
3697 : nsSize size(imageSize.width*appUnitsPerCSSPixel,
3698 0 : imageSize.height*appUnitsPerCSSPixel);
3699 :
3700 0 : nsRect source;
3701 0 : if (aSourceArea) {
3702 0 : source = *aSourceArea;
3703 : } else {
3704 0 : source.SizeTo(size);
3705 : }
3706 :
3707 0 : nsRect dest(aDest - source.TopLeft(), size);
3708 0 : nsRect fill(aDest, source.Size());
3709 : // Ensure that only a single image tile is drawn. If aSourceArea extends
3710 : // outside the image bounds, we want to honor the aSourceArea-to-aDest
3711 : // translation but we don't want to actually tile the image.
3712 0 : fill.IntersectRect(fill, dest);
3713 : return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter,
3714 : dest, fill, aDest, aDirty ? *aDirty : dest,
3715 0 : imageSize, aImageFlags);
3716 : }
3717 :
3718 : /* static */ nsresult
3719 0 : nsLayoutUtils::DrawSingleImage(nsRenderingContext* aRenderingContext,
3720 : imgIContainer* aImage,
3721 : GraphicsFilter aGraphicsFilter,
3722 : const nsRect& aDest,
3723 : const nsRect& aDirty,
3724 : PRUint32 aImageFlags,
3725 : const nsRect* aSourceArea)
3726 : {
3727 0 : nsIntSize imageSize;
3728 0 : if (aImage->GetType() == imgIContainer::TYPE_VECTOR) {
3729 0 : imageSize.width = nsPresContext::AppUnitsToIntCSSPixels(aDest.width);
3730 0 : imageSize.height = nsPresContext::AppUnitsToIntCSSPixels(aDest.height);
3731 : } else {
3732 0 : aImage->GetWidth(&imageSize.width);
3733 0 : aImage->GetHeight(&imageSize.height);
3734 : }
3735 0 : NS_ENSURE_TRUE(imageSize.width > 0 && imageSize.height > 0, NS_ERROR_FAILURE);
3736 :
3737 0 : nsRect source;
3738 0 : if (aSourceArea) {
3739 0 : source = *aSourceArea;
3740 : } else {
3741 0 : nscoord appUnitsPerCSSPixel = nsDeviceContext::AppUnitsPerCSSPixel();
3742 : source.SizeTo(imageSize.width*appUnitsPerCSSPixel,
3743 0 : imageSize.height*appUnitsPerCSSPixel);
3744 : }
3745 :
3746 : nsRect dest = nsLayoutUtils::GetWholeImageDestination(imageSize, source,
3747 0 : aDest);
3748 : // Ensure that only a single image tile is drawn. If aSourceArea extends
3749 : // outside the image bounds, we want to honor the aSourceArea-to-aDest
3750 : // transform but we don't want to actually tile the image.
3751 0 : nsRect fill;
3752 0 : fill.IntersectRect(aDest, dest);
3753 : return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter, dest, fill,
3754 0 : fill.TopLeft(), aDirty, imageSize, aImageFlags);
3755 : }
3756 :
3757 : /* static */ void
3758 0 : nsLayoutUtils::ComputeSizeForDrawing(imgIContainer *aImage,
3759 : nsIntSize& aImageSize, /*outparam*/
3760 : nsSize& aIntrinsicRatio, /*outparam*/
3761 : bool& aGotWidth, /*outparam*/
3762 : bool& aGotHeight /*outparam*/)
3763 : {
3764 0 : aGotWidth = NS_SUCCEEDED(aImage->GetWidth(&aImageSize.width));
3765 0 : aGotHeight = NS_SUCCEEDED(aImage->GetHeight(&aImageSize.height));
3766 :
3767 0 : if (aGotWidth && aGotHeight) {
3768 0 : aIntrinsicRatio = nsSize(aImageSize.width, aImageSize.height);
3769 0 : return;
3770 : }
3771 :
3772 : // If we failed to get width or height, we either have a vector image and
3773 : // should return its intrinsic ratio, or we hit an error (say, because the
3774 : // image failed to load or couldn't be decoded) and should return zero size.
3775 0 : if (nsIFrame* rootFrame = aImage->GetRootLayoutFrame()) {
3776 0 : aIntrinsicRatio = rootFrame->GetIntrinsicRatio();
3777 : } else {
3778 0 : aGotWidth = aGotHeight = true;
3779 0 : aImageSize = nsIntSize(0, 0);
3780 0 : aIntrinsicRatio = nsSize(0, 0);
3781 : }
3782 : }
3783 :
3784 :
3785 : /* static */ nsresult
3786 0 : nsLayoutUtils::DrawBackgroundImage(nsRenderingContext* aRenderingContext,
3787 : imgIContainer* aImage,
3788 : const nsIntSize& aImageSize,
3789 : GraphicsFilter aGraphicsFilter,
3790 : const nsRect& aDest,
3791 : const nsRect& aFill,
3792 : const nsPoint& aAnchor,
3793 : const nsRect& aDirty,
3794 : PRUint32 aImageFlags)
3795 : {
3796 0 : SAMPLE_LABEL("layout", "nsLayoutUtils::DrawBackgroundImage");
3797 : return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter,
3798 : aDest, aFill, aAnchor, aDirty,
3799 0 : aImageSize, aImageFlags);
3800 : }
3801 :
3802 : /* static */ nsresult
3803 0 : nsLayoutUtils::DrawImage(nsRenderingContext* aRenderingContext,
3804 : imgIContainer* aImage,
3805 : GraphicsFilter aGraphicsFilter,
3806 : const nsRect& aDest,
3807 : const nsRect& aFill,
3808 : const nsPoint& aAnchor,
3809 : const nsRect& aDirty,
3810 : PRUint32 aImageFlags)
3811 : {
3812 0 : nsIntSize imageSize;
3813 0 : nsSize imageRatio;
3814 : bool gotHeight, gotWidth;
3815 0 : ComputeSizeForDrawing(aImage, imageSize, imageRatio, gotWidth, gotHeight);
3816 :
3817 : // XXX Dimensionless images shouldn't fall back to filled-area size -- the
3818 : // caller should provide the image size, a la DrawBackgroundImage.
3819 0 : if (gotWidth != gotHeight) {
3820 0 : if (!gotWidth) {
3821 0 : if (imageRatio.height != 0) {
3822 : imageSize.width =
3823 : NSCoordSaturatingNonnegativeMultiply(imageSize.height,
3824 : float(imageRatio.width) /
3825 0 : float(imageRatio.height));
3826 0 : gotWidth = true;
3827 : }
3828 : } else {
3829 0 : if (imageRatio.width != 0) {
3830 : imageSize.height =
3831 : NSCoordSaturatingNonnegativeMultiply(imageSize.width,
3832 : float(imageRatio.height) /
3833 0 : float(imageRatio.width));
3834 0 : gotHeight = true;
3835 : }
3836 : }
3837 : }
3838 :
3839 0 : if (!gotWidth) {
3840 0 : imageSize.width = nsPresContext::AppUnitsToIntCSSPixels(aFill.width);
3841 : }
3842 0 : if (!gotHeight) {
3843 0 : imageSize.height = nsPresContext::AppUnitsToIntCSSPixels(aFill.height);
3844 : }
3845 :
3846 : return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter,
3847 : aDest, aFill, aAnchor, aDirty,
3848 0 : imageSize, aImageFlags);
3849 : }
3850 :
3851 : /* static */ nsRect
3852 0 : nsLayoutUtils::GetWholeImageDestination(const nsIntSize& aWholeImageSize,
3853 : const nsRect& aImageSourceArea,
3854 : const nsRect& aDestArea)
3855 : {
3856 0 : double scaleX = double(aDestArea.width)/aImageSourceArea.width;
3857 0 : double scaleY = double(aDestArea.height)/aImageSourceArea.height;
3858 0 : nscoord destOffsetX = NSToCoordRound(aImageSourceArea.x*scaleX);
3859 0 : nscoord destOffsetY = NSToCoordRound(aImageSourceArea.y*scaleY);
3860 0 : nscoord appUnitsPerCSSPixel = nsDeviceContext::AppUnitsPerCSSPixel();
3861 0 : nscoord wholeSizeX = NSToCoordRound(aWholeImageSize.width*appUnitsPerCSSPixel*scaleX);
3862 0 : nscoord wholeSizeY = NSToCoordRound(aWholeImageSize.height*appUnitsPerCSSPixel*scaleY);
3863 : return nsRect(aDestArea.TopLeft() - nsPoint(destOffsetX, destOffsetY),
3864 0 : nsSize(wholeSizeX, wholeSizeY));
3865 : }
3866 :
3867 0 : static bool NonZeroStyleCoord(const nsStyleCoord& aCoord)
3868 : {
3869 0 : if (aCoord.IsCoordPercentCalcUnit()) {
3870 : // Since negative results are clamped to 0, check > 0.
3871 0 : return nsRuleNode::ComputeCoordPercentCalc(aCoord, nscoord_MAX) > 0 ||
3872 0 : nsRuleNode::ComputeCoordPercentCalc(aCoord, 0) > 0;
3873 : }
3874 :
3875 0 : return true;
3876 : }
3877 :
3878 : /* static */ bool
3879 0 : nsLayoutUtils::HasNonZeroCorner(const nsStyleCorners& aCorners)
3880 : {
3881 0 : NS_FOR_CSS_HALF_CORNERS(corner) {
3882 0 : if (NonZeroStyleCoord(aCorners.Get(corner)))
3883 0 : return true;
3884 : }
3885 0 : return false;
3886 : }
3887 :
3888 : // aCorner is a "full corner" value, i.e. NS_CORNER_TOP_LEFT etc
3889 0 : static bool IsCornerAdjacentToSide(PRUint8 aCorner, mozilla::css::Side aSide)
3890 : {
3891 : PR_STATIC_ASSERT((int)NS_SIDE_TOP == NS_CORNER_TOP_LEFT);
3892 : PR_STATIC_ASSERT((int)NS_SIDE_RIGHT == NS_CORNER_TOP_RIGHT);
3893 : PR_STATIC_ASSERT((int)NS_SIDE_BOTTOM == NS_CORNER_BOTTOM_RIGHT);
3894 : PR_STATIC_ASSERT((int)NS_SIDE_LEFT == NS_CORNER_BOTTOM_LEFT);
3895 : PR_STATIC_ASSERT((int)NS_SIDE_TOP == ((NS_CORNER_TOP_RIGHT - 1)&3));
3896 : PR_STATIC_ASSERT((int)NS_SIDE_RIGHT == ((NS_CORNER_BOTTOM_RIGHT - 1)&3));
3897 : PR_STATIC_ASSERT((int)NS_SIDE_BOTTOM == ((NS_CORNER_BOTTOM_LEFT - 1)&3));
3898 : PR_STATIC_ASSERT((int)NS_SIDE_LEFT == ((NS_CORNER_TOP_LEFT - 1)&3));
3899 :
3900 0 : return aSide == aCorner || aSide == ((aCorner - 1)&3);
3901 : }
3902 :
3903 : /* static */ bool
3904 0 : nsLayoutUtils::HasNonZeroCornerOnSide(const nsStyleCorners& aCorners,
3905 : mozilla::css::Side aSide)
3906 : {
3907 : PR_STATIC_ASSERT(NS_CORNER_TOP_LEFT_X/2 == NS_CORNER_TOP_LEFT);
3908 : PR_STATIC_ASSERT(NS_CORNER_TOP_LEFT_Y/2 == NS_CORNER_TOP_LEFT);
3909 : PR_STATIC_ASSERT(NS_CORNER_TOP_RIGHT_X/2 == NS_CORNER_TOP_RIGHT);
3910 : PR_STATIC_ASSERT(NS_CORNER_TOP_RIGHT_Y/2 == NS_CORNER_TOP_RIGHT);
3911 : PR_STATIC_ASSERT(NS_CORNER_BOTTOM_RIGHT_X/2 == NS_CORNER_BOTTOM_RIGHT);
3912 : PR_STATIC_ASSERT(NS_CORNER_BOTTOM_RIGHT_Y/2 == NS_CORNER_BOTTOM_RIGHT);
3913 : PR_STATIC_ASSERT(NS_CORNER_BOTTOM_LEFT_X/2 == NS_CORNER_BOTTOM_LEFT);
3914 : PR_STATIC_ASSERT(NS_CORNER_BOTTOM_LEFT_Y/2 == NS_CORNER_BOTTOM_LEFT);
3915 :
3916 0 : NS_FOR_CSS_HALF_CORNERS(corner) {
3917 : // corner is a "half corner" value, so dividing by two gives us a
3918 : // "full corner" value.
3919 0 : if (NonZeroStyleCoord(aCorners.Get(corner)) &&
3920 0 : IsCornerAdjacentToSide(corner/2, aSide))
3921 0 : return true;
3922 : }
3923 0 : return false;
3924 : }
3925 :
3926 : /* static */ nsTransparencyMode
3927 0 : nsLayoutUtils::GetFrameTransparency(nsIFrame* aBackgroundFrame,
3928 : nsIFrame* aCSSRootFrame) {
3929 0 : if (aCSSRootFrame->GetStyleContext()->GetStyleDisplay()->mOpacity < 1.0f)
3930 0 : return eTransparencyTransparent;
3931 :
3932 0 : if (HasNonZeroCorner(aCSSRootFrame->GetStyleContext()->GetStyleBorder()->mBorderRadius))
3933 0 : return eTransparencyTransparent;
3934 :
3935 0 : if (aCSSRootFrame->GetStyleDisplay()->mAppearance == NS_THEME_WIN_GLASS)
3936 0 : return eTransparencyGlass;
3937 :
3938 0 : if (aCSSRootFrame->GetStyleDisplay()->mAppearance == NS_THEME_WIN_BORDERLESS_GLASS)
3939 0 : return eTransparencyBorderlessGlass;
3940 :
3941 : nsITheme::Transparency transparency;
3942 0 : if (aCSSRootFrame->IsThemed(&transparency))
3943 : return transparency == nsITheme::eTransparent
3944 : ? eTransparencyTransparent
3945 0 : : eTransparencyOpaque;
3946 :
3947 : // We need an uninitialized window to be treated as opaque because
3948 : // doing otherwise breaks window display effects on some platforms,
3949 : // specifically Vista. (bug 450322)
3950 0 : if (aBackgroundFrame->GetType() == nsGkAtoms::viewportFrame &&
3951 0 : !aBackgroundFrame->GetFirstPrincipalChild()) {
3952 0 : return eTransparencyOpaque;
3953 : }
3954 :
3955 : nsStyleContext* bgSC;
3956 0 : if (!nsCSSRendering::FindBackground(aBackgroundFrame->PresContext(),
3957 0 : aBackgroundFrame, &bgSC)) {
3958 0 : return eTransparencyTransparent;
3959 : }
3960 0 : const nsStyleBackground* bg = bgSC->GetStyleBackground();
3961 0 : if (NS_GET_A(bg->mBackgroundColor) < 255 ||
3962 : // bottom layer's clip is used for the color
3963 0 : bg->BottomLayer().mClip != NS_STYLE_BG_CLIP_BORDER)
3964 0 : return eTransparencyTransparent;
3965 0 : return eTransparencyOpaque;
3966 : }
3967 :
3968 0 : static bool IsPopupFrame(nsIFrame* aFrame)
3969 : {
3970 : // aFrame is a popup it's the list control frame dropdown for a combobox.
3971 0 : nsIAtom* frameType = aFrame->GetType();
3972 0 : if (frameType == nsGkAtoms::listControlFrame) {
3973 0 : nsListControlFrame* lcf = static_cast<nsListControlFrame*>(aFrame);
3974 0 : return lcf->IsInDropDownMode();
3975 : }
3976 :
3977 : // ... or if it's a XUL menupopup frame.
3978 0 : return frameType == nsGkAtoms::menuPopupFrame;
3979 : }
3980 :
3981 : /* static */ bool
3982 0 : nsLayoutUtils::IsPopup(nsIFrame* aFrame)
3983 : {
3984 : // Optimization: the frame can't possibly be a popup if it has no view.
3985 0 : if (!aFrame->HasView()) {
3986 0 : NS_ASSERTION(!IsPopupFrame(aFrame), "popup frame must have a view");
3987 0 : return false;
3988 : }
3989 0 : return IsPopupFrame(aFrame);
3990 : }
3991 :
3992 : /* static */ nsIFrame*
3993 0 : nsLayoutUtils::GetDisplayRootFrame(nsIFrame* aFrame)
3994 : {
3995 0 : nsIFrame* f = aFrame;
3996 0 : for (;;) {
3997 0 : if (IsPopup(f))
3998 0 : return f;
3999 0 : nsIFrame* parent = GetCrossDocParentFrame(f);
4000 0 : if (!parent)
4001 0 : return f;
4002 0 : f = parent;
4003 : }
4004 : }
4005 :
4006 : static bool
4007 0 : IsNonzeroCoord(const nsStyleCoord& aCoord)
4008 : {
4009 0 : if (eStyleUnit_Coord == aCoord.GetUnit())
4010 0 : return aCoord.GetCoordValue() != 0;
4011 0 : return false;
4012 : }
4013 :
4014 : /* static */ PRUint32
4015 0 : nsLayoutUtils::GetTextRunFlagsForStyle(nsStyleContext* aStyleContext,
4016 : const nsStyleText* aStyleText,
4017 : const nsStyleFont* aStyleFont)
4018 : {
4019 0 : PRUint32 result = 0;
4020 0 : if (IsNonzeroCoord(aStyleText->mLetterSpacing)) {
4021 0 : result |= gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES;
4022 : }
4023 0 : switch (aStyleContext->GetStyleSVG()->mTextRendering) {
4024 : case NS_STYLE_TEXT_RENDERING_OPTIMIZESPEED:
4025 0 : result |= gfxTextRunFactory::TEXT_OPTIMIZE_SPEED;
4026 0 : break;
4027 : case NS_STYLE_TEXT_RENDERING_AUTO:
4028 0 : if (aStyleFont->mFont.size <
4029 0 : aStyleContext->PresContext()->GetAutoQualityMinFontSize()) {
4030 0 : result |= gfxTextRunFactory::TEXT_OPTIMIZE_SPEED;
4031 : }
4032 0 : break;
4033 : default:
4034 0 : break;
4035 : }
4036 0 : return result;
4037 : }
4038 :
4039 : /* static */ void
4040 0 : nsLayoutUtils::GetRectDifferenceStrips(const nsRect& aR1, const nsRect& aR2,
4041 : nsRect* aHStrip, nsRect* aVStrip) {
4042 0 : NS_ASSERTION(aR1.TopLeft() == aR2.TopLeft(),
4043 : "expected rects at the same position");
4044 0 : nsRect unionRect(aR1.x, aR1.y, NS_MAX(aR1.width, aR2.width),
4045 0 : NS_MAX(aR1.height, aR2.height));
4046 0 : nscoord VStripStart = NS_MIN(aR1.width, aR2.width);
4047 0 : nscoord HStripStart = NS_MIN(aR1.height, aR2.height);
4048 0 : *aVStrip = unionRect;
4049 0 : aVStrip->x += VStripStart;
4050 0 : aVStrip->width -= VStripStart;
4051 0 : *aHStrip = unionRect;
4052 0 : aHStrip->y += HStripStart;
4053 0 : aHStrip->height -= HStripStart;
4054 0 : }
4055 :
4056 : nsDeviceContext*
4057 0 : nsLayoutUtils::GetDeviceContextForScreenInfo(nsIDocShell* aDocShell)
4058 : {
4059 0 : nsCOMPtr<nsIDocShell> docShell = aDocShell;
4060 0 : while (docShell) {
4061 : // Now make sure our size is up to date. That will mean that the device
4062 : // context does the right thing on multi-monitor systems when we return it to
4063 : // the caller. It will also make sure that our prescontext has been created,
4064 : // if we're supposed to have one.
4065 0 : nsCOMPtr<nsPIDOMWindow> win = do_GetInterface(docShell);
4066 0 : if (!win) {
4067 : // No reason to go on
4068 0 : return nsnull;
4069 : }
4070 :
4071 0 : win->EnsureSizeUpToDate();
4072 :
4073 0 : nsRefPtr<nsPresContext> presContext;
4074 0 : docShell->GetPresContext(getter_AddRefs(presContext));
4075 0 : if (presContext) {
4076 0 : nsDeviceContext* context = presContext->DeviceContext();
4077 0 : if (context) {
4078 0 : return context;
4079 : }
4080 : }
4081 :
4082 0 : nsCOMPtr<nsIDocShellTreeItem> curItem = do_QueryInterface(docShell);
4083 0 : nsCOMPtr<nsIDocShellTreeItem> parentItem;
4084 0 : curItem->GetParent(getter_AddRefs(parentItem));
4085 0 : docShell = do_QueryInterface(parentItem);
4086 : }
4087 :
4088 0 : return nsnull;
4089 : }
4090 :
4091 : /* static */ bool
4092 0 : nsLayoutUtils::IsReallyFixedPos(nsIFrame* aFrame)
4093 : {
4094 0 : NS_PRECONDITION(aFrame->GetParent(),
4095 : "IsReallyFixedPos called on frame not in tree");
4096 0 : NS_PRECONDITION(aFrame->GetStyleDisplay()->mPosition ==
4097 : NS_STYLE_POSITION_FIXED,
4098 : "IsReallyFixedPos called on non-'position:fixed' frame");
4099 :
4100 0 : nsIAtom *parentType = aFrame->GetParent()->GetType();
4101 : return parentType == nsGkAtoms::viewportFrame ||
4102 0 : parentType == nsGkAtoms::pageContentFrame;
4103 : }
4104 :
4105 : nsLayoutUtils::SurfaceFromElementResult
4106 0 : nsLayoutUtils::SurfaceFromElement(dom::Element* aElement,
4107 : PRUint32 aSurfaceFlags)
4108 : {
4109 0 : SurfaceFromElementResult result;
4110 : nsresult rv;
4111 :
4112 0 : bool forceCopy = (aSurfaceFlags & SFE_WANT_NEW_SURFACE) != 0;
4113 0 : bool wantImageSurface = (aSurfaceFlags & SFE_WANT_IMAGE_SURFACE) != 0;
4114 :
4115 0 : if (aSurfaceFlags & SFE_NO_PREMULTIPLY_ALPHA) {
4116 0 : forceCopy = true;
4117 0 : wantImageSurface = true;
4118 : }
4119 :
4120 : // If it's a <canvas>, we may be able to just grab its internal surface
4121 0 : if (nsHTMLCanvasElement* canvas = nsHTMLCanvasElement::FromContent(aElement)) {
4122 0 : gfxIntSize size = canvas->GetSize();
4123 :
4124 0 : nsRefPtr<gfxASurface> surf;
4125 :
4126 0 : if (!forceCopy && canvas->CountContexts() == 1) {
4127 0 : nsICanvasRenderingContextInternal *srcCanvas = canvas->GetContextAtIndex(0);
4128 0 : rv = srcCanvas->GetThebesSurface(getter_AddRefs(surf));
4129 :
4130 0 : if (NS_FAILED(rv))
4131 0 : surf = nsnull;
4132 : }
4133 :
4134 0 : if (surf && wantImageSurface && surf->GetType() != gfxASurface::SurfaceTypeImage)
4135 0 : surf = nsnull;
4136 :
4137 0 : if (!surf) {
4138 0 : if (wantImageSurface) {
4139 0 : surf = new gfxImageSurface(size, gfxASurface::ImageFormatARGB32);
4140 : } else {
4141 0 : surf = gfxPlatform::GetPlatform()->CreateOffscreenSurface(size, gfxASurface::CONTENT_COLOR_ALPHA);
4142 : }
4143 :
4144 0 : nsRefPtr<gfxContext> ctx = new gfxContext(surf);
4145 : // XXX shouldn't use the external interface, but maybe we can layerify this
4146 0 : rv = canvas->RenderContextsExternal(ctx, gfxPattern::FILTER_NEAREST);
4147 0 : if (NS_FAILED(rv))
4148 : return result;
4149 : }
4150 :
4151 : // Ensure that any future changes to the canvas trigger proper invalidation,
4152 : // in case this is being used by -moz-element()
4153 0 : canvas->MarkContextClean();
4154 :
4155 0 : if (aSurfaceFlags & SFE_NO_PREMULTIPLY_ALPHA) {
4156 : // we can modify this surface since we force a copy above when
4157 : // when NO_PREMULTIPLY_ALPHA is set
4158 0 : gfxUtils::UnpremultiplyImageSurface(static_cast<gfxImageSurface*>(surf.get()));
4159 : }
4160 :
4161 0 : result.mSurface = surf;
4162 0 : result.mSize = size;
4163 0 : result.mPrincipal = aElement->NodePrincipal();
4164 0 : result.mIsWriteOnly = canvas->IsWriteOnly();
4165 :
4166 : return result;
4167 : }
4168 :
4169 : #ifdef MOZ_MEDIA
4170 : // Maybe it's <video>?
4171 0 : if (nsHTMLVideoElement* video = nsHTMLVideoElement::FromContent(aElement)) {
4172 : PRUint16 readyState;
4173 0 : if (NS_SUCCEEDED(video->GetReadyState(&readyState)) &&
4174 : (readyState == nsIDOMHTMLMediaElement::HAVE_NOTHING ||
4175 : readyState == nsIDOMHTMLMediaElement::HAVE_METADATA)) {
4176 0 : result.mIsStillLoading = true;
4177 0 : return result;
4178 : }
4179 :
4180 : // If it doesn't have a principal, just bail
4181 0 : nsCOMPtr<nsIPrincipal> principal = video->GetCurrentPrincipal();
4182 0 : if (!principal)
4183 : return result;
4184 :
4185 0 : ImageContainer *container = video->GetImageContainer();
4186 0 : if (!container)
4187 : return result;
4188 :
4189 0 : gfxIntSize size;
4190 0 : nsRefPtr<gfxASurface> surf = container->GetCurrentAsSurface(&size);
4191 0 : if (!surf)
4192 : return result;
4193 :
4194 0 : if (wantImageSurface && surf->GetType() != gfxASurface::SurfaceTypeImage) {
4195 : nsRefPtr<gfxImageSurface> imgSurf =
4196 0 : new gfxImageSurface(size, gfxASurface::ImageFormatARGB32);
4197 :
4198 0 : nsRefPtr<gfxContext> ctx = new gfxContext(imgSurf);
4199 0 : ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
4200 0 : ctx->DrawSurface(surf, size);
4201 0 : surf = imgSurf;
4202 : }
4203 :
4204 0 : result.mCORSUsed = video->GetCORSMode() != CORS_NONE;
4205 0 : result.mSurface = surf;
4206 0 : result.mSize = size;
4207 0 : result.mPrincipal = principal.forget();
4208 0 : result.mIsWriteOnly = false;
4209 :
4210 : return result;
4211 : }
4212 : #endif
4213 :
4214 : // Finally, check if it's a normal image
4215 0 : nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aElement);
4216 :
4217 0 : if (!imageLoader)
4218 : return result;
4219 :
4220 : // Push a null JSContext on the stack so that code that runs within
4221 : // the below code doesn't think it's being called by JS. See bug
4222 : // 604262.
4223 0 : nsCxPusher pusher;
4224 0 : pusher.PushNull();
4225 :
4226 0 : nsCOMPtr<imgIRequest> imgRequest;
4227 0 : rv = imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
4228 0 : getter_AddRefs(imgRequest));
4229 0 : if (NS_FAILED(rv) || !imgRequest)
4230 : return result;
4231 :
4232 : PRUint32 status;
4233 0 : imgRequest->GetImageStatus(&status);
4234 0 : if ((status & imgIRequest::STATUS_LOAD_COMPLETE) == 0) {
4235 : // Spec says to use GetComplete, but that only works on
4236 : // nsIDOMHTMLImageElement, and we support all sorts of other stuff
4237 : // here. Do this for now pending spec clarification.
4238 0 : result.mIsStillLoading = (status & imgIRequest::STATUS_ERROR) == 0;
4239 : return result;
4240 : }
4241 :
4242 0 : nsCOMPtr<nsIPrincipal> principal;
4243 0 : rv = imgRequest->GetImagePrincipal(getter_AddRefs(principal));
4244 0 : if (NS_FAILED(rv) || !principal)
4245 : return result;
4246 :
4247 0 : nsCOMPtr<imgIContainer> imgContainer;
4248 0 : rv = imgRequest->GetImage(getter_AddRefs(imgContainer));
4249 0 : if (NS_FAILED(rv) || !imgContainer)
4250 : return result;
4251 :
4252 : PRUint32 whichFrame = (aSurfaceFlags & SFE_WANT_FIRST_FRAME)
4253 : ? (PRUint32) imgIContainer::FRAME_FIRST
4254 0 : : (PRUint32) imgIContainer::FRAME_CURRENT;
4255 0 : PRUint32 frameFlags = imgIContainer::FLAG_SYNC_DECODE;
4256 0 : if (aSurfaceFlags & SFE_NO_COLORSPACE_CONVERSION)
4257 0 : frameFlags |= imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION;
4258 0 : if (aSurfaceFlags & SFE_NO_PREMULTIPLY_ALPHA)
4259 0 : frameFlags |= imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
4260 0 : nsRefPtr<gfxASurface> framesurf;
4261 0 : rv = imgContainer->GetFrame(whichFrame,
4262 : frameFlags,
4263 0 : getter_AddRefs(framesurf));
4264 0 : if (NS_FAILED(rv))
4265 : return result;
4266 :
4267 : PRInt32 imgWidth, imgHeight;
4268 0 : rv = imgContainer->GetWidth(&imgWidth);
4269 0 : rv |= imgContainer->GetHeight(&imgHeight);
4270 0 : if (NS_FAILED(rv))
4271 : return result;
4272 :
4273 0 : if (wantImageSurface && framesurf->GetType() != gfxASurface::SurfaceTypeImage) {
4274 0 : forceCopy = true;
4275 : }
4276 :
4277 0 : nsRefPtr<gfxASurface> gfxsurf = framesurf;
4278 0 : if (forceCopy) {
4279 0 : if (wantImageSurface) {
4280 0 : gfxsurf = new gfxImageSurface (gfxIntSize(imgWidth, imgHeight), gfxASurface::ImageFormatARGB32);
4281 : } else {
4282 0 : gfxsurf = gfxPlatform::GetPlatform()->CreateOffscreenSurface(gfxIntSize(imgWidth, imgHeight),
4283 0 : gfxASurface::CONTENT_COLOR_ALPHA);
4284 : }
4285 :
4286 0 : nsRefPtr<gfxContext> ctx = new gfxContext(gfxsurf);
4287 :
4288 0 : ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
4289 0 : ctx->SetSource(framesurf);
4290 0 : ctx->Paint();
4291 : }
4292 :
4293 : PRInt32 corsmode;
4294 0 : if (NS_SUCCEEDED(imgRequest->GetCORSMode(&corsmode))) {
4295 0 : result.mCORSUsed = (corsmode != imgIRequest::CORS_NONE);
4296 : }
4297 :
4298 0 : result.mSurface = gfxsurf;
4299 0 : result.mSize = gfxIntSize(imgWidth, imgHeight);
4300 0 : result.mPrincipal = principal.forget();
4301 : // no images, including SVG images, can load content from another domain.
4302 0 : result.mIsWriteOnly = false;
4303 0 : result.mImageRequest = imgRequest.forget();
4304 :
4305 : return result;
4306 : }
4307 :
4308 : /* static */
4309 : nsIContent*
4310 0 : nsLayoutUtils::GetEditableRootContentByContentEditable(nsIDocument* aDocument)
4311 : {
4312 : // If the document is in designMode we should return NULL.
4313 0 : if (!aDocument || aDocument->HasFlag(NODE_IS_EDITABLE)) {
4314 0 : return nsnull;
4315 : }
4316 :
4317 : // contenteditable only works with HTML document.
4318 : // Note: Use nsIDOMHTMLDocument rather than nsIHTMLDocument for getting the
4319 : // body node because nsIDOMHTMLDocument::GetBody() does something
4320 : // additional work for some cases and nsEditor uses them.
4321 0 : nsCOMPtr<nsIDOMHTMLDocument> domHTMLDoc = do_QueryInterface(aDocument);
4322 0 : if (!domHTMLDoc) {
4323 0 : return nsnull;
4324 : }
4325 :
4326 0 : Element* rootElement = aDocument->GetRootElement();
4327 0 : if (rootElement && rootElement->IsEditable()) {
4328 0 : return rootElement;
4329 : }
4330 :
4331 : // If there are no editable root element, check its <body> element.
4332 : // Note that the body element could be <frameset> element.
4333 0 : nsCOMPtr<nsIDOMHTMLElement> body;
4334 0 : nsresult rv = domHTMLDoc->GetBody(getter_AddRefs(body));
4335 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(body);
4336 0 : if (NS_SUCCEEDED(rv) && content && content->IsEditable()) {
4337 0 : return content;
4338 : }
4339 0 : return nsnull;
4340 : }
4341 :
4342 : #ifdef DEBUG
4343 : /* static */ void
4344 0 : nsLayoutUtils::AssertNoDuplicateContinuations(nsIFrame* aContainer,
4345 : const nsFrameList& aFrameList)
4346 : {
4347 0 : for (nsIFrame* f = aFrameList.FirstChild(); f ; f = f->GetNextSibling()) {
4348 : // Check only later continuations of f; we deal with checking the
4349 : // earlier continuations when we hit those earlier continuations in
4350 : // the frame list.
4351 0 : for (nsIFrame *c = f; (c = c->GetNextInFlow());) {
4352 0 : NS_ASSERTION(c->GetParent() != aContainer ||
4353 : !aFrameList.ContainsFrame(c),
4354 : "Two continuations of the same frame in the same "
4355 : "frame list");
4356 : }
4357 : }
4358 0 : }
4359 :
4360 : // Is one of aFrame's ancestors a letter frame?
4361 : static bool
4362 0 : IsInLetterFrame(nsIFrame *aFrame)
4363 : {
4364 0 : for (nsIFrame *f = aFrame->GetParent(); f; f = f->GetParent()) {
4365 0 : if (f->GetType() == nsGkAtoms::letterFrame) {
4366 0 : return true;
4367 : }
4368 : }
4369 0 : return false;
4370 : }
4371 :
4372 : /* static */ void
4373 0 : nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(nsIFrame *aSubtreeRoot)
4374 : {
4375 0 : NS_ASSERTION(aSubtreeRoot->GetPrevInFlow(),
4376 : "frame tree not empty, but caller reported complete status");
4377 :
4378 : // Also assert that text frames map no text.
4379 : PRInt32 start, end;
4380 0 : nsresult rv = aSubtreeRoot->GetOffsets(start, end);
4381 0 : NS_ASSERTION(NS_SUCCEEDED(rv), "GetOffsets failed");
4382 : // In some cases involving :first-letter, we'll partially unlink a
4383 : // continuation in the middle of a continuation chain from its
4384 : // previous and next continuations before destroying it, presumably so
4385 : // that we don't also destroy the later continuations. Once we've
4386 : // done this, GetOffsets returns incorrect values.
4387 : // For examples, see list of tests in
4388 : // https://bugzilla.mozilla.org/show_bug.cgi?id=619021#c29
4389 0 : NS_ASSERTION(start == end || IsInLetterFrame(aSubtreeRoot),
4390 : "frame tree not empty, but caller reported complete status");
4391 :
4392 0 : nsIFrame::ChildListIterator lists(aSubtreeRoot);
4393 0 : for (; !lists.IsDone(); lists.Next()) {
4394 0 : nsFrameList::Enumerator childFrames(lists.CurrentList());
4395 0 : for (; !childFrames.AtEnd(); childFrames.Next()) {
4396 0 : nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(childFrames.get());
4397 : }
4398 : }
4399 0 : }
4400 : #endif
4401 :
4402 : /* static */
4403 : nsresult
4404 0 : nsLayoutUtils::GetFontFacesForFrames(nsIFrame* aFrame,
4405 : nsFontFaceList* aFontFaceList)
4406 : {
4407 0 : NS_PRECONDITION(aFrame, "NULL frame pointer");
4408 :
4409 0 : if (aFrame->GetType() == nsGkAtoms::textFrame) {
4410 : return GetFontFacesForText(aFrame, 0, PR_INT32_MAX, false,
4411 0 : aFontFaceList);
4412 : }
4413 :
4414 0 : while (aFrame) {
4415 : nsIFrame::ChildListID childLists[] = { nsIFrame::kPrincipalList,
4416 0 : nsIFrame::kPopupList };
4417 0 : for (size_t i = 0; i < ArrayLength(childLists); ++i) {
4418 0 : nsFrameList children(aFrame->GetChildList(childLists[i]));
4419 0 : for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
4420 0 : nsIFrame* child = e.get();
4421 0 : if (child->GetPrevContinuation()) {
4422 0 : continue;
4423 : }
4424 0 : child = nsPlaceholderFrame::GetRealFrameFor(child);
4425 0 : nsresult rv = GetFontFacesForFrames(child, aFontFaceList);
4426 0 : NS_ENSURE_SUCCESS(rv, rv);
4427 : }
4428 : }
4429 0 : aFrame = GetNextContinuationOrSpecialSibling(aFrame);
4430 : }
4431 :
4432 0 : return NS_OK;
4433 : }
4434 :
4435 : /* static */
4436 : nsresult
4437 0 : nsLayoutUtils::GetFontFacesForText(nsIFrame* aFrame,
4438 : PRInt32 aStartOffset, PRInt32 aEndOffset,
4439 : bool aFollowContinuations,
4440 : nsFontFaceList* aFontFaceList)
4441 : {
4442 0 : NS_PRECONDITION(aFrame, "NULL frame pointer");
4443 :
4444 0 : if (aFrame->GetType() != nsGkAtoms::textFrame) {
4445 0 : return NS_OK;
4446 : }
4447 :
4448 0 : nsTextFrame* curr = static_cast<nsTextFrame*>(aFrame);
4449 0 : do {
4450 0 : PRInt32 fstart = NS_MAX(curr->GetContentOffset(), aStartOffset);
4451 0 : PRInt32 fend = NS_MIN(curr->GetContentEnd(), aEndOffset);
4452 0 : if (fstart >= fend) {
4453 0 : continue;
4454 : }
4455 :
4456 : // overlapping with the offset we want
4457 0 : gfxSkipCharsIterator iter = curr->EnsureTextRun(nsTextFrame::eInflated);
4458 0 : gfxTextRun* textRun = curr->GetTextRun(nsTextFrame::eInflated);
4459 0 : NS_ENSURE_TRUE(textRun, NS_ERROR_OUT_OF_MEMORY);
4460 :
4461 0 : PRUint32 skipStart = iter.ConvertOriginalToSkipped(fstart);
4462 0 : PRUint32 skipEnd = iter.ConvertOriginalToSkipped(fend);
4463 : aFontFaceList->AddFontsFromTextRun(textRun,
4464 : skipStart,
4465 : skipEnd - skipStart,
4466 0 : curr);
4467 : } while (aFollowContinuations &&
4468 0 : (curr = static_cast<nsTextFrame*>(curr->GetNextContinuation())));
4469 :
4470 0 : return NS_OK;
4471 : }
4472 :
4473 : /* static */
4474 : size_t
4475 0 : nsLayoutUtils::SizeOfTextRunsForFrames(nsIFrame* aFrame,
4476 : nsMallocSizeOfFun aMallocSizeOf,
4477 : bool clear)
4478 : {
4479 0 : NS_PRECONDITION(aFrame, "NULL frame pointer");
4480 :
4481 0 : size_t total = 0;
4482 :
4483 0 : if (aFrame->GetType() == nsGkAtoms::textFrame) {
4484 0 : nsTextFrame* textFrame = static_cast<nsTextFrame*>(aFrame);
4485 0 : for (PRUint32 i = 0; i < 2; ++i) {
4486 : gfxTextRun *run = textFrame->GetTextRun(
4487 0 : (i != 0) ? nsTextFrame::eInflated : nsTextFrame::eNotInflated);
4488 0 : if (run) {
4489 0 : if (clear) {
4490 0 : run->ResetSizeOfAccountingFlags();
4491 : } else {
4492 0 : total += run->MaybeSizeOfIncludingThis(aMallocSizeOf);
4493 : }
4494 : }
4495 : }
4496 0 : return total;
4497 : }
4498 :
4499 0 : nsAutoTArray<nsIFrame::ChildList,4> childListArray;
4500 0 : aFrame->GetChildLists(&childListArray);
4501 :
4502 0 : for (nsIFrame::ChildListArrayIterator childLists(childListArray);
4503 0 : !childLists.IsDone(); childLists.Next()) {
4504 0 : for (nsFrameList::Enumerator e(childLists.CurrentList());
4505 0 : !e.AtEnd(); e.Next()) {
4506 0 : total += SizeOfTextRunsForFrames(e.get(), aMallocSizeOf, clear);
4507 : }
4508 : }
4509 0 : return total;
4510 : }
4511 :
4512 : /* static */
4513 : void
4514 1404 : nsLayoutUtils::Initialize()
4515 : {
4516 : mozilla::Preferences::AddUintVarCache(&sFontSizeInflationEmPerLine,
4517 1404 : "font.size.inflation.emPerLine");
4518 : mozilla::Preferences::AddUintVarCache(&sFontSizeInflationMinTwips,
4519 1404 : "font.size.inflation.minTwips");
4520 1404 : }
4521 :
4522 : /* static */
4523 : void
4524 1403 : nsLayoutUtils::Shutdown()
4525 : {
4526 1403 : if (sContentMap) {
4527 0 : delete sContentMap;
4528 0 : sContentMap = NULL;
4529 : }
4530 1403 : }
4531 :
4532 : /* static */
4533 : void
4534 0 : nsLayoutUtils::RegisterImageRequest(nsPresContext* aPresContext,
4535 : imgIRequest* aRequest,
4536 : bool* aRequestRegistered)
4537 : {
4538 0 : if (!aPresContext) {
4539 0 : return;
4540 : }
4541 :
4542 0 : if (aRequestRegistered && *aRequestRegistered) {
4543 : // Our request is already registered with the refresh driver, so
4544 : // no need to register it again.
4545 0 : return;
4546 : }
4547 :
4548 0 : if (aRequest) {
4549 0 : if (!aPresContext->RefreshDriver()->AddImageRequest(aRequest)) {
4550 0 : NS_WARNING("Unable to add image request");
4551 0 : return;
4552 : }
4553 :
4554 0 : if (aRequestRegistered) {
4555 0 : *aRequestRegistered = true;
4556 : }
4557 : }
4558 : }
4559 :
4560 : /* static */
4561 : void
4562 0 : nsLayoutUtils::RegisterImageRequestIfAnimated(nsPresContext* aPresContext,
4563 : imgIRequest* aRequest,
4564 : bool* aRequestRegistered)
4565 : {
4566 0 : if (!aPresContext) {
4567 0 : return;
4568 : }
4569 :
4570 0 : if (aRequestRegistered && *aRequestRegistered) {
4571 : // Our request is already registered with the refresh driver, so
4572 : // no need to register it again.
4573 0 : return;
4574 : }
4575 :
4576 0 : if (aRequest) {
4577 0 : nsCOMPtr<imgIContainer> image;
4578 0 : aRequest->GetImage(getter_AddRefs(image));
4579 0 : if (image) {
4580 :
4581 : // Check to verify that the image is animated. If so, then add it to the
4582 : // list of images tracked by the refresh driver.
4583 0 : bool isAnimated = false;
4584 0 : nsresult rv = image->GetAnimated(&isAnimated);
4585 0 : if (NS_SUCCEEDED(rv) && isAnimated) {
4586 0 : if (!aPresContext->RefreshDriver()->AddImageRequest(aRequest)) {
4587 0 : NS_WARNING("Unable to add image request");
4588 : return;
4589 : }
4590 :
4591 0 : if (aRequestRegistered) {
4592 0 : *aRequestRegistered = true;
4593 : }
4594 : }
4595 : }
4596 : }
4597 : }
4598 :
4599 : /* static */
4600 : void
4601 0 : nsLayoutUtils::DeregisterImageRequest(nsPresContext* aPresContext,
4602 : imgIRequest* aRequest,
4603 : bool* aRequestRegistered)
4604 : {
4605 0 : if (!aPresContext) {
4606 0 : return;
4607 : }
4608 :
4609 : // Deregister our imgIRequest with the refresh driver to
4610 : // complete tear-down, but only if it has been registered
4611 0 : if (aRequestRegistered && !*aRequestRegistered) {
4612 0 : return;
4613 : }
4614 :
4615 0 : if (aRequest) {
4616 0 : nsCOMPtr<imgIContainer> image;
4617 0 : aRequest->GetImage(getter_AddRefs(image));
4618 0 : if (image) {
4619 0 : aPresContext->RefreshDriver()->RemoveImageRequest(aRequest);
4620 :
4621 0 : if (aRequestRegistered) {
4622 0 : *aRequestRegistered = false;
4623 : }
4624 : }
4625 : }
4626 : }
4627 :
4628 0 : nsSetAttrRunnable::nsSetAttrRunnable(nsIContent* aContent, nsIAtom* aAttrName,
4629 : const nsAString& aValue)
4630 : : mContent(aContent),
4631 : mAttrName(aAttrName),
4632 0 : mValue(aValue)
4633 : {
4634 0 : NS_ASSERTION(aContent && aAttrName, "Missing stuff, prepare to crash");
4635 0 : }
4636 :
4637 0 : nsSetAttrRunnable::nsSetAttrRunnable(nsIContent* aContent, nsIAtom* aAttrName,
4638 : PRInt32 aValue)
4639 : : mContent(aContent),
4640 0 : mAttrName(aAttrName)
4641 : {
4642 0 : NS_ASSERTION(aContent && aAttrName, "Missing stuff, prepare to crash");
4643 0 : mValue.AppendInt(aValue);
4644 0 : }
4645 :
4646 : NS_IMETHODIMP
4647 0 : nsSetAttrRunnable::Run()
4648 : {
4649 0 : return mContent->SetAttr(kNameSpaceID_None, mAttrName, mValue, true);
4650 : }
4651 :
4652 0 : nsUnsetAttrRunnable::nsUnsetAttrRunnable(nsIContent* aContent,
4653 : nsIAtom* aAttrName)
4654 : : mContent(aContent),
4655 0 : mAttrName(aAttrName)
4656 : {
4657 0 : NS_ASSERTION(aContent && aAttrName, "Missing stuff, prepare to crash");
4658 0 : }
4659 :
4660 : NS_IMETHODIMP
4661 0 : nsUnsetAttrRunnable::Run()
4662 : {
4663 0 : return mContent->UnsetAttr(kNameSpaceID_None, mAttrName, true);
4664 : }
4665 :
4666 0 : nsReflowFrameRunnable::nsReflowFrameRunnable(nsIFrame* aFrame,
4667 : nsIPresShell::IntrinsicDirty aIntrinsicDirty,
4668 : nsFrameState aBitToAdd)
4669 : : mWeakFrame(aFrame),
4670 : mIntrinsicDirty(aIntrinsicDirty),
4671 0 : mBitToAdd(aBitToAdd)
4672 : {
4673 0 : }
4674 :
4675 : NS_IMETHODIMP
4676 0 : nsReflowFrameRunnable::Run()
4677 : {
4678 0 : if (mWeakFrame.IsAlive()) {
4679 0 : mWeakFrame->PresContext()->PresShell()->
4680 0 : FrameNeedsReflow(mWeakFrame, mIntrinsicDirty, mBitToAdd);
4681 : }
4682 0 : return NS_OK;
4683 : }
4684 :
4685 : /**
4686 : * Compute the minimum font size inside of a container with the given
4687 : * width, such that **when the user zooms the container to fill the full
4688 : * width of the device**, the fonts satisfy our minima.
4689 : */
4690 : static nscoord
4691 0 : MinimumFontSizeFor(nsPresContext* aPresContext, nscoord aContainerWidth)
4692 : {
4693 0 : if (sFontSizeInflationEmPerLine == 0 && sFontSizeInflationMinTwips == 0) {
4694 0 : return 0;
4695 : }
4696 :
4697 : // Clamp the container width to the device dimensions
4698 0 : nscoord iFrameWidth = aPresContext->GetVisibleArea().width;
4699 0 : nscoord effectiveContainerWidth = NS_MIN(iFrameWidth, aContainerWidth);
4700 :
4701 0 : nscoord byLine = 0, byInch = 0;
4702 0 : if (sFontSizeInflationEmPerLine != 0) {
4703 0 : byLine = effectiveContainerWidth / sFontSizeInflationEmPerLine;
4704 : }
4705 0 : if (sFontSizeInflationMinTwips != 0) {
4706 : // REVIEW: Is this giving us app units and sizes *not* counting
4707 : // viewport scaling?
4708 0 : nsDeviceContext *dx = aPresContext->DeviceContext();
4709 0 : nsRect clientRect;
4710 0 : dx->GetClientRect(clientRect); // FIXME: GetClientRect looks expensive
4711 : float deviceWidthInches =
4712 0 : float(clientRect.width) / float(dx->AppUnitsPerPhysicalInch());
4713 : byInch = NSToCoordRound(effectiveContainerWidth /
4714 : (deviceWidthInches * 1440 /
4715 0 : sFontSizeInflationMinTwips ));
4716 : }
4717 0 : return NS_MAX(byLine, byInch);
4718 : }
4719 :
4720 : /* static */ float
4721 0 : nsLayoutUtils::FontSizeInflationInner(const nsIFrame *aFrame,
4722 : nscoord aMinFontSize)
4723 : {
4724 : // Note that line heights should be inflated by the same ratio as the
4725 : // font size of the same text; thus we operate only on the font size
4726 : // even when we're scaling a line height.
4727 0 : nscoord styleFontSize = aFrame->GetStyleFont()->mFont.size;
4728 0 : if (styleFontSize <= 0) {
4729 : // Never scale zero font size.
4730 0 : return 1.0;
4731 : }
4732 :
4733 0 : if (aMinFontSize <= 0) {
4734 : // No need to scale.
4735 0 : return 1.0;
4736 : }
4737 :
4738 : // Scale everything from 0-1.5 times min to instead fit in the range
4739 : // 1-1.5 times min, so that we still show some distinction rather than
4740 : // just enforcing a minimum.
4741 : // FIXME: Fiddle with this algorithm; maybe have prefs to control it?
4742 0 : float ratio = float(styleFontSize) / float(aMinFontSize);
4743 0 : if (ratio >= 1.5f) {
4744 : // If we're already at 1.5 or more times the minimum, don't scale.
4745 0 : return 1.0;
4746 : }
4747 :
4748 : // To scale 0-1.5 times min to instead be 1-1.5 times min, we want
4749 : // to the desired multiple of min to be 1 + (ratio/3) (where ratio
4750 : // is our input's multiple of min). The scaling needed to produce
4751 : // that is that divided by |ratio|, or:
4752 0 : return (1.0f / ratio) + (1.0f / 3.0f);
4753 : }
4754 :
4755 : static bool
4756 0 : ShouldInflateFontsForContainer(const nsIFrame *aFrame)
4757 : {
4758 : // We only want to inflate fonts for text that is in a place
4759 : // with room to expand. The question is what the best heuristic for
4760 : // that is...
4761 : // For now, we're going to use NS_FRAME_IN_CONSTRAINED_HEIGHT, which
4762 : // indicates whether the frame is inside something with a constrained
4763 : // height (propagating down the tree), but the propagation stops when
4764 : // we hit overflow-y: scroll or auto.
4765 0 : const nsStyleText* styleText = aFrame->GetStyleText();
4766 :
4767 : return styleText->mTextSizeAdjust != NS_STYLE_TEXT_SIZE_ADJUST_NONE &&
4768 0 : !(aFrame->GetStateBits() & NS_FRAME_IN_CONSTRAINED_HEIGHT) &&
4769 : // We also want to disable font inflation for containers that have
4770 : // preformatted text.
4771 0 : styleText->WhiteSpaceCanWrap();
4772 : }
4773 :
4774 : nscoord
4775 0 : nsLayoutUtils::InflationMinFontSizeFor(const nsIFrame *aFrame,
4776 : WidthDetermination aWidthDetermination)
4777 : {
4778 : #ifdef DEBUG
4779 0 : if (aWidthDetermination == eNotInReflow) {
4780 : // Check that neither this frame nor any of its ancestors are
4781 : // currently being reflowed.
4782 : // It's ok for box frames (but not arbitrary ancestors of box frames)
4783 : // since they set their size before reflow.
4784 0 : if (!(aFrame->IsBoxFrame() && IsContainerForFontSizeInflation(aFrame))) {
4785 0 : for (const nsIFrame *f = aFrame; f; f = f->GetParent()) {
4786 0 : NS_ABORT_IF_FALSE(!(f->GetStateBits() & NS_FRAME_IN_REFLOW),
4787 : "must call nsHTMLReflowState& version during reflow");
4788 : }
4789 : }
4790 : // It's ok if frames are dirty, or even if they've never been
4791 : // reflowed, since they will be eventually and then we'll get the
4792 : // right size.
4793 : }
4794 : #endif
4795 :
4796 0 : if (!FontSizeInflationEnabled(aFrame->PresContext())) {
4797 0 : return 0;
4798 : }
4799 :
4800 0 : if (aWidthDetermination == eInReflow) {
4801 0 : nsPresContext *presContext = aFrame->PresContext();
4802 0 : nsIFrame *container = presContext->mCurrentInflationContainer;
4803 0 : if (!container || !ShouldInflateFontsForContainer(container)) {
4804 0 : return 0;
4805 : }
4806 : return MinimumFontSizeFor(presContext,
4807 0 : presContext->mCurrentInflationContainerWidth);
4808 : }
4809 :
4810 0 : for (const nsIFrame *f = aFrame; f; f = f->GetParent()) {
4811 0 : if (IsContainerForFontSizeInflation(f)) {
4812 0 : if (!ShouldInflateFontsForContainer(f)) {
4813 0 : return 0;
4814 : }
4815 :
4816 : return MinimumFontSizeFor(aFrame->PresContext(),
4817 0 : f->GetContentRect().width);
4818 : }
4819 : }
4820 :
4821 0 : NS_ABORT_IF_FALSE(false, "root should always be container");
4822 :
4823 0 : return 0;
4824 : }
4825 :
4826 : float
4827 0 : nsLayoutUtils::FontSizeInflationFor(const nsIFrame *aFrame,
4828 : WidthDetermination aWidthDetermination)
4829 : {
4830 : #ifdef DEBUG
4831 0 : if (aWidthDetermination == eNotInReflow) {
4832 : // Check that neither this frame nor any of its ancestors are
4833 : // currently being reflowed.
4834 : // It's ok for box frames (but not arbitrary ancestors of box frames)
4835 : // since they set their size before reflow.
4836 0 : if (!(aFrame->IsBoxFrame() && IsContainerForFontSizeInflation(aFrame))) {
4837 0 : for (const nsIFrame *f = aFrame; f; f = f->GetParent()) {
4838 0 : NS_ABORT_IF_FALSE(!(f->GetStateBits() & NS_FRAME_IN_REFLOW),
4839 : "must call nsHTMLReflowState& version during reflow");
4840 : }
4841 : }
4842 : // It's ok if frames are dirty, or even if they've never been
4843 : // reflowed, since they will be eventually and then we'll get the
4844 : // right size.
4845 : }
4846 : #endif
4847 :
4848 0 : if (!FontSizeInflationEnabled(aFrame->PresContext())) {
4849 0 : return 1.0;
4850 : }
4851 :
4852 : return FontSizeInflationInner(aFrame,
4853 : InflationMinFontSizeFor(aFrame,
4854 0 : aWidthDetermination));
4855 : }
4856 :
4857 : /* static */ bool
4858 0 : nsLayoutUtils::FontSizeInflationEnabled(nsPresContext *aPresContext)
4859 : {
4860 0 : if ((sFontSizeInflationEmPerLine == 0 &&
4861 : sFontSizeInflationMinTwips == 0) ||
4862 0 : aPresContext->IsChrome()) {
4863 0 : return false;
4864 : }
4865 :
4866 : ViewportInfo vInf =
4867 0 : nsContentUtils::GetViewportInfo(aPresContext->PresShell()->GetDocument());
4868 :
4869 0 : if (vInf.defaultZoom >= 1.0 || vInf.autoSize) {
4870 0 : return false;
4871 : }
4872 :
4873 0 : return true;
4874 4392 : }
|