|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 // vim:cindent:ts=2:et:sw=2: |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 /* base class of all rendering objects */ |
|
8 |
|
9 #include "nsFrame.h" |
|
10 |
|
11 #include "mozilla/Attributes.h" |
|
12 #include "mozilla/DebugOnly.h" |
|
13 |
|
14 #include "nsCOMPtr.h" |
|
15 #include "nsFrameList.h" |
|
16 #include "nsPlaceholderFrame.h" |
|
17 #include "nsIContent.h" |
|
18 #include "nsContentUtils.h" |
|
19 #include "nsIAtom.h" |
|
20 #include "nsString.h" |
|
21 #include "nsReadableUtils.h" |
|
22 #include "nsStyleContext.h" |
|
23 #include "nsTableOuterFrame.h" |
|
24 #include "nsView.h" |
|
25 #include "nsViewManager.h" |
|
26 #include "nsIScrollableFrame.h" |
|
27 #include "nsPresContext.h" |
|
28 #include "nsStyleConsts.h" |
|
29 #include "nsIPresShell.h" |
|
30 #include "prlog.h" |
|
31 #include "prprf.h" |
|
32 #include <stdarg.h> |
|
33 #include "nsFrameManager.h" |
|
34 #include "nsLayoutUtils.h" |
|
35 |
|
36 #include "nsIDOMNode.h" |
|
37 #include "nsISelection.h" |
|
38 #include "nsISelectionPrivate.h" |
|
39 #include "nsFrameSelection.h" |
|
40 #include "nsGkAtoms.h" |
|
41 #include "nsCSSAnonBoxes.h" |
|
42 |
|
43 #include "nsFrameTraversal.h" |
|
44 #include "nsRange.h" |
|
45 #include "nsITextControlFrame.h" |
|
46 #include "nsNameSpaceManager.h" |
|
47 #include "nsIPercentHeightObserver.h" |
|
48 #include "nsStyleStructInlines.h" |
|
49 #include <algorithm> |
|
50 |
|
51 #include "nsBidiPresUtils.h" |
|
52 |
|
53 // For triple-click pref |
|
54 #include "imgIContainer.h" |
|
55 #include "imgIRequest.h" |
|
56 #include "nsError.h" |
|
57 #include "nsContainerFrame.h" |
|
58 #include "nsBoxLayoutState.h" |
|
59 #include "nsBlockFrame.h" |
|
60 #include "nsDisplayList.h" |
|
61 #include "nsSVGIntegrationUtils.h" |
|
62 #include "nsSVGEffects.h" |
|
63 #include "nsChangeHint.h" |
|
64 #include "nsDeckFrame.h" |
|
65 #include "nsSubDocumentFrame.h" |
|
66 #include "SVGTextFrame.h" |
|
67 |
|
68 #include "gfxContext.h" |
|
69 #include "nsRenderingContext.h" |
|
70 #include "nsAbsoluteContainingBlock.h" |
|
71 #include "StickyScrollContainer.h" |
|
72 #include "nsFontInflationData.h" |
|
73 #include "gfxASurface.h" |
|
74 #include "nsRegion.h" |
|
75 #include "nsIFrameInlines.h" |
|
76 |
|
77 #include "mozilla/AsyncEventDispatcher.h" |
|
78 #include "mozilla/EventListenerManager.h" |
|
79 #include "mozilla/EventStateManager.h" |
|
80 #include "mozilla/EventStates.h" |
|
81 #include "mozilla/Preferences.h" |
|
82 #include "mozilla/LookAndFeel.h" |
|
83 #include "mozilla/MouseEvents.h" |
|
84 #include "mozilla/css/ImageLoader.h" |
|
85 #include "mozilla/gfx/Tools.h" |
|
86 #include "nsPrintfCString.h" |
|
87 |
|
88 using namespace mozilla; |
|
89 using namespace mozilla::css; |
|
90 using namespace mozilla::dom; |
|
91 using namespace mozilla::layers; |
|
92 using namespace mozilla::layout; |
|
93 |
|
94 // Struct containing cached metrics for box-wrapped frames. |
|
95 struct nsBoxLayoutMetrics |
|
96 { |
|
97 nsSize mPrefSize; |
|
98 nsSize mMinSize; |
|
99 nsSize mMaxSize; |
|
100 |
|
101 nsSize mBlockMinSize; |
|
102 nsSize mBlockPrefSize; |
|
103 nscoord mBlockAscent; |
|
104 |
|
105 nscoord mFlex; |
|
106 nscoord mAscent; |
|
107 |
|
108 nsSize mLastSize; |
|
109 }; |
|
110 |
|
111 struct nsContentAndOffset |
|
112 { |
|
113 nsIContent* mContent; |
|
114 int32_t mOffset; |
|
115 }; |
|
116 |
|
117 // Some Misc #defines |
|
118 #define SELECTION_DEBUG 0 |
|
119 #define FORCE_SELECTION_UPDATE 1 |
|
120 #define CALC_DEBUG 0 |
|
121 |
|
122 |
|
123 #include "nsILineIterator.h" |
|
124 |
|
125 //non Hack prototypes |
|
126 #if 0 |
|
127 static void RefreshContentFrames(nsPresContext* aPresContext, nsIContent * aStartContent, nsIContent * aEndContent); |
|
128 #endif |
|
129 |
|
130 #include "prenv.h" |
|
131 |
|
132 // Formerly the nsIFrameDebug interface |
|
133 |
|
134 #ifdef DEBUG |
|
135 static bool gShowFrameBorders = false; |
|
136 |
|
137 void nsFrame::ShowFrameBorders(bool aEnable) |
|
138 { |
|
139 gShowFrameBorders = aEnable; |
|
140 } |
|
141 |
|
142 bool nsFrame::GetShowFrameBorders() |
|
143 { |
|
144 return gShowFrameBorders; |
|
145 } |
|
146 |
|
147 static bool gShowEventTargetFrameBorder = false; |
|
148 |
|
149 void nsFrame::ShowEventTargetFrameBorder(bool aEnable) |
|
150 { |
|
151 gShowEventTargetFrameBorder = aEnable; |
|
152 } |
|
153 |
|
154 bool nsFrame::GetShowEventTargetFrameBorder() |
|
155 { |
|
156 return gShowEventTargetFrameBorder; |
|
157 } |
|
158 |
|
159 /** |
|
160 * Note: the log module is created during library initialization which |
|
161 * means that you cannot perform logging before then. |
|
162 */ |
|
163 static PRLogModuleInfo* gLogModule; |
|
164 |
|
165 static PRLogModuleInfo* gStyleVerifyTreeLogModuleInfo; |
|
166 |
|
167 static uint32_t gStyleVerifyTreeEnable = 0x55; |
|
168 |
|
169 bool |
|
170 nsFrame::GetVerifyStyleTreeEnable() |
|
171 { |
|
172 if (gStyleVerifyTreeEnable == 0x55) { |
|
173 if (nullptr == gStyleVerifyTreeLogModuleInfo) { |
|
174 gStyleVerifyTreeLogModuleInfo = PR_NewLogModule("styleverifytree"); |
|
175 gStyleVerifyTreeEnable = 0 != gStyleVerifyTreeLogModuleInfo->level; |
|
176 } |
|
177 } |
|
178 return gStyleVerifyTreeEnable; |
|
179 } |
|
180 |
|
181 void |
|
182 nsFrame::SetVerifyStyleTreeEnable(bool aEnabled) |
|
183 { |
|
184 gStyleVerifyTreeEnable = aEnabled; |
|
185 } |
|
186 |
|
187 PRLogModuleInfo* |
|
188 nsFrame::GetLogModuleInfo() |
|
189 { |
|
190 if (nullptr == gLogModule) { |
|
191 gLogModule = PR_NewLogModule("frame"); |
|
192 } |
|
193 return gLogModule; |
|
194 } |
|
195 |
|
196 #endif |
|
197 |
|
198 static void |
|
199 DestroyAbsoluteContainingBlock(void* aPropertyValue) |
|
200 { |
|
201 delete static_cast<nsAbsoluteContainingBlock*>(aPropertyValue); |
|
202 } |
|
203 |
|
204 NS_DECLARE_FRAME_PROPERTY(AbsoluteContainingBlockProperty, DestroyAbsoluteContainingBlock) |
|
205 |
|
206 bool |
|
207 nsIFrame::HasAbsolutelyPositionedChildren() const { |
|
208 return IsAbsoluteContainer() && GetAbsoluteContainingBlock()->HasAbsoluteFrames(); |
|
209 } |
|
210 |
|
211 nsAbsoluteContainingBlock* |
|
212 nsIFrame::GetAbsoluteContainingBlock() const { |
|
213 NS_ASSERTION(IsAbsoluteContainer(), "The frame is not marked as an abspos container correctly"); |
|
214 nsAbsoluteContainingBlock* absCB = static_cast<nsAbsoluteContainingBlock*> |
|
215 (Properties().Get(AbsoluteContainingBlockProperty())); |
|
216 NS_ASSERTION(absCB, "The frame is marked as an abspos container but doesn't have the property"); |
|
217 return absCB; |
|
218 } |
|
219 |
|
220 void |
|
221 nsIFrame::MarkAsAbsoluteContainingBlock() |
|
222 { |
|
223 MOZ_ASSERT(GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); |
|
224 NS_ASSERTION(!Properties().Get(AbsoluteContainingBlockProperty()), |
|
225 "Already has an abs-pos containing block property?"); |
|
226 NS_ASSERTION(!HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN), |
|
227 "Already has NS_FRAME_HAS_ABSPOS_CHILDREN state bit?"); |
|
228 AddStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN); |
|
229 Properties().Set(AbsoluteContainingBlockProperty(), new nsAbsoluteContainingBlock(GetAbsoluteListID())); |
|
230 } |
|
231 |
|
232 void |
|
233 nsIFrame::MarkAsNotAbsoluteContainingBlock() |
|
234 { |
|
235 NS_ASSERTION(!HasAbsolutelyPositionedChildren(), "Think of the children!"); |
|
236 NS_ASSERTION(Properties().Get(AbsoluteContainingBlockProperty()), |
|
237 "Should have an abs-pos containing block property"); |
|
238 NS_ASSERTION(HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN), |
|
239 "Should have NS_FRAME_HAS_ABSPOS_CHILDREN state bit"); |
|
240 MOZ_ASSERT(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)); |
|
241 RemoveStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN); |
|
242 Properties().Delete(AbsoluteContainingBlockProperty()); |
|
243 } |
|
244 |
|
245 bool |
|
246 nsIFrame::CheckAndClearPaintedState() |
|
247 { |
|
248 bool result = (GetStateBits() & NS_FRAME_PAINTED_THEBES); |
|
249 RemoveStateBits(NS_FRAME_PAINTED_THEBES); |
|
250 |
|
251 nsIFrame::ChildListIterator lists(this); |
|
252 for (; !lists.IsDone(); lists.Next()) { |
|
253 nsFrameList::Enumerator childFrames(lists.CurrentList()); |
|
254 for (; !childFrames.AtEnd(); childFrames.Next()) { |
|
255 nsIFrame* child = childFrames.get(); |
|
256 if (child->CheckAndClearPaintedState()) { |
|
257 result = true; |
|
258 } |
|
259 } |
|
260 } |
|
261 return result; |
|
262 } |
|
263 |
|
264 bool |
|
265 nsIFrame::IsVisibleConsideringAncestors(uint32_t aFlags) const |
|
266 { |
|
267 if (!StyleVisibility()->IsVisible()) { |
|
268 return false; |
|
269 } |
|
270 |
|
271 const nsIFrame* frame = this; |
|
272 while (frame) { |
|
273 nsView* view = frame->GetView(); |
|
274 if (view && view->GetVisibility() == nsViewVisibility_kHide) |
|
275 return false; |
|
276 |
|
277 nsIFrame* parent = frame->GetParent(); |
|
278 nsDeckFrame* deck = do_QueryFrame(parent); |
|
279 if (deck) { |
|
280 if (deck->GetSelectedBox() != frame) |
|
281 return false; |
|
282 } |
|
283 |
|
284 if (parent) { |
|
285 frame = parent; |
|
286 } else { |
|
287 parent = nsLayoutUtils::GetCrossDocParentFrame(frame); |
|
288 if (!parent) |
|
289 break; |
|
290 |
|
291 if ((aFlags & nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) == 0 && |
|
292 parent->PresContext()->IsChrome() && !frame->PresContext()->IsChrome()) { |
|
293 break; |
|
294 } |
|
295 |
|
296 if (!parent->StyleVisibility()->IsVisible()) |
|
297 return false; |
|
298 |
|
299 frame = parent; |
|
300 } |
|
301 } |
|
302 |
|
303 return true; |
|
304 } |
|
305 |
|
306 void |
|
307 nsIFrame::FindCloserFrameForSelection( |
|
308 nsPoint aPoint, |
|
309 nsIFrame::FrameWithDistance* aCurrentBestFrame) |
|
310 { |
|
311 if (nsLayoutUtils::PointIsCloserToRect(aPoint, mRect, |
|
312 aCurrentBestFrame->mXDistance, |
|
313 aCurrentBestFrame->mYDistance)) { |
|
314 aCurrentBestFrame->mFrame = this; |
|
315 } |
|
316 } |
|
317 |
|
318 void |
|
319 nsIFrame::ContentStatesChanged(mozilla::EventStates aStates) |
|
320 { |
|
321 } |
|
322 |
|
323 void |
|
324 NS_MergeReflowStatusInto(nsReflowStatus* aPrimary, nsReflowStatus aSecondary) |
|
325 { |
|
326 *aPrimary |= aSecondary & |
|
327 (NS_FRAME_NOT_COMPLETE | NS_FRAME_OVERFLOW_INCOMPLETE | |
|
328 NS_FRAME_TRUNCATED | NS_FRAME_REFLOW_NEXTINFLOW); |
|
329 if (*aPrimary & NS_FRAME_NOT_COMPLETE) { |
|
330 *aPrimary &= ~NS_FRAME_OVERFLOW_INCOMPLETE; |
|
331 } |
|
332 } |
|
333 |
|
334 void |
|
335 nsWeakFrame::Init(nsIFrame* aFrame) |
|
336 { |
|
337 Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr); |
|
338 mFrame = aFrame; |
|
339 if (mFrame) { |
|
340 nsIPresShell* shell = mFrame->PresContext()->GetPresShell(); |
|
341 NS_WARN_IF_FALSE(shell, "Null PresShell in nsWeakFrame!"); |
|
342 if (shell) { |
|
343 shell->AddWeakFrame(this); |
|
344 } else { |
|
345 mFrame = nullptr; |
|
346 } |
|
347 } |
|
348 } |
|
349 |
|
350 nsIFrame* |
|
351 NS_NewEmptyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) |
|
352 { |
|
353 return new (aPresShell) nsFrame(aContext); |
|
354 } |
|
355 |
|
356 nsFrame::nsFrame(nsStyleContext* aContext) |
|
357 { |
|
358 MOZ_COUNT_CTOR(nsFrame); |
|
359 |
|
360 mState = NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY; |
|
361 mStyleContext = aContext; |
|
362 mStyleContext->AddRef(); |
|
363 } |
|
364 |
|
365 nsFrame::~nsFrame() |
|
366 { |
|
367 MOZ_COUNT_DTOR(nsFrame); |
|
368 |
|
369 NS_IF_RELEASE(mContent); |
|
370 mStyleContext->Release(); |
|
371 } |
|
372 |
|
373 NS_IMPL_FRAMEARENA_HELPERS(nsFrame) |
|
374 |
|
375 // Dummy operator delete. Will never be called, but must be defined |
|
376 // to satisfy some C++ ABIs. |
|
377 void |
|
378 nsFrame::operator delete(void *, size_t) |
|
379 { |
|
380 NS_RUNTIMEABORT("nsFrame::operator delete should never be called"); |
|
381 } |
|
382 |
|
383 NS_QUERYFRAME_HEAD(nsFrame) |
|
384 NS_QUERYFRAME_ENTRY(nsIFrame) |
|
385 NS_QUERYFRAME_TAIL_INHERITANCE_ROOT |
|
386 |
|
387 ///////////////////////////////////////////////////////////////////////////// |
|
388 // nsIFrame |
|
389 |
|
390 static bool |
|
391 IsFontSizeInflationContainer(nsIFrame* aFrame, |
|
392 const nsStyleDisplay* aStyleDisplay) |
|
393 { |
|
394 /* |
|
395 * Font size inflation is built around the idea that we're inflating |
|
396 * the fonts for a pan-and-zoom UI so that when the user scales up a |
|
397 * block or other container to fill the width of the device, the fonts |
|
398 * will be readable. To do this, we need to pick what counts as a |
|
399 * container. |
|
400 * |
|
401 * From a code perspective, the only hard requirement is that frames |
|
402 * that are line participants |
|
403 * (nsIFrame::IsFrameOfType(nsIFrame::eLineParticipant)) are never |
|
404 * containers, since line layout assumes that the inflation is |
|
405 * consistent within a line. |
|
406 * |
|
407 * This is not an imposition, since we obviously want a bunch of text |
|
408 * (possibly with inline elements) flowing within a block to count the |
|
409 * block (or higher) as its container. |
|
410 * |
|
411 * We also want form controls, including the text in the anonymous |
|
412 * content inside of them, to match each other and the text next to |
|
413 * them, so they and their anonymous content should also not be a |
|
414 * container. |
|
415 * |
|
416 * However, because we can't reliably compute sizes across XUL during |
|
417 * reflow, any XUL frame with a XUL parent is always a container. |
|
418 * |
|
419 * There are contexts where it would be nice if some blocks didn't |
|
420 * count as a container, so that, for example, an indented quotation |
|
421 * didn't end up with a smaller font size. However, it's hard to |
|
422 * distinguish these situations where we really do want the indented |
|
423 * thing to count as a container, so we don't try, and blocks are |
|
424 * always containers. |
|
425 */ |
|
426 |
|
427 // The root frame should always be an inflation container. |
|
428 if (!aFrame->GetParent()) { |
|
429 return true; |
|
430 } |
|
431 |
|
432 nsIContent *content = aFrame->GetContent(); |
|
433 bool isInline = (aFrame->GetDisplay() == NS_STYLE_DISPLAY_INLINE || |
|
434 (aFrame->IsFloating() && |
|
435 aFrame->GetType() == nsGkAtoms::letterFrame) || |
|
436 // Given multiple frames for the same node, only the |
|
437 // outer one should be considered a container. |
|
438 // (Important, e.g., for nsSelectsAreaFrame.) |
|
439 (aFrame->GetParent()->GetContent() == content) || |
|
440 (content && (content->IsHTML(nsGkAtoms::option) || |
|
441 content->IsHTML(nsGkAtoms::optgroup) || |
|
442 content->IsHTML(nsGkAtoms::select) || |
|
443 content->IsInNativeAnonymousSubtree()))) && |
|
444 !(aFrame->IsBoxFrame() && aFrame->GetParent()->IsBoxFrame()); |
|
445 NS_ASSERTION(!aFrame->IsFrameOfType(nsIFrame::eLineParticipant) || |
|
446 isInline || |
|
447 // br frames and mathml frames report being line |
|
448 // participants even when their position or display is |
|
449 // set |
|
450 aFrame->GetType() == nsGkAtoms::brFrame || |
|
451 aFrame->IsFrameOfType(nsIFrame::eMathML), |
|
452 "line participants must not be containers"); |
|
453 NS_ASSERTION(aFrame->GetType() != nsGkAtoms::bulletFrame || isInline, |
|
454 "bullets should not be containers"); |
|
455 return !isInline; |
|
456 } |
|
457 |
|
458 void |
|
459 nsFrame::Init(nsIContent* aContent, |
|
460 nsIFrame* aParent, |
|
461 nsIFrame* aPrevInFlow) |
|
462 { |
|
463 NS_PRECONDITION(!mContent, "Double-initing a frame?"); |
|
464 NS_ASSERTION(IsFrameOfType(eDEBUGAllFrames) && |
|
465 !IsFrameOfType(eDEBUGNoFrames), |
|
466 "IsFrameOfType implementation that doesn't call base class"); |
|
467 |
|
468 mContent = aContent; |
|
469 mParent = aParent; |
|
470 |
|
471 if (aContent) { |
|
472 NS_ADDREF(aContent); |
|
473 } |
|
474 |
|
475 if (aPrevInFlow) { |
|
476 // Make sure the general flags bits are the same |
|
477 nsFrameState state = aPrevInFlow->GetStateBits(); |
|
478 |
|
479 // Make bits that are currently off (see constructor) the same: |
|
480 mState |= state & (NS_FRAME_INDEPENDENT_SELECTION | |
|
481 NS_FRAME_PART_OF_IBSPLIT | |
|
482 NS_FRAME_MAY_BE_TRANSFORMED | |
|
483 NS_FRAME_MAY_HAVE_GENERATED_CONTENT | |
|
484 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); |
|
485 } |
|
486 if (mParent) { |
|
487 nsFrameState state = mParent->GetStateBits(); |
|
488 |
|
489 // Make bits that are currently off (see constructor) the same: |
|
490 mState |= state & (NS_FRAME_INDEPENDENT_SELECTION | |
|
491 NS_FRAME_GENERATED_CONTENT | |
|
492 NS_FRAME_IS_SVG_TEXT | |
|
493 NS_FRAME_IN_POPUP | |
|
494 NS_FRAME_IS_NONDISPLAY); |
|
495 } |
|
496 const nsStyleDisplay *disp = StyleDisplay(); |
|
497 if (disp->HasTransform(this)) { |
|
498 // The frame gets reconstructed if we toggle the -moz-transform |
|
499 // property, so we can set this bit here and then ignore it. |
|
500 mState |= NS_FRAME_MAY_BE_TRANSFORMED; |
|
501 } |
|
502 if (disp->mPosition == NS_STYLE_POSITION_STICKY && |
|
503 !aPrevInFlow && |
|
504 !(mState & NS_FRAME_IS_NONDISPLAY) && |
|
505 !disp->IsInnerTableStyle()) { |
|
506 // Note that we only add first continuations, but we really only |
|
507 // want to add first continuation-or-ib-split-siblings. But since we |
|
508 // don't yet know if we're a later part of a block-in-inline split, |
|
509 // we'll just add later members of a block-in-inline split here, and |
|
510 // then StickyScrollContainer will remove them later. |
|
511 // We don't currently support relative positioning of inner table |
|
512 // elements (bug 35168), so exclude them from sticky positioning too. |
|
513 StickyScrollContainer* ssc = |
|
514 StickyScrollContainer::GetStickyScrollContainerForFrame(this); |
|
515 if (ssc) { |
|
516 ssc->AddFrame(this); |
|
517 } |
|
518 } |
|
519 |
|
520 if (nsLayoutUtils::FontSizeInflationEnabled(PresContext()) || !GetParent() |
|
521 #ifdef DEBUG |
|
522 // We have assertions that check inflation invariants even when |
|
523 // font size inflation is not enabled. |
|
524 || true |
|
525 #endif |
|
526 ) { |
|
527 if (IsFontSizeInflationContainer(this, disp)) { |
|
528 AddStateBits(NS_FRAME_FONT_INFLATION_CONTAINER); |
|
529 if (!GetParent() || |
|
530 // I'd use NS_FRAME_OUT_OF_FLOW, but it's not set yet. |
|
531 disp->IsFloating(this) || disp->IsAbsolutelyPositioned(this)) { |
|
532 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT); |
|
533 } |
|
534 } |
|
535 NS_ASSERTION(GetParent() || |
|
536 (GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER), |
|
537 "root frame should always be a container"); |
|
538 } |
|
539 |
|
540 DidSetStyleContext(nullptr); |
|
541 |
|
542 if (IsBoxWrapped()) |
|
543 InitBoxMetrics(false); |
|
544 } |
|
545 |
|
546 nsresult nsFrame::SetInitialChildList(ChildListID aListID, |
|
547 nsFrameList& aChildList) |
|
548 { |
|
549 // XXX This shouldn't be getting called at all, but currently is for backwards |
|
550 // compatility reasons... |
|
551 #if 0 |
|
552 NS_ERROR("not a container"); |
|
553 return NS_ERROR_UNEXPECTED; |
|
554 #else |
|
555 NS_ASSERTION(aChildList.IsEmpty(), "not a container"); |
|
556 return NS_OK; |
|
557 #endif |
|
558 } |
|
559 |
|
560 nsresult |
|
561 nsFrame::AppendFrames(ChildListID aListID, |
|
562 nsFrameList& aFrameList) |
|
563 { |
|
564 NS_PRECONDITION(false, "not a container"); |
|
565 return NS_ERROR_UNEXPECTED; |
|
566 } |
|
567 |
|
568 nsresult |
|
569 nsFrame::InsertFrames(ChildListID aListID, |
|
570 nsIFrame* aPrevFrame, |
|
571 nsFrameList& aFrameList) |
|
572 { |
|
573 NS_PRECONDITION(false, "not a container"); |
|
574 return NS_ERROR_UNEXPECTED; |
|
575 } |
|
576 |
|
577 nsresult |
|
578 nsFrame::RemoveFrame(ChildListID aListID, |
|
579 nsIFrame* aOldFrame) |
|
580 { |
|
581 NS_PRECONDITION(false, "not a container"); |
|
582 return NS_ERROR_UNEXPECTED; |
|
583 } |
|
584 |
|
585 void |
|
586 nsFrame::DestroyFrom(nsIFrame* aDestructRoot) |
|
587 { |
|
588 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), |
|
589 "destroy called on frame while scripts not blocked"); |
|
590 NS_ASSERTION(!GetNextSibling() && !GetPrevSibling(), |
|
591 "Frames should be removed before destruction."); |
|
592 NS_ASSERTION(aDestructRoot, "Must specify destruct root"); |
|
593 MOZ_ASSERT(!HasAbsolutelyPositionedChildren()); |
|
594 |
|
595 nsSVGEffects::InvalidateDirectRenderingObservers(this); |
|
596 |
|
597 if (StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY) { |
|
598 StickyScrollContainer* ssc = |
|
599 StickyScrollContainer::GetStickyScrollContainerForFrame(this); |
|
600 if (ssc) { |
|
601 ssc->RemoveFrame(this); |
|
602 } |
|
603 } |
|
604 |
|
605 // Get the view pointer now before the frame properties disappear |
|
606 // when we call NotifyDestroyingFrame() |
|
607 nsView* view = GetView(); |
|
608 nsPresContext* presContext = PresContext(); |
|
609 |
|
610 nsIPresShell *shell = presContext->GetPresShell(); |
|
611 if (mState & NS_FRAME_OUT_OF_FLOW) { |
|
612 nsPlaceholderFrame* placeholder = |
|
613 shell->FrameManager()->GetPlaceholderFrameFor(this); |
|
614 NS_ASSERTION(!placeholder || (aDestructRoot != this), |
|
615 "Don't call Destroy() on OOFs, call Destroy() on the placeholder."); |
|
616 NS_ASSERTION(!placeholder || |
|
617 nsLayoutUtils::IsProperAncestorFrame(aDestructRoot, placeholder), |
|
618 "Placeholder relationship should have been torn down already; " |
|
619 "this might mean we have a stray placeholder in the tree."); |
|
620 if (placeholder) { |
|
621 shell->FrameManager()->UnregisterPlaceholderFrame(placeholder); |
|
622 placeholder->SetOutOfFlowFrame(nullptr); |
|
623 } |
|
624 } |
|
625 |
|
626 // If we have any IB split siblings, clear their references to us. |
|
627 // (Note: This has to happen before we call shell->NotifyDestroyingFrame, |
|
628 // because that clears our Properties() table.) |
|
629 if (mState & NS_FRAME_PART_OF_IBSPLIT) { |
|
630 // Delete previous sibling's reference to me. |
|
631 nsIFrame* prevSib = static_cast<nsIFrame*> |
|
632 (Properties().Get(nsIFrame::IBSplitPrevSibling())); |
|
633 if (prevSib) { |
|
634 NS_WARN_IF_FALSE(this == |
|
635 prevSib->Properties().Get(nsIFrame::IBSplitSibling()), |
|
636 "IB sibling chain is inconsistent"); |
|
637 prevSib->Properties().Delete(nsIFrame::IBSplitSibling()); |
|
638 } |
|
639 |
|
640 // Delete next sibling's reference to me. |
|
641 nsIFrame* nextSib = static_cast<nsIFrame*> |
|
642 (Properties().Get(nsIFrame::IBSplitSibling())); |
|
643 if (nextSib) { |
|
644 NS_WARN_IF_FALSE(this == |
|
645 nextSib->Properties().Get(nsIFrame::IBSplitPrevSibling()), |
|
646 "IB sibling chain is inconsistent"); |
|
647 nextSib->Properties().Delete(nsIFrame::IBSplitPrevSibling()); |
|
648 } |
|
649 } |
|
650 |
|
651 shell->NotifyDestroyingFrame(this); |
|
652 |
|
653 if (mState & NS_FRAME_EXTERNAL_REFERENCE) { |
|
654 shell->ClearFrameRefs(this); |
|
655 } |
|
656 |
|
657 if (view) { |
|
658 // Break association between view and frame |
|
659 view->SetFrame(nullptr); |
|
660 |
|
661 // Destroy the view |
|
662 view->Destroy(); |
|
663 } |
|
664 |
|
665 // Make sure that our deleted frame can't be returned from GetPrimaryFrame() |
|
666 if (mContent && mContent->GetPrimaryFrame() == this) { |
|
667 mContent->SetPrimaryFrame(nullptr); |
|
668 } |
|
669 |
|
670 // Must retrieve the object ID before calling destructors, so the |
|
671 // vtable is still valid. |
|
672 // |
|
673 // Note to future tweakers: having the method that returns the |
|
674 // object size call the destructor will not avoid an indirect call; |
|
675 // the compiler cannot devirtualize the call to the destructor even |
|
676 // if it's from a method defined in the same class. |
|
677 |
|
678 nsQueryFrame::FrameIID id = GetFrameId(); |
|
679 this->~nsFrame(); |
|
680 |
|
681 // Now that we're totally cleaned out, we need to add ourselves to |
|
682 // the presshell's recycler. |
|
683 shell->FreeFrame(id, this); |
|
684 } |
|
685 |
|
686 nsresult |
|
687 nsFrame::GetOffsets(int32_t &aStart, int32_t &aEnd) const |
|
688 { |
|
689 aStart = 0; |
|
690 aEnd = 0; |
|
691 return NS_OK; |
|
692 } |
|
693 |
|
694 // Subclass hook for style post processing |
|
695 /* virtual */ void |
|
696 nsFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) |
|
697 { |
|
698 if (IsSVGText()) { |
|
699 SVGTextFrame* svgTextFrame = static_cast<SVGTextFrame*>( |
|
700 nsLayoutUtils::GetClosestFrameOfType(this, nsGkAtoms::svgTextFrame)); |
|
701 nsIFrame* anonBlock = svgTextFrame->GetFirstPrincipalChild(); |
|
702 // Just as in SVGTextFrame::DidSetStyleContext, we need to ensure that |
|
703 // any non-display SVGTextFrames get reflowed when a child text frame |
|
704 // gets new style. |
|
705 // |
|
706 // Note that we must check NS_FRAME_FIRST_REFLOW on our SVGTextFrame's |
|
707 // anonymous block frame rather than our self, since NS_FRAME_FIRST_REFLOW |
|
708 // may be set on us if we're a new frame that has been inserted after the |
|
709 // document's first reflow. (In which case this DidSetStyleContext call may |
|
710 // be happening under frame construction under a Reflow() call.) |
|
711 if (anonBlock && !(anonBlock->GetStateBits() & NS_FRAME_FIRST_REFLOW) && |
|
712 (svgTextFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) && |
|
713 !(svgTextFrame->GetStateBits() & NS_STATE_SVG_TEXT_IN_REFLOW)) { |
|
714 svgTextFrame->ScheduleReflowSVGNonDisplayText(); |
|
715 } |
|
716 } |
|
717 |
|
718 ImageLoader* imageLoader = PresContext()->Document()->StyleImageLoader(); |
|
719 |
|
720 // If the old context had a background image image and new context |
|
721 // does not have the same image, clear the image load notifier |
|
722 // (which keeps the image loading, if it still is) for the frame. |
|
723 // We want to do this conservatively because some frames paint their |
|
724 // backgrounds from some other frame's style data, and we don't want |
|
725 // to clear those notifiers unless we have to. (They'll be reset |
|
726 // when we paint, although we could miss a notification in that |
|
727 // interval.) |
|
728 const nsStyleBackground *oldBG = aOldStyleContext ? |
|
729 aOldStyleContext->StyleBackground() : |
|
730 nullptr; |
|
731 const nsStyleBackground *newBG = StyleBackground(); |
|
732 if (oldBG) { |
|
733 NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, oldBG) { |
|
734 // If there is an image in oldBG that's not in newBG, drop it. |
|
735 if (i >= newBG->mImageCount || |
|
736 !oldBG->mLayers[i].mImage.ImageDataEquals(newBG->mLayers[i].mImage)) { |
|
737 const nsStyleImage& oldImage = oldBG->mLayers[i].mImage; |
|
738 if (oldImage.GetType() != eStyleImageType_Image) { |
|
739 continue; |
|
740 } |
|
741 |
|
742 imageLoader->DisassociateRequestFromFrame(oldImage.GetImageData(), |
|
743 this); |
|
744 } |
|
745 } |
|
746 } |
|
747 |
|
748 NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, newBG) { |
|
749 // If there is an image in newBG that's not in oldBG, add it. |
|
750 if (!oldBG || i >= oldBG->mImageCount || |
|
751 !newBG->mLayers[i].mImage.ImageDataEquals(oldBG->mLayers[i].mImage)) { |
|
752 const nsStyleImage& newImage = newBG->mLayers[i].mImage; |
|
753 if (newImage.GetType() != eStyleImageType_Image) { |
|
754 continue; |
|
755 } |
|
756 |
|
757 imageLoader->AssociateRequestToFrame(newImage.GetImageData(), this); |
|
758 } |
|
759 } |
|
760 |
|
761 if (aOldStyleContext) { |
|
762 // If we detect a change on margin, padding or border, we store the old |
|
763 // values on the frame itself between now and reflow, so if someone |
|
764 // calls GetUsed(Margin|Border|Padding)() before the next reflow, we |
|
765 // can give an accurate answer. |
|
766 // We don't want to set the property if one already exists. |
|
767 FrameProperties props = Properties(); |
|
768 nsMargin oldValue(0, 0, 0, 0); |
|
769 nsMargin newValue(0, 0, 0, 0); |
|
770 const nsStyleMargin* oldMargin = aOldStyleContext->PeekStyleMargin(); |
|
771 if (oldMargin && oldMargin->GetMargin(oldValue)) { |
|
772 if ((!StyleMargin()->GetMargin(newValue) || oldValue != newValue) && |
|
773 !props.Get(UsedMarginProperty())) { |
|
774 props.Set(UsedMarginProperty(), new nsMargin(oldValue)); |
|
775 } |
|
776 } |
|
777 |
|
778 const nsStylePadding* oldPadding = aOldStyleContext->PeekStylePadding(); |
|
779 if (oldPadding && oldPadding->GetPadding(oldValue)) { |
|
780 if ((!StylePadding()->GetPadding(newValue) || oldValue != newValue) && |
|
781 !props.Get(UsedPaddingProperty())) { |
|
782 props.Set(UsedPaddingProperty(), new nsMargin(oldValue)); |
|
783 } |
|
784 } |
|
785 |
|
786 const nsStyleBorder* oldBorder = aOldStyleContext->PeekStyleBorder(); |
|
787 if (oldBorder) { |
|
788 oldValue = oldBorder->GetComputedBorder(); |
|
789 newValue = StyleBorder()->GetComputedBorder(); |
|
790 if (oldValue != newValue && |
|
791 !props.Get(UsedBorderProperty())) { |
|
792 props.Set(UsedBorderProperty(), new nsMargin(oldValue)); |
|
793 } |
|
794 } |
|
795 } |
|
796 |
|
797 imgIRequest *oldBorderImage = aOldStyleContext |
|
798 ? aOldStyleContext->StyleBorder()->GetBorderImageRequest() |
|
799 : nullptr; |
|
800 imgIRequest *newBorderImage = StyleBorder()->GetBorderImageRequest(); |
|
801 // FIXME (Bug 759996): The following is no longer true. |
|
802 // For border-images, we can't be as conservative (we need to set the |
|
803 // new loaders if there has been any change) since the CalcDifference |
|
804 // call depended on the result of GetComputedBorder() and that result |
|
805 // depends on whether the image has loaded, start the image load now |
|
806 // so that we'll get notified when it completes loading and can do a |
|
807 // restyle. Otherwise, the image might finish loading from the |
|
808 // network before we start listening to its notifications, and then |
|
809 // we'll never know that it's finished loading. Likewise, we want to |
|
810 // do this for freshly-created frames to prevent a similar race if the |
|
811 // image loads between reflow (which can depend on whether the image |
|
812 // is loaded) and paint. We also don't really care about any callers |
|
813 // who try to paint borders with a different style context, because |
|
814 // they won't have the correct size for the border either. |
|
815 if (oldBorderImage != newBorderImage) { |
|
816 // stop and restart the image loading/notification |
|
817 if (oldBorderImage) { |
|
818 imageLoader->DisassociateRequestFromFrame(oldBorderImage, this); |
|
819 } |
|
820 if (newBorderImage) { |
|
821 imageLoader->AssociateRequestToFrame(newBorderImage, this); |
|
822 } |
|
823 } |
|
824 |
|
825 // If the page contains markup that overrides text direction, and |
|
826 // does not contain any characters that would activate the Unicode |
|
827 // bidi algorithm, we need to call |SetBidiEnabled| on the pres |
|
828 // context before reflow starts. See bug 115921. |
|
829 if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { |
|
830 PresContext()->SetBidiEnabled(); |
|
831 } |
|
832 } |
|
833 |
|
834 // MSVC fails with link error "one or more multiply defined symbols found", |
|
835 // gcc fails with "hidden symbol `nsIFrame::kPrincipalList' isn't defined" |
|
836 // etc if they are not defined. |
|
837 #ifndef _MSC_VER |
|
838 // static nsIFrame constants; initialized in the header file. |
|
839 const nsIFrame::ChildListID nsIFrame::kPrincipalList; |
|
840 const nsIFrame::ChildListID nsIFrame::kAbsoluteList; |
|
841 const nsIFrame::ChildListID nsIFrame::kBulletList; |
|
842 const nsIFrame::ChildListID nsIFrame::kCaptionList; |
|
843 const nsIFrame::ChildListID nsIFrame::kColGroupList; |
|
844 const nsIFrame::ChildListID nsIFrame::kExcessOverflowContainersList; |
|
845 const nsIFrame::ChildListID nsIFrame::kFixedList; |
|
846 const nsIFrame::ChildListID nsIFrame::kFloatList; |
|
847 const nsIFrame::ChildListID nsIFrame::kOverflowContainersList; |
|
848 const nsIFrame::ChildListID nsIFrame::kOverflowList; |
|
849 const nsIFrame::ChildListID nsIFrame::kOverflowOutOfFlowList; |
|
850 const nsIFrame::ChildListID nsIFrame::kPopupList; |
|
851 const nsIFrame::ChildListID nsIFrame::kPushedFloatsList; |
|
852 const nsIFrame::ChildListID nsIFrame::kSelectPopupList; |
|
853 const nsIFrame::ChildListID nsIFrame::kNoReflowPrincipalList; |
|
854 #endif |
|
855 |
|
856 /* virtual */ nsMargin |
|
857 nsIFrame::GetUsedMargin() const |
|
858 { |
|
859 nsMargin margin(0, 0, 0, 0); |
|
860 if (((mState & NS_FRAME_FIRST_REFLOW) && |
|
861 !(mState & NS_FRAME_IN_REFLOW)) || |
|
862 IsSVGText()) |
|
863 return margin; |
|
864 |
|
865 nsMargin *m = static_cast<nsMargin*> |
|
866 (Properties().Get(UsedMarginProperty())); |
|
867 if (m) { |
|
868 margin = *m; |
|
869 } else { |
|
870 DebugOnly<bool> hasMargin = StyleMargin()->GetMargin(margin); |
|
871 NS_ASSERTION(hasMargin, "We should have a margin here! (out of memory?)"); |
|
872 } |
|
873 return margin; |
|
874 } |
|
875 |
|
876 /* virtual */ nsMargin |
|
877 nsIFrame::GetUsedBorder() const |
|
878 { |
|
879 nsMargin border(0, 0, 0, 0); |
|
880 if (((mState & NS_FRAME_FIRST_REFLOW) && |
|
881 !(mState & NS_FRAME_IN_REFLOW)) || |
|
882 IsSVGText()) |
|
883 return border; |
|
884 |
|
885 // Theme methods don't use const-ness. |
|
886 nsIFrame *mutable_this = const_cast<nsIFrame*>(this); |
|
887 |
|
888 const nsStyleDisplay *disp = StyleDisplay(); |
|
889 if (mutable_this->IsThemed(disp)) { |
|
890 nsIntMargin result; |
|
891 nsPresContext *presContext = PresContext(); |
|
892 presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(), |
|
893 mutable_this, disp->mAppearance, |
|
894 &result); |
|
895 border.left = presContext->DevPixelsToAppUnits(result.left); |
|
896 border.top = presContext->DevPixelsToAppUnits(result.top); |
|
897 border.right = presContext->DevPixelsToAppUnits(result.right); |
|
898 border.bottom = presContext->DevPixelsToAppUnits(result.bottom); |
|
899 return border; |
|
900 } |
|
901 |
|
902 nsMargin *b = static_cast<nsMargin*> |
|
903 (Properties().Get(UsedBorderProperty())); |
|
904 if (b) { |
|
905 border = *b; |
|
906 } else { |
|
907 border = StyleBorder()->GetComputedBorder(); |
|
908 } |
|
909 return border; |
|
910 } |
|
911 |
|
912 /* virtual */ nsMargin |
|
913 nsIFrame::GetUsedPadding() const |
|
914 { |
|
915 nsMargin padding(0, 0, 0, 0); |
|
916 if (((mState & NS_FRAME_FIRST_REFLOW) && |
|
917 !(mState & NS_FRAME_IN_REFLOW)) || |
|
918 IsSVGText()) |
|
919 return padding; |
|
920 |
|
921 // Theme methods don't use const-ness. |
|
922 nsIFrame *mutable_this = const_cast<nsIFrame*>(this); |
|
923 |
|
924 const nsStyleDisplay *disp = StyleDisplay(); |
|
925 if (mutable_this->IsThemed(disp)) { |
|
926 nsPresContext *presContext = PresContext(); |
|
927 nsIntMargin widget; |
|
928 if (presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(), |
|
929 mutable_this, |
|
930 disp->mAppearance, |
|
931 &widget)) { |
|
932 padding.top = presContext->DevPixelsToAppUnits(widget.top); |
|
933 padding.right = presContext->DevPixelsToAppUnits(widget.right); |
|
934 padding.bottom = presContext->DevPixelsToAppUnits(widget.bottom); |
|
935 padding.left = presContext->DevPixelsToAppUnits(widget.left); |
|
936 return padding; |
|
937 } |
|
938 } |
|
939 |
|
940 nsMargin *p = static_cast<nsMargin*> |
|
941 (Properties().Get(UsedPaddingProperty())); |
|
942 if (p) { |
|
943 padding = *p; |
|
944 } else { |
|
945 DebugOnly<bool> hasPadding = StylePadding()->GetPadding(padding); |
|
946 NS_ASSERTION(hasPadding, "We should have padding here! (out of memory?)"); |
|
947 } |
|
948 return padding; |
|
949 } |
|
950 |
|
951 int |
|
952 nsIFrame::GetSkipSides(const nsHTMLReflowState* aReflowState) const |
|
953 { |
|
954 // Convert the logical skip sides to physical sides using the frame's |
|
955 // writing mode |
|
956 WritingMode writingMode = GetWritingMode(); |
|
957 int logicalSkip = GetLogicalSkipSides(aReflowState); |
|
958 int skip = 0; |
|
959 |
|
960 if (logicalSkip & LOGICAL_SIDE_B_START) { |
|
961 if (writingMode.IsVertical()) { |
|
962 skip |= 1 << (writingMode.IsVerticalLR() ? NS_SIDE_LEFT : NS_SIDE_RIGHT); |
|
963 } else { |
|
964 skip |= 1 << NS_SIDE_TOP; |
|
965 } |
|
966 } |
|
967 |
|
968 if (logicalSkip & LOGICAL_SIDE_B_END) { |
|
969 if (writingMode.IsVertical()) { |
|
970 skip |= 1 << (writingMode.IsVerticalLR() ? NS_SIDE_RIGHT : NS_SIDE_LEFT); |
|
971 } else { |
|
972 skip |= 1 << NS_SIDE_BOTTOM; |
|
973 } |
|
974 } |
|
975 |
|
976 if (logicalSkip & LOGICAL_SIDE_I_START) { |
|
977 if (writingMode.IsVertical()) { |
|
978 skip |= 1 << NS_SIDE_TOP; |
|
979 } else { |
|
980 skip |= 1 << (writingMode.IsBidiLTR() ? NS_SIDE_LEFT : NS_SIDE_RIGHT); |
|
981 } |
|
982 } |
|
983 |
|
984 if (logicalSkip & LOGICAL_SIDE_I_END) { |
|
985 if (writingMode.IsVertical()) { |
|
986 skip |= 1 << NS_SIDE_BOTTOM; |
|
987 } else { |
|
988 skip |= 1 << (writingMode.IsBidiLTR() ? NS_SIDE_RIGHT : NS_SIDE_LEFT); |
|
989 } |
|
990 } |
|
991 |
|
992 return skip; |
|
993 } |
|
994 |
|
995 |
|
996 void |
|
997 nsIFrame::ApplySkipSides(nsMargin& aMargin, |
|
998 const nsHTMLReflowState* aReflowState) const |
|
999 { |
|
1000 int skipSides = GetSkipSides(aReflowState); |
|
1001 if (skipSides & (1 << NS_SIDE_TOP)) { |
|
1002 aMargin.top = 0; |
|
1003 } |
|
1004 if (skipSides & (1 << NS_SIDE_RIGHT)) { |
|
1005 aMargin.right = 0; |
|
1006 } |
|
1007 if (skipSides & (1 << NS_SIDE_BOTTOM)) { |
|
1008 aMargin.bottom = 0; |
|
1009 } |
|
1010 if (skipSides & (1 << NS_SIDE_LEFT)) { |
|
1011 aMargin.left = 0; |
|
1012 } |
|
1013 } |
|
1014 |
|
1015 void |
|
1016 nsIFrame::ApplyLogicalSkipSides(LogicalMargin& aMargin, |
|
1017 const nsHTMLReflowState* aReflowState) const |
|
1018 { |
|
1019 int skipSides = GetLogicalSkipSides(aReflowState); |
|
1020 if (skipSides & (LOGICAL_SIDE_B_START)) { |
|
1021 aMargin.BStart(GetWritingMode()) = 0; |
|
1022 } |
|
1023 if (skipSides & (LOGICAL_SIDE_I_END)) { |
|
1024 aMargin.IEnd(GetWritingMode()) = 0; |
|
1025 } |
|
1026 if (skipSides & (LOGICAL_SIDE_B_END)) { |
|
1027 aMargin.BEnd(GetWritingMode()) = 0; |
|
1028 } |
|
1029 if (skipSides & (LOGICAL_SIDE_I_START)) { |
|
1030 aMargin.IStart(GetWritingMode()) = 0; |
|
1031 } |
|
1032 } |
|
1033 |
|
1034 nsRect |
|
1035 nsIFrame::GetPaddingRectRelativeToSelf() const |
|
1036 { |
|
1037 nsMargin border(GetUsedBorder()); |
|
1038 ApplySkipSides(border); |
|
1039 nsRect r(0, 0, mRect.width, mRect.height); |
|
1040 r.Deflate(border); |
|
1041 return r; |
|
1042 } |
|
1043 |
|
1044 nsRect |
|
1045 nsIFrame::GetPaddingRect() const |
|
1046 { |
|
1047 return GetPaddingRectRelativeToSelf() + GetPosition(); |
|
1048 } |
|
1049 |
|
1050 WritingMode |
|
1051 nsIFrame::GetWritingMode(nsIFrame* aSubFrame) const |
|
1052 { |
|
1053 WritingMode writingMode = GetWritingMode(); |
|
1054 |
|
1055 if (!writingMode.IsVertical() && |
|
1056 (StyleTextReset()->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT)) { |
|
1057 nsBidiLevel frameLevel = nsBidiPresUtils::GetFrameBaseLevel(aSubFrame); |
|
1058 writingMode.SetDirectionFromBidiLevel(frameLevel); |
|
1059 } |
|
1060 |
|
1061 return writingMode; |
|
1062 } |
|
1063 |
|
1064 nsRect |
|
1065 nsIFrame::GetMarginRectRelativeToSelf() const |
|
1066 { |
|
1067 nsMargin m = GetUsedMargin(); |
|
1068 ApplySkipSides(m); |
|
1069 nsRect r(0, 0, mRect.width, mRect.height); |
|
1070 r.Inflate(m); |
|
1071 return r; |
|
1072 } |
|
1073 |
|
1074 bool |
|
1075 nsIFrame::IsTransformed() const |
|
1076 { |
|
1077 return ((mState & NS_FRAME_MAY_BE_TRANSFORMED) && |
|
1078 (StyleDisplay()->HasTransform(this) || |
|
1079 IsSVGTransformed() || |
|
1080 (mContent && |
|
1081 nsLayoutUtils::HasAnimationsForCompositor(mContent, |
|
1082 eCSSProperty_transform) && |
|
1083 IsFrameOfType(eSupportsCSSTransforms) && |
|
1084 mContent->GetPrimaryFrame() == this))); |
|
1085 } |
|
1086 |
|
1087 bool |
|
1088 nsIFrame::HasOpacityInternal(float aThreshold) const |
|
1089 { |
|
1090 MOZ_ASSERT(0.0 <= aThreshold && aThreshold <= 1.0, "Invalid argument"); |
|
1091 const nsStyleDisplay* displayStyle = StyleDisplay(); |
|
1092 return StyleDisplay()->mOpacity < aThreshold || |
|
1093 (displayStyle->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) || |
|
1094 (mContent && |
|
1095 nsLayoutUtils::HasAnimationsForCompositor(mContent, |
|
1096 eCSSProperty_opacity) && |
|
1097 mContent->GetPrimaryFrame() == this); |
|
1098 } |
|
1099 |
|
1100 bool |
|
1101 nsIFrame::IsSVGTransformed(gfx::Matrix *aOwnTransforms, |
|
1102 gfx::Matrix *aFromParentTransforms) const |
|
1103 { |
|
1104 return false; |
|
1105 } |
|
1106 |
|
1107 bool |
|
1108 nsIFrame::Preserves3DChildren() const |
|
1109 { |
|
1110 const nsStyleDisplay* disp = StyleDisplay(); |
|
1111 if (disp->mTransformStyle != NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D || |
|
1112 !IsFrameOfType(nsIFrame::eSupportsCSSTransforms)) { |
|
1113 return false; |
|
1114 } |
|
1115 |
|
1116 // If we're all scroll frame, then all descendants will be clipped, so we can't preserve 3d. |
|
1117 if (GetType() == nsGkAtoms::scrollFrame) { |
|
1118 return false; |
|
1119 } |
|
1120 |
|
1121 nsRect temp; |
|
1122 return !nsFrame::ShouldApplyOverflowClipping(this, disp) && |
|
1123 !GetClipPropClipRect(disp, &temp, GetSize()) && |
|
1124 !nsSVGIntegrationUtils::UsingEffectsForFrame(this); |
|
1125 } |
|
1126 |
|
1127 bool |
|
1128 nsIFrame::Preserves3D() const |
|
1129 { |
|
1130 if (!GetParent() || !GetParent()->Preserves3DChildren()) { |
|
1131 return false; |
|
1132 } |
|
1133 return StyleDisplay()->HasTransform(this) || StyleDisplay()->BackfaceIsHidden(); |
|
1134 } |
|
1135 |
|
1136 bool |
|
1137 nsIFrame::HasPerspective() const |
|
1138 { |
|
1139 if (!IsTransformed()) { |
|
1140 return false; |
|
1141 } |
|
1142 nsStyleContext* parentStyleContext = StyleContext()->GetParent(); |
|
1143 if (!parentStyleContext) { |
|
1144 return false; |
|
1145 } |
|
1146 const nsStyleDisplay* parentDisp = parentStyleContext->StyleDisplay(); |
|
1147 return parentDisp->mChildPerspective.GetUnit() == eStyleUnit_Coord; |
|
1148 } |
|
1149 |
|
1150 bool |
|
1151 nsIFrame::ChildrenHavePerspective() const |
|
1152 { |
|
1153 return StyleDisplay()->HasPerspectiveStyle(); |
|
1154 } |
|
1155 |
|
1156 nsRect |
|
1157 nsIFrame::GetContentRectRelativeToSelf() const |
|
1158 { |
|
1159 nsMargin bp(GetUsedBorderAndPadding()); |
|
1160 ApplySkipSides(bp); |
|
1161 nsRect r(0, 0, mRect.width, mRect.height); |
|
1162 r.Deflate(bp); |
|
1163 return r; |
|
1164 } |
|
1165 |
|
1166 nsRect |
|
1167 nsIFrame::GetContentRect() const |
|
1168 { |
|
1169 return GetContentRectRelativeToSelf() + GetPosition(); |
|
1170 } |
|
1171 |
|
1172 bool |
|
1173 nsIFrame::ComputeBorderRadii(const nsStyleCorners& aBorderRadius, |
|
1174 const nsSize& aFrameSize, |
|
1175 const nsSize& aBorderArea, |
|
1176 int aSkipSides, |
|
1177 nscoord aRadii[8]) |
|
1178 { |
|
1179 // Percentages are relative to whichever side they're on. |
|
1180 NS_FOR_CSS_HALF_CORNERS(i) { |
|
1181 const nsStyleCoord c = aBorderRadius.Get(i); |
|
1182 nscoord axis = |
|
1183 NS_HALF_CORNER_IS_X(i) ? aFrameSize.width : aFrameSize.height; |
|
1184 |
|
1185 if (c.IsCoordPercentCalcUnit()) { |
|
1186 aRadii[i] = nsRuleNode::ComputeCoordPercentCalc(c, axis); |
|
1187 if (aRadii[i] < 0) { |
|
1188 // clamp calc() |
|
1189 aRadii[i] = 0; |
|
1190 } |
|
1191 } else { |
|
1192 NS_NOTREACHED("ComputeBorderRadii: bad unit"); |
|
1193 aRadii[i] = 0; |
|
1194 } |
|
1195 } |
|
1196 |
|
1197 if (aSkipSides & (1 << NS_SIDE_TOP)) { |
|
1198 aRadii[NS_CORNER_TOP_LEFT_X] = 0; |
|
1199 aRadii[NS_CORNER_TOP_LEFT_Y] = 0; |
|
1200 aRadii[NS_CORNER_TOP_RIGHT_X] = 0; |
|
1201 aRadii[NS_CORNER_TOP_RIGHT_Y] = 0; |
|
1202 } |
|
1203 |
|
1204 if (aSkipSides & (1 << NS_SIDE_RIGHT)) { |
|
1205 aRadii[NS_CORNER_TOP_RIGHT_X] = 0; |
|
1206 aRadii[NS_CORNER_TOP_RIGHT_Y] = 0; |
|
1207 aRadii[NS_CORNER_BOTTOM_RIGHT_X] = 0; |
|
1208 aRadii[NS_CORNER_BOTTOM_RIGHT_Y] = 0; |
|
1209 } |
|
1210 |
|
1211 if (aSkipSides & (1 << NS_SIDE_BOTTOM)) { |
|
1212 aRadii[NS_CORNER_BOTTOM_RIGHT_X] = 0; |
|
1213 aRadii[NS_CORNER_BOTTOM_RIGHT_Y] = 0; |
|
1214 aRadii[NS_CORNER_BOTTOM_LEFT_X] = 0; |
|
1215 aRadii[NS_CORNER_BOTTOM_LEFT_Y] = 0; |
|
1216 } |
|
1217 |
|
1218 if (aSkipSides & (1 << NS_SIDE_LEFT)) { |
|
1219 aRadii[NS_CORNER_BOTTOM_LEFT_X] = 0; |
|
1220 aRadii[NS_CORNER_BOTTOM_LEFT_Y] = 0; |
|
1221 aRadii[NS_CORNER_TOP_LEFT_X] = 0; |
|
1222 aRadii[NS_CORNER_TOP_LEFT_Y] = 0; |
|
1223 } |
|
1224 |
|
1225 // css3-background specifies this algorithm for reducing |
|
1226 // corner radii when they are too big. |
|
1227 bool haveRadius = false; |
|
1228 double ratio = 1.0f; |
|
1229 NS_FOR_CSS_SIDES(side) { |
|
1230 uint32_t hc1 = NS_SIDE_TO_HALF_CORNER(side, false, true); |
|
1231 uint32_t hc2 = NS_SIDE_TO_HALF_CORNER(side, true, true); |
|
1232 nscoord length = |
|
1233 NS_SIDE_IS_VERTICAL(side) ? aBorderArea.height : aBorderArea.width; |
|
1234 nscoord sum = aRadii[hc1] + aRadii[hc2]; |
|
1235 if (sum) |
|
1236 haveRadius = true; |
|
1237 |
|
1238 // avoid floating point division in the normal case |
|
1239 if (length < sum) |
|
1240 ratio = std::min(ratio, double(length)/sum); |
|
1241 } |
|
1242 if (ratio < 1.0) { |
|
1243 NS_FOR_CSS_HALF_CORNERS(corner) { |
|
1244 aRadii[corner] *= ratio; |
|
1245 } |
|
1246 } |
|
1247 |
|
1248 return haveRadius; |
|
1249 } |
|
1250 |
|
1251 /* static */ void |
|
1252 nsIFrame::InsetBorderRadii(nscoord aRadii[8], const nsMargin &aOffsets) |
|
1253 { |
|
1254 NS_FOR_CSS_SIDES(side) { |
|
1255 nscoord offset = aOffsets.Side(side); |
|
1256 uint32_t hc1 = NS_SIDE_TO_HALF_CORNER(side, false, false); |
|
1257 uint32_t hc2 = NS_SIDE_TO_HALF_CORNER(side, true, false); |
|
1258 aRadii[hc1] = std::max(0, aRadii[hc1] - offset); |
|
1259 aRadii[hc2] = std::max(0, aRadii[hc2] - offset); |
|
1260 } |
|
1261 } |
|
1262 |
|
1263 /* static */ void |
|
1264 nsIFrame::OutsetBorderRadii(nscoord aRadii[8], const nsMargin &aOffsets) |
|
1265 { |
|
1266 NS_FOR_CSS_SIDES(side) { |
|
1267 nscoord offset = aOffsets.Side(side); |
|
1268 uint32_t hc1 = NS_SIDE_TO_HALF_CORNER(side, false, false); |
|
1269 uint32_t hc2 = NS_SIDE_TO_HALF_CORNER(side, true, false); |
|
1270 if (aRadii[hc1] > 0) |
|
1271 aRadii[hc1] += offset; |
|
1272 if (aRadii[hc2] > 0) |
|
1273 aRadii[hc2] += offset; |
|
1274 } |
|
1275 } |
|
1276 |
|
1277 /* virtual */ bool |
|
1278 nsIFrame::GetBorderRadii(nscoord aRadii[8]) const |
|
1279 { |
|
1280 if (IsThemed()) { |
|
1281 // When we're themed, the native theme code draws the border and |
|
1282 // background, and therefore it doesn't make sense to tell other |
|
1283 // code that's interested in border-radius that we have any radii. |
|
1284 // |
|
1285 // In an ideal world, we might have a way for the them to tell us an |
|
1286 // border radius, but since we don't, we're better off assuming |
|
1287 // zero. |
|
1288 NS_FOR_CSS_HALF_CORNERS(corner) { |
|
1289 aRadii[corner] = 0; |
|
1290 } |
|
1291 return false; |
|
1292 } |
|
1293 nsSize size = GetSize(); |
|
1294 return ComputeBorderRadii(StyleBorder()->mBorderRadius, size, size, |
|
1295 GetSkipSides(), aRadii); |
|
1296 } |
|
1297 |
|
1298 bool |
|
1299 nsIFrame::GetPaddingBoxBorderRadii(nscoord aRadii[8]) const |
|
1300 { |
|
1301 if (!GetBorderRadii(aRadii)) |
|
1302 return false; |
|
1303 InsetBorderRadii(aRadii, GetUsedBorder()); |
|
1304 NS_FOR_CSS_HALF_CORNERS(corner) { |
|
1305 if (aRadii[corner]) |
|
1306 return true; |
|
1307 } |
|
1308 return false; |
|
1309 } |
|
1310 |
|
1311 bool |
|
1312 nsIFrame::GetContentBoxBorderRadii(nscoord aRadii[8]) const |
|
1313 { |
|
1314 if (!GetBorderRadii(aRadii)) |
|
1315 return false; |
|
1316 InsetBorderRadii(aRadii, GetUsedBorderAndPadding()); |
|
1317 NS_FOR_CSS_HALF_CORNERS(corner) { |
|
1318 if (aRadii[corner]) |
|
1319 return true; |
|
1320 } |
|
1321 return false; |
|
1322 } |
|
1323 |
|
1324 nsStyleContext* |
|
1325 nsFrame::GetAdditionalStyleContext(int32_t aIndex) const |
|
1326 { |
|
1327 NS_PRECONDITION(aIndex >= 0, "invalid index number"); |
|
1328 return nullptr; |
|
1329 } |
|
1330 |
|
1331 void |
|
1332 nsFrame::SetAdditionalStyleContext(int32_t aIndex, |
|
1333 nsStyleContext* aStyleContext) |
|
1334 { |
|
1335 NS_PRECONDITION(aIndex >= 0, "invalid index number"); |
|
1336 } |
|
1337 |
|
1338 nscoord |
|
1339 nsFrame::GetBaseline() const |
|
1340 { |
|
1341 NS_ASSERTION(!NS_SUBTREE_DIRTY(this), |
|
1342 "frame must not be dirty"); |
|
1343 // Default to the bottom margin edge, per CSS2.1's definition of the |
|
1344 // 'baseline' value of 'vertical-align'. |
|
1345 return mRect.height + GetUsedMargin().bottom; |
|
1346 } |
|
1347 |
|
1348 const nsFrameList& |
|
1349 nsFrame::GetChildList(ChildListID aListID) const |
|
1350 { |
|
1351 if (IsAbsoluteContainer() && |
|
1352 aListID == GetAbsoluteListID()) { |
|
1353 return GetAbsoluteContainingBlock()->GetChildList(); |
|
1354 } else { |
|
1355 return nsFrameList::EmptyList(); |
|
1356 } |
|
1357 } |
|
1358 |
|
1359 void |
|
1360 nsFrame::GetChildLists(nsTArray<ChildList>* aLists) const |
|
1361 { |
|
1362 if (IsAbsoluteContainer()) { |
|
1363 nsFrameList absoluteList = GetAbsoluteContainingBlock()->GetChildList(); |
|
1364 absoluteList.AppendIfNonempty(aLists, GetAbsoluteListID()); |
|
1365 } |
|
1366 } |
|
1367 |
|
1368 void |
|
1369 nsIFrame::GetCrossDocChildLists(nsTArray<ChildList>* aLists) |
|
1370 { |
|
1371 nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(this); |
|
1372 if (subdocumentFrame) { |
|
1373 // Descend into the subdocument |
|
1374 nsIFrame* root = subdocumentFrame->GetSubdocumentRootFrame(); |
|
1375 if (root) { |
|
1376 aLists->AppendElement(nsIFrame::ChildList( |
|
1377 nsFrameList(root, nsLayoutUtils::GetLastSibling(root)), |
|
1378 nsIFrame::kPrincipalList)); |
|
1379 } |
|
1380 } |
|
1381 |
|
1382 GetChildLists(aLists); |
|
1383 } |
|
1384 |
|
1385 static nsIFrame* |
|
1386 GetActiveSelectionFrame(nsPresContext* aPresContext, nsIFrame* aFrame) |
|
1387 { |
|
1388 nsIContent* capturingContent = nsIPresShell::GetCapturingContent(); |
|
1389 if (capturingContent) { |
|
1390 nsIFrame* activeFrame = aPresContext->GetPrimaryFrameFor(capturingContent); |
|
1391 return activeFrame ? activeFrame : aFrame; |
|
1392 } |
|
1393 |
|
1394 return aFrame; |
|
1395 } |
|
1396 |
|
1397 int16_t |
|
1398 nsFrame::DisplaySelection(nsPresContext* aPresContext, bool isOkToTurnOn) |
|
1399 { |
|
1400 int16_t selType = nsISelectionController::SELECTION_OFF; |
|
1401 |
|
1402 nsCOMPtr<nsISelectionController> selCon; |
|
1403 nsresult result = GetSelectionController(aPresContext, getter_AddRefs(selCon)); |
|
1404 if (NS_SUCCEEDED(result) && selCon) { |
|
1405 result = selCon->GetDisplaySelection(&selType); |
|
1406 if (NS_SUCCEEDED(result) && (selType != nsISelectionController::SELECTION_OFF)) { |
|
1407 // Check whether style allows selection. |
|
1408 bool selectable; |
|
1409 IsSelectable(&selectable, nullptr); |
|
1410 if (!selectable) { |
|
1411 selType = nsISelectionController::SELECTION_OFF; |
|
1412 isOkToTurnOn = false; |
|
1413 } |
|
1414 } |
|
1415 if (isOkToTurnOn && (selType == nsISelectionController::SELECTION_OFF)) { |
|
1416 selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON); |
|
1417 selType = nsISelectionController::SELECTION_ON; |
|
1418 } |
|
1419 } |
|
1420 return selType; |
|
1421 } |
|
1422 |
|
1423 class nsDisplaySelectionOverlay : public nsDisplayItem { |
|
1424 public: |
|
1425 nsDisplaySelectionOverlay(nsDisplayListBuilder* aBuilder, |
|
1426 nsFrame* aFrame, int16_t aSelectionValue) |
|
1427 : nsDisplayItem(aBuilder, aFrame), mSelectionValue(aSelectionValue) { |
|
1428 MOZ_COUNT_CTOR(nsDisplaySelectionOverlay); |
|
1429 } |
|
1430 #ifdef NS_BUILD_REFCNT_LOGGING |
|
1431 virtual ~nsDisplaySelectionOverlay() { |
|
1432 MOZ_COUNT_DTOR(nsDisplaySelectionOverlay); |
|
1433 } |
|
1434 #endif |
|
1435 |
|
1436 virtual void Paint(nsDisplayListBuilder* aBuilder, |
|
1437 nsRenderingContext* aCtx) MOZ_OVERRIDE; |
|
1438 NS_DISPLAY_DECL_NAME("SelectionOverlay", TYPE_SELECTION_OVERLAY) |
|
1439 private: |
|
1440 int16_t mSelectionValue; |
|
1441 }; |
|
1442 |
|
1443 void nsDisplaySelectionOverlay::Paint(nsDisplayListBuilder* aBuilder, |
|
1444 nsRenderingContext* aCtx) |
|
1445 { |
|
1446 LookAndFeel::ColorID colorID; |
|
1447 if (mSelectionValue == nsISelectionController::SELECTION_ON) { |
|
1448 colorID = LookAndFeel::eColorID_TextSelectBackground; |
|
1449 } else if (mSelectionValue == nsISelectionController::SELECTION_ATTENTION) { |
|
1450 colorID = LookAndFeel::eColorID_TextSelectBackgroundAttention; |
|
1451 } else { |
|
1452 colorID = LookAndFeel::eColorID_TextSelectBackgroundDisabled; |
|
1453 } |
|
1454 |
|
1455 nscolor color = LookAndFeel::GetColor(colorID, NS_RGB(255, 255, 255)); |
|
1456 |
|
1457 gfxRGBA c(color); |
|
1458 c.a = .5; |
|
1459 |
|
1460 gfxContext *ctx = aCtx->ThebesContext(); |
|
1461 ctx->SetColor(c); |
|
1462 |
|
1463 nsIntRect pxRect = |
|
1464 mVisibleRect.ToOutsidePixels(mFrame->PresContext()->AppUnitsPerDevPixel()); |
|
1465 ctx->NewPath(); |
|
1466 ctx->Rectangle(gfxRect(pxRect.x, pxRect.y, pxRect.width, pxRect.height), true); |
|
1467 ctx->Fill(); |
|
1468 } |
|
1469 |
|
1470 /******************************************************** |
|
1471 * Refreshes each content's frame |
|
1472 *********************************************************/ |
|
1473 |
|
1474 void |
|
1475 nsFrame::DisplaySelectionOverlay(nsDisplayListBuilder* aBuilder, |
|
1476 nsDisplayList* aList, |
|
1477 uint16_t aContentType) |
|
1478 { |
|
1479 if (!IsSelected() || !IsVisibleForPainting(aBuilder)) |
|
1480 return; |
|
1481 |
|
1482 nsPresContext* presContext = PresContext(); |
|
1483 nsIPresShell *shell = presContext->PresShell(); |
|
1484 if (!shell) |
|
1485 return; |
|
1486 |
|
1487 int16_t displaySelection = shell->GetSelectionFlags(); |
|
1488 if (!(displaySelection & aContentType)) |
|
1489 return; |
|
1490 |
|
1491 const nsFrameSelection* frameSelection = GetConstFrameSelection(); |
|
1492 int16_t selectionValue = frameSelection->GetDisplaySelection(); |
|
1493 |
|
1494 if (selectionValue <= nsISelectionController::SELECTION_HIDDEN) |
|
1495 return; // selection is hidden or off |
|
1496 |
|
1497 nsIContent *newContent = mContent->GetParent(); |
|
1498 |
|
1499 //check to see if we are anonymous content |
|
1500 int32_t offset = 0; |
|
1501 if (newContent) { |
|
1502 // XXXbz there has GOT to be a better way of determining this! |
|
1503 offset = newContent->IndexOf(mContent); |
|
1504 } |
|
1505 |
|
1506 SelectionDetails *details; |
|
1507 //look up to see what selection(s) are on this frame |
|
1508 details = frameSelection->LookUpSelection(newContent, offset, 1, false); |
|
1509 if (!details) |
|
1510 return; |
|
1511 |
|
1512 bool normal = false; |
|
1513 while (details) { |
|
1514 if (details->mType == nsISelectionController::SELECTION_NORMAL) { |
|
1515 normal = true; |
|
1516 } |
|
1517 SelectionDetails *next = details->mNext; |
|
1518 delete details; |
|
1519 details = next; |
|
1520 } |
|
1521 |
|
1522 if (!normal && aContentType == nsISelectionDisplay::DISPLAY_IMAGES) { |
|
1523 // Don't overlay an image if it's not in the primary selection. |
|
1524 return; |
|
1525 } |
|
1526 |
|
1527 aList->AppendNewToTop(new (aBuilder) |
|
1528 nsDisplaySelectionOverlay(aBuilder, this, selectionValue)); |
|
1529 } |
|
1530 |
|
1531 void |
|
1532 nsFrame::DisplayOutlineUnconditional(nsDisplayListBuilder* aBuilder, |
|
1533 const nsDisplayListSet& aLists) |
|
1534 { |
|
1535 if (StyleOutline()->GetOutlineStyle() == NS_STYLE_BORDER_STYLE_NONE) |
|
1536 return; |
|
1537 |
|
1538 aLists.Outlines()->AppendNewToTop( |
|
1539 new (aBuilder) nsDisplayOutline(aBuilder, this)); |
|
1540 } |
|
1541 |
|
1542 void |
|
1543 nsFrame::DisplayOutline(nsDisplayListBuilder* aBuilder, |
|
1544 const nsDisplayListSet& aLists) |
|
1545 { |
|
1546 if (!IsVisibleForPainting(aBuilder)) |
|
1547 return; |
|
1548 |
|
1549 DisplayOutlineUnconditional(aBuilder, aLists); |
|
1550 } |
|
1551 |
|
1552 void |
|
1553 nsIFrame::DisplayCaret(nsDisplayListBuilder* aBuilder, |
|
1554 const nsRect& aDirtyRect, nsDisplayList* aList) |
|
1555 { |
|
1556 if (!IsVisibleForPainting(aBuilder)) |
|
1557 return; |
|
1558 |
|
1559 aList->AppendNewToTop( |
|
1560 new (aBuilder) nsDisplayCaret(aBuilder, this, aBuilder->GetCaret())); |
|
1561 } |
|
1562 |
|
1563 nscolor |
|
1564 nsIFrame::GetCaretColorAt(int32_t aOffset) |
|
1565 { |
|
1566 nscolor color = NS_RGB(0, 0, 0); |
|
1567 if (nsLayoutUtils::GetNativeTextColor(this, color)) |
|
1568 return color; |
|
1569 |
|
1570 // Use CSS text color. |
|
1571 return StyleColor()->mColor; |
|
1572 } |
|
1573 |
|
1574 bool |
|
1575 nsFrame::DisplayBackgroundUnconditional(nsDisplayListBuilder* aBuilder, |
|
1576 const nsDisplayListSet& aLists, |
|
1577 bool aForceBackground) |
|
1578 { |
|
1579 // Here we don't try to detect background propagation. Frames that might |
|
1580 // receive a propagated background should just set aForceBackground to |
|
1581 // true. |
|
1582 if (aBuilder->IsForEventDelivery() || aForceBackground || |
|
1583 !StyleBackground()->IsTransparent() || StyleDisplay()->mAppearance) { |
|
1584 return nsDisplayBackgroundImage::AppendBackgroundItemsToTop( |
|
1585 aBuilder, this, aLists.BorderBackground()); |
|
1586 } |
|
1587 return false; |
|
1588 } |
|
1589 |
|
1590 void |
|
1591 nsFrame::DisplayBorderBackgroundOutline(nsDisplayListBuilder* aBuilder, |
|
1592 const nsDisplayListSet& aLists, |
|
1593 bool aForceBackground) |
|
1594 { |
|
1595 // The visibility check belongs here since child elements have the |
|
1596 // opportunity to override the visibility property and display even if |
|
1597 // their parent is hidden. |
|
1598 if (!IsVisibleForPainting(aBuilder)) |
|
1599 return; |
|
1600 |
|
1601 nsCSSShadowArray* shadows = StyleBorder()->mBoxShadow; |
|
1602 if (shadows && shadows->HasShadowWithInset(false)) { |
|
1603 aLists.BorderBackground()->AppendNewToTop(new (aBuilder) |
|
1604 nsDisplayBoxShadowOuter(aBuilder, this)); |
|
1605 } |
|
1606 |
|
1607 bool bgIsThemed = DisplayBackgroundUnconditional(aBuilder, aLists, |
|
1608 aForceBackground); |
|
1609 |
|
1610 if (shadows && shadows->HasShadowWithInset(true)) { |
|
1611 aLists.BorderBackground()->AppendNewToTop(new (aBuilder) |
|
1612 nsDisplayBoxShadowInner(aBuilder, this)); |
|
1613 } |
|
1614 |
|
1615 // If there's a themed background, we should not create a border item. |
|
1616 // It won't be rendered. |
|
1617 if (!bgIsThemed && StyleBorder()->HasBorder()) { |
|
1618 aLists.BorderBackground()->AppendNewToTop(new (aBuilder) |
|
1619 nsDisplayBorder(aBuilder, this)); |
|
1620 } |
|
1621 |
|
1622 DisplayOutlineUnconditional(aBuilder, aLists); |
|
1623 } |
|
1624 |
|
1625 inline static bool IsSVGContentWithCSSClip(const nsIFrame *aFrame) |
|
1626 { |
|
1627 // The CSS spec says that the 'clip' property only applies to absolutely |
|
1628 // positioned elements, whereas the SVG spec says that it applies to SVG |
|
1629 // elements regardless of the value of the 'position' property. Here we obey |
|
1630 // the CSS spec for outer-<svg> (since that's what we generally do), but |
|
1631 // obey the SVG spec for other SVG elements to which 'clip' applies. |
|
1632 nsIAtom *tag = aFrame->GetContent()->Tag(); |
|
1633 return (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) && |
|
1634 (tag == nsGkAtoms::svg || tag == nsGkAtoms::foreignObject); |
|
1635 } |
|
1636 |
|
1637 bool |
|
1638 nsIFrame::GetClipPropClipRect(const nsStyleDisplay* aDisp, nsRect* aRect, |
|
1639 const nsSize& aSize) const |
|
1640 { |
|
1641 NS_PRECONDITION(aRect, "Must have aRect out parameter"); |
|
1642 |
|
1643 if (!(aDisp->mClipFlags & NS_STYLE_CLIP_RECT) || |
|
1644 !(aDisp->IsAbsolutelyPositioned(this) || IsSVGContentWithCSSClip(this))) { |
|
1645 return false; |
|
1646 } |
|
1647 |
|
1648 *aRect = aDisp->mClip; |
|
1649 if (NS_STYLE_CLIP_RIGHT_AUTO & aDisp->mClipFlags) { |
|
1650 aRect->width = aSize.width - aRect->x; |
|
1651 } |
|
1652 if (NS_STYLE_CLIP_BOTTOM_AUTO & aDisp->mClipFlags) { |
|
1653 aRect->height = aSize.height - aRect->y; |
|
1654 } |
|
1655 return true; |
|
1656 } |
|
1657 |
|
1658 /** |
|
1659 * If the CSS 'clip' property applies to this frame, set it up |
|
1660 * in aBuilder->ClipState() to clip all content descendants. Returns true |
|
1661 * if the property applies, and if so also returns the clip rect (relative |
|
1662 * to aFrame) in *aRect. |
|
1663 */ |
|
1664 static bool |
|
1665 ApplyClipPropClipping(nsDisplayListBuilder* aBuilder, |
|
1666 const nsIFrame* aFrame, |
|
1667 const nsStyleDisplay* aDisp, |
|
1668 nsRect* aRect, |
|
1669 DisplayListClipState::AutoSaveRestore& aClipState) |
|
1670 { |
|
1671 if (!aFrame->GetClipPropClipRect(aDisp, aRect, aFrame->GetSize())) |
|
1672 return false; |
|
1673 |
|
1674 nsRect clipRect = *aRect + aBuilder->ToReferenceFrame(aFrame); |
|
1675 aClipState.ClipContentDescendants(clipRect); |
|
1676 return true; |
|
1677 } |
|
1678 |
|
1679 /** |
|
1680 * If the CSS 'overflow' property applies to this frame, and is not |
|
1681 * handled by constructing a dedicated nsHTML/XULScrollFrame, set up clipping |
|
1682 * for that overflow in aBuilder->ClipState() to clip all containing-block |
|
1683 * descendants. |
|
1684 */ |
|
1685 static void |
|
1686 ApplyOverflowClipping(nsDisplayListBuilder* aBuilder, |
|
1687 const nsIFrame* aFrame, |
|
1688 const nsStyleDisplay* aDisp, |
|
1689 DisplayListClipState::AutoClipMultiple& aClipState) |
|
1690 { |
|
1691 // Only -moz-hidden-unscrollable is handled here (and 'hidden' for table |
|
1692 // frames, and any non-visible value for blocks in a paginated context). |
|
1693 // We allow -moz-hidden-unscrollable to apply to any kind of frame. This |
|
1694 // is required by comboboxes which make their display text (an inline frame) |
|
1695 // have clipping. |
|
1696 if (!nsFrame::ShouldApplyOverflowClipping(aFrame, aDisp)) { |
|
1697 return; |
|
1698 } |
|
1699 nsRect clipRect; |
|
1700 bool haveRadii = false; |
|
1701 nscoord radii[8]; |
|
1702 if (aFrame->StyleDisplay()->mOverflowClipBox == |
|
1703 NS_STYLE_OVERFLOW_CLIP_BOX_PADDING_BOX) { |
|
1704 clipRect = aFrame->GetPaddingRectRelativeToSelf() + |
|
1705 aBuilder->ToReferenceFrame(aFrame); |
|
1706 haveRadii = aFrame->GetPaddingBoxBorderRadii(radii); |
|
1707 } else { |
|
1708 clipRect = aFrame->GetContentRectRelativeToSelf() + |
|
1709 aBuilder->ToReferenceFrame(aFrame); |
|
1710 // XXX border-radius |
|
1711 } |
|
1712 aClipState.ClipContainingBlockDescendantsExtra(clipRect, haveRadii ? radii : nullptr); |
|
1713 } |
|
1714 |
|
1715 #ifdef DEBUG |
|
1716 static void PaintDebugBorder(nsIFrame* aFrame, nsRenderingContext* aCtx, |
|
1717 const nsRect& aDirtyRect, nsPoint aPt) { |
|
1718 nsRect r(aPt, aFrame->GetSize()); |
|
1719 if (aFrame->HasView()) { |
|
1720 aCtx->SetColor(NS_RGB(0,0,255)); |
|
1721 } else { |
|
1722 aCtx->SetColor(NS_RGB(255,0,0)); |
|
1723 } |
|
1724 aCtx->DrawRect(r); |
|
1725 } |
|
1726 |
|
1727 static void PaintEventTargetBorder(nsIFrame* aFrame, nsRenderingContext* aCtx, |
|
1728 const nsRect& aDirtyRect, nsPoint aPt) { |
|
1729 nsRect r(aPt, aFrame->GetSize()); |
|
1730 aCtx->SetColor(NS_RGB(128,0,128)); |
|
1731 aCtx->DrawRect(r); |
|
1732 } |
|
1733 |
|
1734 static void |
|
1735 DisplayDebugBorders(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, |
|
1736 const nsDisplayListSet& aLists) { |
|
1737 // Draw a border around the child |
|
1738 // REVIEW: From nsContainerFrame::PaintChild |
|
1739 if (nsFrame::GetShowFrameBorders() && !aFrame->GetRect().IsEmpty()) { |
|
1740 aLists.Outlines()->AppendNewToTop(new (aBuilder) |
|
1741 nsDisplayGeneric(aBuilder, aFrame, PaintDebugBorder, "DebugBorder", |
|
1742 nsDisplayItem::TYPE_DEBUG_BORDER)); |
|
1743 } |
|
1744 // Draw a border around the current event target |
|
1745 if (nsFrame::GetShowEventTargetFrameBorder() && |
|
1746 aFrame->PresContext()->PresShell()->GetDrawEventTargetFrame() == aFrame) { |
|
1747 aLists.Outlines()->AppendNewToTop(new (aBuilder) |
|
1748 nsDisplayGeneric(aBuilder, aFrame, PaintEventTargetBorder, "EventTargetBorder", |
|
1749 nsDisplayItem::TYPE_EVENT_TARGET_BORDER)); |
|
1750 } |
|
1751 } |
|
1752 #endif |
|
1753 |
|
1754 static nsresult |
|
1755 WrapPreserve3DListInternal(nsIFrame* aFrame, nsDisplayListBuilder *aBuilder, nsDisplayList *aList, nsDisplayList *aOutput, uint32_t& aIndex, nsDisplayList* aTemp) |
|
1756 { |
|
1757 if (aIndex > nsDisplayTransform::INDEX_MAX) { |
|
1758 return NS_OK; |
|
1759 } |
|
1760 |
|
1761 nsresult rv = NS_OK; |
|
1762 while (nsDisplayItem *item = aList->RemoveBottom()) { |
|
1763 nsIFrame *childFrame = item->Frame(); |
|
1764 |
|
1765 // We accumulate sequential items that aren't transforms into the 'temp' list |
|
1766 // and then flush this list into aOutput by wrapping the whole lot with a single |
|
1767 // nsDisplayTransform. |
|
1768 |
|
1769 if (childFrame->GetParent() && |
|
1770 (childFrame->GetParent()->Preserves3DChildren() || childFrame == aFrame)) { |
|
1771 switch (item->GetType()) { |
|
1772 case nsDisplayItem::TYPE_TRANSFORM: { |
|
1773 if (!aTemp->IsEmpty()) { |
|
1774 aOutput->AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, aFrame, aTemp, aIndex++)); |
|
1775 } |
|
1776 // Override item's clipping with our current clip state (if any). Since we're |
|
1777 // bubbling up a preserve-3d transformed child to a preserve-3d parent, |
|
1778 // we can be sure the child doesn't have clip state of its own. |
|
1779 NS_ASSERTION(!item->GetClip().HasClip(), "Unexpected clip on item"); |
|
1780 const DisplayItemClip* clip = aBuilder->ClipState().GetCurrentCombinedClip(aBuilder); |
|
1781 if (clip) { |
|
1782 item->SetClip(aBuilder, *clip); |
|
1783 } |
|
1784 aOutput->AppendToTop(item); |
|
1785 break; |
|
1786 } |
|
1787 case nsDisplayItem::TYPE_WRAP_LIST: { |
|
1788 nsDisplayWrapList *list = static_cast<nsDisplayWrapList*>(item); |
|
1789 rv = WrapPreserve3DListInternal(aFrame, aBuilder, |
|
1790 list->GetChildren(), aOutput, aIndex, aTemp); |
|
1791 list->~nsDisplayWrapList(); |
|
1792 break; |
|
1793 } |
|
1794 case nsDisplayItem::TYPE_OPACITY: { |
|
1795 if (!aTemp->IsEmpty()) { |
|
1796 aOutput->AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, aFrame, aTemp, aIndex++)); |
|
1797 } |
|
1798 nsDisplayOpacity *opacity = static_cast<nsDisplayOpacity*>(item); |
|
1799 nsDisplayList output; |
|
1800 // Call GetChildren, not GetSameCoordinateSystemChildren, because |
|
1801 // the preserve-3d children of 'opacity' are temporarily not in the |
|
1802 // same coordinate system as the opacity --- until this wrapping is done. |
|
1803 rv = WrapPreserve3DListInternal(aFrame, aBuilder, |
|
1804 opacity->GetChildren(), &output, aIndex, aTemp); |
|
1805 if (!aTemp->IsEmpty()) { |
|
1806 output.AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, aFrame, aTemp, aIndex++)); |
|
1807 } |
|
1808 opacity->GetChildren()->AppendToTop(&output); |
|
1809 opacity->UpdateBounds(aBuilder); |
|
1810 aOutput->AppendToTop(item); |
|
1811 break; |
|
1812 } |
|
1813 default: { |
|
1814 if (childFrame->StyleDisplay()->BackfaceIsHidden()) { |
|
1815 if (!aTemp->IsEmpty()) { |
|
1816 aOutput->AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, aFrame, aTemp, aIndex++)); |
|
1817 } |
|
1818 |
|
1819 aOutput->AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, childFrame, item, aIndex++)); |
|
1820 } else { |
|
1821 aTemp->AppendToTop(item); |
|
1822 } |
|
1823 break; |
|
1824 } |
|
1825 } |
|
1826 } else { |
|
1827 aTemp->AppendToTop(item); |
|
1828 } |
|
1829 |
|
1830 if (NS_FAILED(rv) || !item || aIndex > nsDisplayTransform::INDEX_MAX) |
|
1831 return rv; |
|
1832 } |
|
1833 |
|
1834 return NS_OK; |
|
1835 } |
|
1836 |
|
1837 static bool |
|
1838 IsScrollFrameActive(nsIScrollableFrame* aScrollableFrame) |
|
1839 { |
|
1840 return aScrollableFrame && aScrollableFrame->IsScrollingActive(); |
|
1841 } |
|
1842 |
|
1843 static nsresult |
|
1844 WrapPreserve3DList(nsIFrame* aFrame, nsDisplayListBuilder* aBuilder, nsDisplayList *aList) |
|
1845 { |
|
1846 uint32_t index = 0; |
|
1847 nsDisplayList temp; |
|
1848 nsDisplayList output; |
|
1849 nsresult rv = WrapPreserve3DListInternal(aFrame, aBuilder, aList, &output, index, &temp); |
|
1850 |
|
1851 if (!temp.IsEmpty()) { |
|
1852 output.AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, aFrame, &temp, index++)); |
|
1853 } |
|
1854 |
|
1855 aList->AppendToTop(&output); |
|
1856 return rv; |
|
1857 } |
|
1858 |
|
1859 class AutoSaveRestoreBlendMode |
|
1860 { |
|
1861 nsDisplayListBuilder& mBuilder; |
|
1862 bool AutoResetContainsBlendMode; |
|
1863 public: |
|
1864 AutoSaveRestoreBlendMode(nsDisplayListBuilder& aBuilder) |
|
1865 : mBuilder(aBuilder), |
|
1866 AutoResetContainsBlendMode(aBuilder.ContainsBlendMode()) { |
|
1867 } |
|
1868 |
|
1869 ~AutoSaveRestoreBlendMode() { |
|
1870 mBuilder.SetContainsBlendMode(AutoResetContainsBlendMode); |
|
1871 } |
|
1872 }; |
|
1873 |
|
1874 static void |
|
1875 CheckForTouchEventHandler(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) |
|
1876 { |
|
1877 nsIContent* content = aFrame->GetContent(); |
|
1878 if (!content) { |
|
1879 return; |
|
1880 } |
|
1881 EventListenerManager* elm = nsContentUtils::GetExistingListenerManagerForNode(content); |
|
1882 if (!elm) { |
|
1883 return; |
|
1884 } |
|
1885 if (elm->HasListenersFor(nsGkAtoms::ontouchstart) || |
|
1886 elm->HasListenersFor(nsGkAtoms::ontouchmove)) { |
|
1887 aBuilder->SetAncestorHasTouchEventHandler(true); |
|
1888 } |
|
1889 } |
|
1890 |
|
1891 void |
|
1892 nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, |
|
1893 const nsRect& aDirtyRect, |
|
1894 nsDisplayList* aList) { |
|
1895 if (GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE) |
|
1896 return; |
|
1897 |
|
1898 // Replaced elements have their visibility handled here, because |
|
1899 // they're visually atomic |
|
1900 if (IsFrameOfType(eReplaced) && !IsVisibleForPainting(aBuilder)) |
|
1901 return; |
|
1902 |
|
1903 const nsStyleDisplay* disp = StyleDisplay(); |
|
1904 // We can stop right away if this is a zero-opacity stacking context and |
|
1905 // we're painting, and we're not animating opacity. Don't do this |
|
1906 // if we're going to compute plugin geometry, since opacity-0 plugins |
|
1907 // need to have display items built for them. |
|
1908 if (disp->mOpacity == 0.0 && aBuilder->IsForPainting() && |
|
1909 !aBuilder->WillComputePluginGeometry() && |
|
1910 !(disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) && |
|
1911 !nsLayoutUtils::HasAnimations(mContent, eCSSProperty_opacity)) { |
|
1912 return; |
|
1913 } |
|
1914 |
|
1915 nsRect dirtyRect = aDirtyRect; |
|
1916 |
|
1917 bool inTransform = aBuilder->IsInTransform(); |
|
1918 bool isTransformed = IsTransformed(); |
|
1919 // reset blend mode so we can keep track if this stacking context needs have |
|
1920 // a nsDisplayBlendContainer. Set the blend mode back when the routine exits |
|
1921 // so we keep track if the parent stacking context needs a container too. |
|
1922 AutoSaveRestoreBlendMode autoRestoreBlendMode(*aBuilder); |
|
1923 aBuilder->SetContainsBlendMode(false); |
|
1924 |
|
1925 if (isTransformed) { |
|
1926 const nsRect overflow = GetVisualOverflowRectRelativeToSelf(); |
|
1927 if (aBuilder->IsForPainting() && |
|
1928 nsDisplayTransform::ShouldPrerenderTransformedContent(aBuilder, this)) { |
|
1929 dirtyRect = overflow; |
|
1930 } else { |
|
1931 if (overflow.IsEmpty() && !Preserves3DChildren()) { |
|
1932 return; |
|
1933 } |
|
1934 |
|
1935 nsPoint offset = aBuilder->ToReferenceFrame(this); |
|
1936 dirtyRect += offset; |
|
1937 |
|
1938 nsRect untransformedDirtyRect; |
|
1939 if (nsDisplayTransform::UntransformRect(dirtyRect, overflow, this, offset, &untransformedDirtyRect)) { |
|
1940 dirtyRect = untransformedDirtyRect; |
|
1941 } else { |
|
1942 NS_WARNING("Unable to untransform dirty rect!"); |
|
1943 // This should only happen if the transform is singular, in which case nothing is visible anyway |
|
1944 dirtyRect.SetEmpty(); |
|
1945 } |
|
1946 } |
|
1947 inTransform = true; |
|
1948 } |
|
1949 |
|
1950 bool useOpacity = HasVisualOpacity() && !nsSVGUtils::CanOptimizeOpacity(this); |
|
1951 bool useBlendMode = disp->mMixBlendMode != NS_STYLE_BLEND_NORMAL; |
|
1952 bool usingSVGEffects = nsSVGIntegrationUtils::UsingEffectsForFrame(this); |
|
1953 bool useStickyPosition = disp->mPosition == NS_STYLE_POSITION_STICKY && |
|
1954 IsScrollFrameActive(nsLayoutUtils::GetNearestScrollableFrame(GetParent(), |
|
1955 nsLayoutUtils::SCROLLABLE_SAME_DOC | |
|
1956 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN)); |
|
1957 |
|
1958 DisplayListClipState::AutoSaveRestore clipState(aBuilder); |
|
1959 |
|
1960 if (isTransformed || useOpacity || useBlendMode || usingSVGEffects || useStickyPosition) { |
|
1961 // We don't need to pass ancestor clipping down to our children; |
|
1962 // everything goes inside a display item's child list, and the display |
|
1963 // item itself will be clipped. |
|
1964 // For transforms we also need to clear ancestor clipping because it's |
|
1965 // relative to the wrong display item reference frame anyway. |
|
1966 clipState.Clear(); |
|
1967 } |
|
1968 |
|
1969 nsDisplayListCollection set; |
|
1970 { |
|
1971 nsDisplayListBuilder::AutoBuildingDisplayList rootSetter(aBuilder, true); |
|
1972 DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder); |
|
1973 nsDisplayListBuilder::AutoInTransformSetter |
|
1974 inTransformSetter(aBuilder, inTransform); |
|
1975 CheckForTouchEventHandler(aBuilder, this); |
|
1976 |
|
1977 if (usingSVGEffects) { |
|
1978 dirtyRect = |
|
1979 nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect); |
|
1980 } |
|
1981 |
|
1982 nsRect clipPropClip; |
|
1983 if (ApplyClipPropClipping(aBuilder, this, disp, &clipPropClip, |
|
1984 nestedClipState)) { |
|
1985 dirtyRect.IntersectRect(dirtyRect, clipPropClip); |
|
1986 } |
|
1987 |
|
1988 MarkAbsoluteFramesForDisplayList(aBuilder, dirtyRect); |
|
1989 |
|
1990 // Preserve3DChildren() also guarantees that applyAbsPosClipping and usingSVGEffects are false |
|
1991 // We only modify the preserve-3d rect if we are the top of a preserve-3d heirarchy |
|
1992 if (Preserves3DChildren()) { |
|
1993 aBuilder->MarkPreserve3DFramesForDisplayList(this, aDirtyRect); |
|
1994 } |
|
1995 |
|
1996 if (aBuilder->IsBuildingLayerEventRegions()) { |
|
1997 nsDisplayLayerEventRegions* eventRegions = |
|
1998 new (aBuilder) nsDisplayLayerEventRegions(aBuilder, this); |
|
1999 aBuilder->SetLayerEventRegions(eventRegions); |
|
2000 set.BorderBackground()->AppendNewToTop(eventRegions); |
|
2001 } |
|
2002 BuildDisplayList(aBuilder, dirtyRect, set); |
|
2003 } |
|
2004 |
|
2005 if (aBuilder->IsBackgroundOnly()) { |
|
2006 set.BlockBorderBackgrounds()->DeleteAll(); |
|
2007 set.Floats()->DeleteAll(); |
|
2008 set.Content()->DeleteAll(); |
|
2009 set.PositionedDescendants()->DeleteAll(); |
|
2010 set.Outlines()->DeleteAll(); |
|
2011 } |
|
2012 |
|
2013 // This z-order sort also sorts secondarily by content order. We need to do |
|
2014 // this so that boxes produced by the same element are placed together |
|
2015 // in the sort. Consider a position:relative inline element that breaks |
|
2016 // across lines and has absolutely positioned children; all the abs-pos |
|
2017 // children should be z-ordered after all the boxes for the position:relative |
|
2018 // element itself. |
|
2019 set.PositionedDescendants()->SortByZOrder(aBuilder, GetContent()); |
|
2020 |
|
2021 nsDisplayList resultList; |
|
2022 // Now follow the rules of http://www.w3.org/TR/CSS21/zindex.html |
|
2023 // 1,2: backgrounds and borders |
|
2024 resultList.AppendToTop(set.BorderBackground()); |
|
2025 // 3: negative z-index children. |
|
2026 for (;;) { |
|
2027 nsDisplayItem* item = set.PositionedDescendants()->GetBottom(); |
|
2028 if (item && item->ZIndex() < 0) { |
|
2029 set.PositionedDescendants()->RemoveBottom(); |
|
2030 resultList.AppendToTop(item); |
|
2031 continue; |
|
2032 } |
|
2033 break; |
|
2034 } |
|
2035 // 4: block backgrounds |
|
2036 resultList.AppendToTop(set.BlockBorderBackgrounds()); |
|
2037 // 5: floats |
|
2038 resultList.AppendToTop(set.Floats()); |
|
2039 // 7: general content |
|
2040 resultList.AppendToTop(set.Content()); |
|
2041 // 7.5: outlines, in content tree order. We need to sort by content order |
|
2042 // because an element with outline that breaks and has children with outline |
|
2043 // might have placed child outline items between its own outline items. |
|
2044 // The element's outline items need to all come before any child outline |
|
2045 // items. |
|
2046 nsIContent* content = GetContent(); |
|
2047 if (!content) { |
|
2048 content = PresContext()->Document()->GetRootElement(); |
|
2049 } |
|
2050 if (content) { |
|
2051 set.Outlines()->SortByContentOrder(aBuilder, content); |
|
2052 } |
|
2053 #ifdef DEBUG |
|
2054 DisplayDebugBorders(aBuilder, this, set); |
|
2055 #endif |
|
2056 resultList.AppendToTop(set.Outlines()); |
|
2057 // 8, 9: non-negative z-index children |
|
2058 resultList.AppendToTop(set.PositionedDescendants()); |
|
2059 |
|
2060 if (!isTransformed) { |
|
2061 // Restore saved clip state now so that any display items we create below |
|
2062 // are clipped properly. |
|
2063 clipState.Restore(); |
|
2064 } |
|
2065 |
|
2066 /* If there are any SVG effects, wrap the list up in an SVG effects item |
|
2067 * (which also handles CSS group opacity). Note that we create an SVG effects |
|
2068 * item even if resultList is empty, since a filter can produce graphical |
|
2069 * output even if the element being filtered wouldn't otherwise do so. |
|
2070 */ |
|
2071 if (usingSVGEffects) { |
|
2072 /* List now emptied, so add the new list to the top. */ |
|
2073 resultList.AppendNewToTop( |
|
2074 new (aBuilder) nsDisplaySVGEffects(aBuilder, this, &resultList)); |
|
2075 } |
|
2076 /* Else, if the list is non-empty and there is CSS group opacity without SVG |
|
2077 * effects, wrap it up in an opacity item. |
|
2078 */ |
|
2079 else if (useOpacity && !resultList.IsEmpty()) { |
|
2080 resultList.AppendNewToTop( |
|
2081 new (aBuilder) nsDisplayOpacity(aBuilder, this, &resultList)); |
|
2082 } |
|
2083 /* If we have sticky positioning, wrap it in a sticky position item. |
|
2084 */ |
|
2085 if (useStickyPosition) { |
|
2086 resultList.AppendNewToTop( |
|
2087 new (aBuilder) nsDisplayStickyPosition(aBuilder, this, &resultList)); |
|
2088 } |
|
2089 |
|
2090 /* If we're going to apply a transformation and don't have preserve-3d set, wrap |
|
2091 * everything in an nsDisplayTransform. If there's nothing in the list, don't add |
|
2092 * anything. |
|
2093 * |
|
2094 * For the preserve-3d case we want to individually wrap every child in the list with |
|
2095 * a separate nsDisplayTransform instead. When the child is already an nsDisplayTransform, |
|
2096 * we can skip this step, as the computed transform will already include our own. |
|
2097 * |
|
2098 * We also traverse into sublists created by nsDisplayWrapList or nsDisplayOpacity, so that |
|
2099 * we find all the correct children. |
|
2100 */ |
|
2101 if (isTransformed && !resultList.IsEmpty()) { |
|
2102 // Restore clip state now so nsDisplayTransform is clipped properly. |
|
2103 clipState.Restore(); |
|
2104 |
|
2105 if (Preserves3DChildren()) { |
|
2106 WrapPreserve3DList(this, aBuilder, &resultList); |
|
2107 } else { |
|
2108 resultList.AppendNewToTop( |
|
2109 new (aBuilder) nsDisplayTransform(aBuilder, this, &resultList)); |
|
2110 } |
|
2111 } |
|
2112 |
|
2113 /* If adding both a nsDisplayBlendContainer and a nsDisplayMixBlendMode to the |
|
2114 * same list, the nsDisplayBlendContainer should be added first. This only |
|
2115 * happens when the element creating this stacking context has mix-blend-mode |
|
2116 * and also contains a child which has mix-blend-mode. |
|
2117 * The nsDisplayBlendContainer must be added to the list first, so it does not |
|
2118 * isolate the containing element blending as well. |
|
2119 */ |
|
2120 |
|
2121 if (aBuilder->ContainsBlendMode()) { |
|
2122 resultList.AppendNewToTop( |
|
2123 new (aBuilder) nsDisplayBlendContainer(aBuilder, this, &resultList)); |
|
2124 } |
|
2125 |
|
2126 /* If there's blending, wrap up the list in a blend-mode item. Note |
|
2127 * that opacity can be applied before blending as the blend color is |
|
2128 * not affected by foreground opacity (only background alpha). |
|
2129 */ |
|
2130 |
|
2131 if (useBlendMode && !resultList.IsEmpty()) { |
|
2132 resultList.AppendNewToTop( |
|
2133 new (aBuilder) nsDisplayMixBlendMode(aBuilder, this, &resultList)); |
|
2134 } |
|
2135 |
|
2136 CreateOwnLayerIfNeeded(aBuilder, &resultList); |
|
2137 |
|
2138 aList->AppendToTop(&resultList); |
|
2139 } |
|
2140 |
|
2141 static nsDisplayItem* |
|
2142 WrapInWrapList(nsDisplayListBuilder* aBuilder, |
|
2143 nsIFrame* aFrame, nsDisplayList* aList) |
|
2144 { |
|
2145 nsDisplayItem* item = aList->GetBottom(); |
|
2146 if (!item || item->GetAbove() || item->Frame() != aFrame) { |
|
2147 return new (aBuilder) nsDisplayWrapList(aBuilder, aFrame, aList); |
|
2148 } |
|
2149 aList->RemoveBottom(); |
|
2150 return item; |
|
2151 } |
|
2152 |
|
2153 void |
|
2154 nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, |
|
2155 nsIFrame* aChild, |
|
2156 const nsRect& aDirtyRect, |
|
2157 const nsDisplayListSet& aLists, |
|
2158 uint32_t aFlags) { |
|
2159 // If painting is restricted to just the background of the top level frame, |
|
2160 // then we have nothing to do here. |
|
2161 if (aBuilder->IsBackgroundOnly()) |
|
2162 return; |
|
2163 |
|
2164 nsIFrame* child = aChild; |
|
2165 if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE) |
|
2166 return; |
|
2167 |
|
2168 bool isSVG = (child->GetStateBits() & NS_FRAME_SVG_LAYOUT); |
|
2169 |
|
2170 // true if this is a real or pseudo stacking context |
|
2171 bool pseudoStackingContext = |
|
2172 (aFlags & DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT) != 0; |
|
2173 if (!isSVG && |
|
2174 (aFlags & DISPLAY_CHILD_INLINE) && |
|
2175 !child->IsFrameOfType(eLineParticipant)) { |
|
2176 // child is a non-inline frame in an inline context, i.e., |
|
2177 // it acts like inline-block or inline-table. Therefore it is a |
|
2178 // pseudo-stacking-context. |
|
2179 pseudoStackingContext = true; |
|
2180 } |
|
2181 |
|
2182 // dirty rect in child-relative coordinates |
|
2183 nsRect dirty = aDirtyRect - child->GetOffsetTo(this); |
|
2184 |
|
2185 nsIAtom* childType = child->GetType(); |
|
2186 nsDisplayListBuilder::OutOfFlowDisplayData* savedOutOfFlowData = nullptr; |
|
2187 if (childType == nsGkAtoms::placeholderFrame) { |
|
2188 nsPlaceholderFrame* placeholder = static_cast<nsPlaceholderFrame*>(child); |
|
2189 child = placeholder->GetOutOfFlowFrame(); |
|
2190 NS_ASSERTION(child, "No out of flow frame?"); |
|
2191 // If 'child' is a pushed float then it's owned by a block that's not an |
|
2192 // ancestor of the placeholder, and it will be painted by that block and |
|
2193 // should not be painted through the placeholder. |
|
2194 if (!child || nsLayoutUtils::IsPopup(child) || |
|
2195 (child->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT)) |
|
2196 return; |
|
2197 // Make sure that any attempt to use childType below is disappointed. We |
|
2198 // could call GetType again but since we don't currently need it, let's |
|
2199 // avoid the virtual call. |
|
2200 childType = nullptr; |
|
2201 // Recheck NS_FRAME_TOO_DEEP_IN_FRAME_TREE |
|
2202 if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE) |
|
2203 return; |
|
2204 savedOutOfFlowData = static_cast<nsDisplayListBuilder::OutOfFlowDisplayData*> |
|
2205 (child->Properties().Get(nsDisplayListBuilder::OutOfFlowDisplayDataProperty())); |
|
2206 if (savedOutOfFlowData) { |
|
2207 dirty = savedOutOfFlowData->mDirtyRect; |
|
2208 } else { |
|
2209 // The out-of-flow frame did not intersect the dirty area. We may still |
|
2210 // need to traverse into it, since it may contain placeholders we need |
|
2211 // to enter to reach other out-of-flow frames that are visible. |
|
2212 dirty.SetEmpty(); |
|
2213 } |
|
2214 pseudoStackingContext = true; |
|
2215 } |
|
2216 if (child->Preserves3D()) { |
|
2217 nsRect* savedDirty = static_cast<nsRect*> |
|
2218 (child->Properties().Get(nsDisplayListBuilder::Preserve3DDirtyRectProperty())); |
|
2219 if (savedDirty) { |
|
2220 dirty = *savedDirty; |
|
2221 } else { |
|
2222 dirty.SetEmpty(); |
|
2223 } |
|
2224 } |
|
2225 |
|
2226 NS_ASSERTION(childType != nsGkAtoms::placeholderFrame, |
|
2227 "Should have dealt with placeholders already"); |
|
2228 if (aBuilder->GetSelectedFramesOnly() && |
|
2229 child->IsLeaf() && |
|
2230 !aChild->IsSelected()) { |
|
2231 return; |
|
2232 } |
|
2233 |
|
2234 if (aBuilder->GetIncludeAllOutOfFlows() && |
|
2235 (child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) { |
|
2236 dirty = child->GetVisualOverflowRect(); |
|
2237 } else if (!(child->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) { |
|
2238 // No need to descend into child to catch placeholders for visible |
|
2239 // positioned stuff. So see if we can short-circuit frame traversal here. |
|
2240 |
|
2241 // We can stop if child's frame subtree's intersection with the |
|
2242 // dirty area is empty. |
|
2243 // If the child is a scrollframe that we want to ignore, then we need |
|
2244 // to descend into it because its scrolled child may intersect the dirty |
|
2245 // area even if the scrollframe itself doesn't. |
|
2246 // There are cases where the "ignore scroll frame" on the builder is not set |
|
2247 // correctly, and so we additionally want to catch cases where the child is |
|
2248 // a root scrollframe and we are ignoring scrolling on the viewport. |
|
2249 nsIPresShell* shell = PresContext()->PresShell(); |
|
2250 bool keepDescending = child == aBuilder->GetIgnoreScrollFrame() || |
|
2251 (shell->IgnoringViewportScrolling() && child == shell->GetRootScrollFrame()); |
|
2252 if (!keepDescending) { |
|
2253 nsRect childDirty; |
|
2254 if (!childDirty.IntersectRect(dirty, child->GetVisualOverflowRect())) |
|
2255 return; |
|
2256 // Usually we could set dirty to childDirty now but there's no |
|
2257 // benefit, and it can be confusing. It can especially confuse |
|
2258 // situations where we're going to ignore a scrollframe's clipping; |
|
2259 // we wouldn't want to clip the dirty area to the scrollframe's |
|
2260 // bounds in that case. |
|
2261 } |
|
2262 } |
|
2263 |
|
2264 // XXX need to have inline-block and inline-table set pseudoStackingContext |
|
2265 |
|
2266 const nsStyleDisplay* ourDisp = StyleDisplay(); |
|
2267 // REVIEW: Taken from nsBoxFrame::Paint |
|
2268 // Don't paint our children if the theme object is a leaf. |
|
2269 if (IsThemed(ourDisp) && |
|
2270 !PresContext()->GetTheme()->WidgetIsContainer(ourDisp->mAppearance)) |
|
2271 return; |
|
2272 |
|
2273 // Child is composited if it's transformed, partially transparent, or has |
|
2274 // SVG effects or a blend mode.. |
|
2275 const nsStyleDisplay* disp = child->StyleDisplay(); |
|
2276 const nsStylePosition* pos = child->StylePosition(); |
|
2277 bool isVisuallyAtomic = child->HasOpacity() |
|
2278 || child->IsTransformed() |
|
2279 // strictly speaking, 'perspective' doesn't require visual atomicity, |
|
2280 // but the spec says it acts like the rest of these |
|
2281 || disp->mChildPerspective.GetUnit() == eStyleUnit_Coord |
|
2282 || disp->mMixBlendMode != NS_STYLE_BLEND_NORMAL |
|
2283 || nsSVGIntegrationUtils::UsingEffectsForFrame(child); |
|
2284 |
|
2285 bool isPositioned = disp->IsPositioned(child); |
|
2286 bool isStackingContext = |
|
2287 (isPositioned && (disp->mPosition == NS_STYLE_POSITION_STICKY || |
|
2288 pos->mZIndex.GetUnit() == eStyleUnit_Integer)) || |
|
2289 (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT) || |
|
2290 isVisuallyAtomic || (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT); |
|
2291 |
|
2292 if (isVisuallyAtomic || isPositioned || (!isSVG && disp->IsFloating(child)) || |
|
2293 ((disp->mClipFlags & NS_STYLE_CLIP_RECT) && |
|
2294 IsSVGContentWithCSSClip(child)) || |
|
2295 (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT) || |
|
2296 (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) { |
|
2297 // If you change this, also change IsPseudoStackingContextFromStyle() |
|
2298 pseudoStackingContext = true; |
|
2299 } |
|
2300 NS_ASSERTION(!isStackingContext || pseudoStackingContext, |
|
2301 "Stacking contexts must also be pseudo-stacking-contexts"); |
|
2302 |
|
2303 bool isInFixedPos = aBuilder->IsInFixedPos() || |
|
2304 (isPositioned && |
|
2305 disp->mPosition == NS_STYLE_POSITION_FIXED && |
|
2306 nsLayoutUtils::IsReallyFixedPos(child)); |
|
2307 nsDisplayListBuilder::AutoInFixedPosSetter |
|
2308 buildingInFixedPos(aBuilder, isInFixedPos); |
|
2309 |
|
2310 nsDisplayListBuilder::AutoBuildingDisplayList |
|
2311 buildingForChild(aBuilder, child, pseudoStackingContext); |
|
2312 DisplayListClipState::AutoClipMultiple clipState(aBuilder); |
|
2313 CheckForTouchEventHandler(aBuilder, child); |
|
2314 |
|
2315 if (savedOutOfFlowData) { |
|
2316 clipState.SetClipForContainingBlockDescendants( |
|
2317 &savedOutOfFlowData->mContainingBlockClip); |
|
2318 } |
|
2319 |
|
2320 // Setup clipping for the parent's overflow:-moz-hidden-unscrollable, |
|
2321 // or overflow:hidden on elements that don't support scrolling (and therefore |
|
2322 // don't create nsHTML/XULScrollFrame). This clipping needs to not clip |
|
2323 // anything directly rendered by the parent, only the rendering of its |
|
2324 // children. |
|
2325 // Don't use overflowClip to restrict the dirty rect, since some of the |
|
2326 // descendants may not be clipped by it. Even if we end up with unnecessary |
|
2327 // display items, they'll be pruned during ComputeVisibility. |
|
2328 nsIFrame* parent = child->GetParent(); |
|
2329 const nsStyleDisplay* parentDisp = |
|
2330 parent == this ? ourDisp : parent->StyleDisplay(); |
|
2331 ApplyOverflowClipping(aBuilder, parent, parentDisp, clipState); |
|
2332 |
|
2333 nsDisplayList list; |
|
2334 nsDisplayList extraPositionedDescendants; |
|
2335 if (isStackingContext) { |
|
2336 if (disp->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { |
|
2337 aBuilder->SetContainsBlendMode(true); |
|
2338 } |
|
2339 // True stacking context. |
|
2340 // For stacking contexts, BuildDisplayListForStackingContext handles |
|
2341 // clipping and MarkAbsoluteFramesForDisplayList. |
|
2342 child->BuildDisplayListForStackingContext(aBuilder, dirty, &list); |
|
2343 aBuilder->DisplayCaret(child, dirty, &list); |
|
2344 } else { |
|
2345 nsRect clipRect; |
|
2346 if (ApplyClipPropClipping(aBuilder, child, disp, &clipRect, clipState)) { |
|
2347 // clipRect is in builder-reference-frame coordinates, |
|
2348 // dirty/clippedDirtyRect are in child coordinates |
|
2349 dirty.IntersectRect(dirty, clipRect); |
|
2350 } |
|
2351 |
|
2352 child->MarkAbsoluteFramesForDisplayList(aBuilder, dirty); |
|
2353 |
|
2354 if (!pseudoStackingContext) { |
|
2355 // THIS IS THE COMMON CASE. |
|
2356 // Not a pseudo or real stacking context. Do the simple thing and |
|
2357 // return early. |
|
2358 nsDisplayLayerEventRegions* eventRegions = aBuilder->GetLayerEventRegions(); |
|
2359 if (eventRegions) { |
|
2360 eventRegions->AddFrame(aBuilder, child); |
|
2361 } |
|
2362 child->BuildDisplayList(aBuilder, dirty, aLists); |
|
2363 aBuilder->DisplayCaret(child, dirty, aLists.Content()); |
|
2364 #ifdef DEBUG |
|
2365 DisplayDebugBorders(aBuilder, child, aLists); |
|
2366 #endif |
|
2367 return; |
|
2368 } |
|
2369 |
|
2370 // A pseudo-stacking context (e.g., a positioned element with z-index auto). |
|
2371 // We allow positioned descendants of the child to escape to our parent |
|
2372 // stacking context's positioned descendant list, because they might be |
|
2373 // z-index:non-auto |
|
2374 nsDisplayListCollection pseudoStack; |
|
2375 if (aBuilder->IsBuildingLayerEventRegions()) { |
|
2376 nsDisplayLayerEventRegions* eventRegions = |
|
2377 new (aBuilder) nsDisplayLayerEventRegions(aBuilder, this); |
|
2378 aBuilder->SetLayerEventRegions(eventRegions); |
|
2379 pseudoStack.BorderBackground()->AppendNewToTop(eventRegions); |
|
2380 } |
|
2381 child->BuildDisplayList(aBuilder, dirty, pseudoStack); |
|
2382 aBuilder->DisplayCaret(child, dirty, pseudoStack.Content()); |
|
2383 |
|
2384 list.AppendToTop(pseudoStack.BorderBackground()); |
|
2385 list.AppendToTop(pseudoStack.BlockBorderBackgrounds()); |
|
2386 list.AppendToTop(pseudoStack.Floats()); |
|
2387 list.AppendToTop(pseudoStack.Content()); |
|
2388 list.AppendToTop(pseudoStack.Outlines()); |
|
2389 extraPositionedDescendants.AppendToTop(pseudoStack.PositionedDescendants()); |
|
2390 #ifdef DEBUG |
|
2391 DisplayDebugBorders(aBuilder, child, aLists); |
|
2392 #endif |
|
2393 } |
|
2394 |
|
2395 // Clear clip rect for the construction of the items below. Since we're |
|
2396 // clipping all their contents, they themselves don't need to be clipped. |
|
2397 clipState.Clear(); |
|
2398 |
|
2399 if (isPositioned || isVisuallyAtomic || |
|
2400 (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) { |
|
2401 // Genuine stacking contexts, and positioned pseudo-stacking-contexts, |
|
2402 // go in this level. |
|
2403 if (!list.IsEmpty()) { |
|
2404 nsDisplayItem* item = WrapInWrapList(aBuilder, child, &list); |
|
2405 if (isSVG) { |
|
2406 aLists.Content()->AppendNewToTop(item); |
|
2407 } else { |
|
2408 aLists.PositionedDescendants()->AppendNewToTop(item); |
|
2409 } |
|
2410 } |
|
2411 } else if (!isSVG && disp->IsFloating(child)) { |
|
2412 if (!list.IsEmpty()) { |
|
2413 aLists.Floats()->AppendNewToTop(WrapInWrapList(aBuilder, child, &list)); |
|
2414 } |
|
2415 } else { |
|
2416 aLists.Content()->AppendToTop(&list); |
|
2417 } |
|
2418 // We delay placing the positioned descendants of positioned frames to here, |
|
2419 // because in the absence of z-index this is the correct order for them. |
|
2420 // This doesn't affect correctness because the positioned descendants list |
|
2421 // is sorted by z-order and content in BuildDisplayListForStackingContext, |
|
2422 // but it means that sort routine needs to do less work. |
|
2423 aLists.PositionedDescendants()->AppendToTop(&extraPositionedDescendants); |
|
2424 } |
|
2425 |
|
2426 void |
|
2427 nsIFrame::MarkAbsoluteFramesForDisplayList(nsDisplayListBuilder* aBuilder, |
|
2428 const nsRect& aDirtyRect) |
|
2429 { |
|
2430 if (IsAbsoluteContainer()) { |
|
2431 aBuilder->MarkFramesForDisplayList(this, GetAbsoluteContainingBlock()->GetChildList(), aDirtyRect); |
|
2432 } |
|
2433 } |
|
2434 |
|
2435 nsresult |
|
2436 nsFrame::GetContentForEvent(WidgetEvent* aEvent, |
|
2437 nsIContent** aContent) |
|
2438 { |
|
2439 nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(this); |
|
2440 *aContent = f->GetContent(); |
|
2441 NS_IF_ADDREF(*aContent); |
|
2442 return NS_OK; |
|
2443 } |
|
2444 |
|
2445 void |
|
2446 nsFrame::FireDOMEvent(const nsAString& aDOMEventName, nsIContent *aContent) |
|
2447 { |
|
2448 nsIContent* target = aContent ? aContent : mContent; |
|
2449 |
|
2450 if (target) { |
|
2451 nsRefPtr<AsyncEventDispatcher> asyncDispatcher = |
|
2452 new AsyncEventDispatcher(target, aDOMEventName, true, false); |
|
2453 DebugOnly<nsresult> rv = asyncDispatcher->PostDOMEvent(); |
|
2454 NS_ASSERTION(NS_SUCCEEDED(rv), "AsyncEventDispatcher failed to dispatch"); |
|
2455 } |
|
2456 } |
|
2457 |
|
2458 nsresult |
|
2459 nsFrame::HandleEvent(nsPresContext* aPresContext, |
|
2460 WidgetGUIEvent* aEvent, |
|
2461 nsEventStatus* aEventStatus) |
|
2462 { |
|
2463 |
|
2464 if (aEvent->message == NS_MOUSE_MOVE) { |
|
2465 // XXX If the second argument of HandleDrag() is WidgetMouseEvent, |
|
2466 // the implementation becomes simpler. |
|
2467 return HandleDrag(aPresContext, aEvent, aEventStatus); |
|
2468 } |
|
2469 |
|
2470 if ((aEvent->eventStructType == NS_MOUSE_EVENT && |
|
2471 aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) || |
|
2472 aEvent->eventStructType == NS_TOUCH_EVENT) { |
|
2473 if (aEvent->message == NS_MOUSE_BUTTON_DOWN || aEvent->message == NS_TOUCH_START) { |
|
2474 HandlePress(aPresContext, aEvent, aEventStatus); |
|
2475 } else if (aEvent->message == NS_MOUSE_BUTTON_UP || aEvent->message == NS_TOUCH_END) { |
|
2476 HandleRelease(aPresContext, aEvent, aEventStatus); |
|
2477 } |
|
2478 } |
|
2479 return NS_OK; |
|
2480 } |
|
2481 |
|
2482 NS_IMETHODIMP |
|
2483 nsFrame::GetDataForTableSelection(const nsFrameSelection* aFrameSelection, |
|
2484 nsIPresShell* aPresShell, |
|
2485 WidgetMouseEvent* aMouseEvent, |
|
2486 nsIContent** aParentContent, |
|
2487 int32_t* aContentOffset, |
|
2488 int32_t* aTarget) |
|
2489 { |
|
2490 if (!aFrameSelection || !aPresShell || !aMouseEvent || !aParentContent || !aContentOffset || !aTarget) |
|
2491 return NS_ERROR_NULL_POINTER; |
|
2492 |
|
2493 *aParentContent = nullptr; |
|
2494 *aContentOffset = 0; |
|
2495 *aTarget = 0; |
|
2496 |
|
2497 int16_t displaySelection = aPresShell->GetSelectionFlags(); |
|
2498 |
|
2499 bool selectingTableCells = aFrameSelection->GetTableCellSelection(); |
|
2500 |
|
2501 // DISPLAY_ALL means we're in an editor. |
|
2502 // If already in cell selection mode, |
|
2503 // continue selecting with mouse drag or end on mouse up, |
|
2504 // or when using shift key to extend block of cells |
|
2505 // (Mouse down does normal selection unless Ctrl/Cmd is pressed) |
|
2506 bool doTableSelection = |
|
2507 displaySelection == nsISelectionDisplay::DISPLAY_ALL && selectingTableCells && |
|
2508 (aMouseEvent->message == NS_MOUSE_MOVE || |
|
2509 (aMouseEvent->message == NS_MOUSE_BUTTON_UP && |
|
2510 aMouseEvent->button == WidgetMouseEvent::eLeftButton) || |
|
2511 aMouseEvent->IsShift()); |
|
2512 |
|
2513 if (!doTableSelection) |
|
2514 { |
|
2515 // In Browser, special 'table selection' key must be pressed for table selection |
|
2516 // or when just Shift is pressed and we're already in table/cell selection mode |
|
2517 #ifdef XP_MACOSX |
|
2518 doTableSelection = aMouseEvent->IsMeta() || (aMouseEvent->IsShift() && selectingTableCells); |
|
2519 #else |
|
2520 doTableSelection = aMouseEvent->IsControl() || (aMouseEvent->IsShift() && selectingTableCells); |
|
2521 #endif |
|
2522 } |
|
2523 if (!doTableSelection) |
|
2524 return NS_OK; |
|
2525 |
|
2526 // Get the cell frame or table frame (or parent) of the current content node |
|
2527 nsIFrame *frame = this; |
|
2528 bool foundCell = false; |
|
2529 bool foundTable = false; |
|
2530 |
|
2531 // Get the limiting node to stop parent frame search |
|
2532 nsIContent* limiter = aFrameSelection->GetLimiter(); |
|
2533 |
|
2534 // If our content node is an ancestor of the limiting node, |
|
2535 // we should stop the search right now. |
|
2536 if (limiter && nsContentUtils::ContentIsDescendantOf(limiter, GetContent())) |
|
2537 return NS_OK; |
|
2538 |
|
2539 //We don't initiate row/col selection from here now, |
|
2540 // but we may in future |
|
2541 //bool selectColumn = false; |
|
2542 //bool selectRow = false; |
|
2543 |
|
2544 while (frame) |
|
2545 { |
|
2546 // Check for a table cell by querying to a known CellFrame interface |
|
2547 nsITableCellLayout *cellElement = do_QueryFrame(frame); |
|
2548 if (cellElement) |
|
2549 { |
|
2550 foundCell = true; |
|
2551 //TODO: If we want to use proximity to top or left border |
|
2552 // for row and column selection, this is the place to do it |
|
2553 break; |
|
2554 } |
|
2555 else |
|
2556 { |
|
2557 // If not a cell, check for table |
|
2558 // This will happen when starting frame is the table or child of a table, |
|
2559 // such as a row (we were inbetween cells or in table border) |
|
2560 nsTableOuterFrame *tableFrame = do_QueryFrame(frame); |
|
2561 if (tableFrame) |
|
2562 { |
|
2563 foundTable = true; |
|
2564 //TODO: How can we select row when along left table edge |
|
2565 // or select column when along top edge? |
|
2566 break; |
|
2567 } else { |
|
2568 frame = frame->GetParent(); |
|
2569 // Stop if we have hit the selection's limiting content node |
|
2570 if (frame && frame->GetContent() == limiter) |
|
2571 break; |
|
2572 } |
|
2573 } |
|
2574 } |
|
2575 // We aren't in a cell or table |
|
2576 if (!foundCell && !foundTable) return NS_OK; |
|
2577 |
|
2578 nsIContent* tableOrCellContent = frame->GetContent(); |
|
2579 if (!tableOrCellContent) return NS_ERROR_FAILURE; |
|
2580 |
|
2581 nsCOMPtr<nsIContent> parentContent = tableOrCellContent->GetParent(); |
|
2582 if (!parentContent) return NS_ERROR_FAILURE; |
|
2583 |
|
2584 int32_t offset = parentContent->IndexOf(tableOrCellContent); |
|
2585 // Not likely? |
|
2586 if (offset < 0) return NS_ERROR_FAILURE; |
|
2587 |
|
2588 // Everything is OK -- set the return values |
|
2589 *aParentContent = parentContent; |
|
2590 NS_ADDREF(*aParentContent); |
|
2591 |
|
2592 *aContentOffset = offset; |
|
2593 |
|
2594 #if 0 |
|
2595 if (selectRow) |
|
2596 *aTarget = nsISelectionPrivate::TABLESELECTION_ROW; |
|
2597 else if (selectColumn) |
|
2598 *aTarget = nsISelectionPrivate::TABLESELECTION_COLUMN; |
|
2599 else |
|
2600 #endif |
|
2601 if (foundCell) |
|
2602 *aTarget = nsISelectionPrivate::TABLESELECTION_CELL; |
|
2603 else if (foundTable) |
|
2604 *aTarget = nsISelectionPrivate::TABLESELECTION_TABLE; |
|
2605 |
|
2606 return NS_OK; |
|
2607 } |
|
2608 |
|
2609 nsresult |
|
2610 nsFrame::IsSelectable(bool* aSelectable, uint8_t* aSelectStyle) const |
|
2611 { |
|
2612 if (!aSelectable) //it's ok if aSelectStyle is null |
|
2613 return NS_ERROR_NULL_POINTER; |
|
2614 |
|
2615 // Like 'visibility', we must check all the parents: if a parent |
|
2616 // is not selectable, none of its children is selectable. |
|
2617 // |
|
2618 // The -moz-all value acts similarly: if a frame has 'user-select:-moz-all', |
|
2619 // all its children are selectable, even those with 'user-select:none'. |
|
2620 // |
|
2621 // As a result, if 'none' and '-moz-all' are not present in the frame hierarchy, |
|
2622 // aSelectStyle returns the first style that is not AUTO. If these values |
|
2623 // are present in the frame hierarchy, aSelectStyle returns the style of the |
|
2624 // topmost parent that has either 'none' or '-moz-all'. |
|
2625 // |
|
2626 // For instance, if the frame hierarchy is: |
|
2627 // AUTO -> _MOZ_ALL -> NONE -> TEXT, the returned value is _MOZ_ALL |
|
2628 // TEXT -> NONE -> AUTO -> _MOZ_ALL, the returned value is TEXT |
|
2629 // _MOZ_ALL -> TEXT -> AUTO -> AUTO, the returned value is _MOZ_ALL |
|
2630 // AUTO -> CELL -> TEXT -> AUTO, the returned value is TEXT |
|
2631 // |
|
2632 uint8_t selectStyle = NS_STYLE_USER_SELECT_AUTO; |
|
2633 nsIFrame* frame = const_cast<nsFrame*>(this); |
|
2634 |
|
2635 while (frame) { |
|
2636 const nsStyleUIReset* userinterface = frame->StyleUIReset(); |
|
2637 switch (userinterface->mUserSelect) { |
|
2638 case NS_STYLE_USER_SELECT_ALL: |
|
2639 case NS_STYLE_USER_SELECT_MOZ_ALL: |
|
2640 // override the previous values |
|
2641 selectStyle = userinterface->mUserSelect; |
|
2642 break; |
|
2643 default: |
|
2644 // otherwise return the first value which is not 'auto' |
|
2645 if (selectStyle == NS_STYLE_USER_SELECT_AUTO) { |
|
2646 selectStyle = userinterface->mUserSelect; |
|
2647 } |
|
2648 break; |
|
2649 } |
|
2650 frame = frame->GetParent(); |
|
2651 } |
|
2652 |
|
2653 // convert internal values to standard values |
|
2654 if (selectStyle == NS_STYLE_USER_SELECT_AUTO) |
|
2655 selectStyle = NS_STYLE_USER_SELECT_TEXT; |
|
2656 else |
|
2657 if (selectStyle == NS_STYLE_USER_SELECT_MOZ_ALL) |
|
2658 selectStyle = NS_STYLE_USER_SELECT_ALL; |
|
2659 |
|
2660 // return stuff |
|
2661 if (aSelectStyle) |
|
2662 *aSelectStyle = selectStyle; |
|
2663 if (mState & NS_FRAME_GENERATED_CONTENT) |
|
2664 *aSelectable = false; |
|
2665 else |
|
2666 *aSelectable = (selectStyle != NS_STYLE_USER_SELECT_NONE); |
|
2667 return NS_OK; |
|
2668 } |
|
2669 |
|
2670 /** |
|
2671 * Handles the Mouse Press Event for the frame |
|
2672 */ |
|
2673 NS_IMETHODIMP |
|
2674 nsFrame::HandlePress(nsPresContext* aPresContext, |
|
2675 WidgetGUIEvent* aEvent, |
|
2676 nsEventStatus* aEventStatus) |
|
2677 { |
|
2678 NS_ENSURE_ARG_POINTER(aEventStatus); |
|
2679 if (nsEventStatus_eConsumeNoDefault == *aEventStatus) { |
|
2680 return NS_OK; |
|
2681 } |
|
2682 |
|
2683 NS_ENSURE_ARG_POINTER(aEvent); |
|
2684 if (aEvent->eventStructType == NS_TOUCH_EVENT) { |
|
2685 return NS_OK; |
|
2686 } |
|
2687 |
|
2688 //We often get out of sync state issues with mousedown events that |
|
2689 //get interrupted by alerts/dialogs. |
|
2690 //Check with the ESM to see if we should process this one |
|
2691 if (!aPresContext->EventStateManager()->EventStatusOK(aEvent)) |
|
2692 return NS_OK; |
|
2693 |
|
2694 nsresult rv; |
|
2695 nsIPresShell *shell = aPresContext->GetPresShell(); |
|
2696 if (!shell) |
|
2697 return NS_ERROR_FAILURE; |
|
2698 |
|
2699 // if we are in Navigator and the click is in a draggable node, we don't want |
|
2700 // to start selection because we don't want to interfere with a potential |
|
2701 // drag of said node and steal all its glory. |
|
2702 int16_t isEditor = shell->GetSelectionFlags(); |
|
2703 //weaaak. only the editor can display frame selection not just text and images |
|
2704 isEditor = isEditor == nsISelectionDisplay::DISPLAY_ALL; |
|
2705 |
|
2706 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); |
|
2707 |
|
2708 if (!mouseEvent->IsAlt()) { |
|
2709 for (nsIContent* content = mContent; content; |
|
2710 content = content->GetParent()) { |
|
2711 if (nsContentUtils::ContentIsDraggable(content) && |
|
2712 !content->IsEditable()) { |
|
2713 // coordinate stuff is the fix for bug #55921 |
|
2714 if ((mRect - GetPosition()).Contains( |
|
2715 nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, this))) { |
|
2716 return NS_OK; |
|
2717 } |
|
2718 } |
|
2719 } |
|
2720 } |
|
2721 |
|
2722 // check whether style allows selection |
|
2723 // if not, don't tell selection the mouse event even occurred. |
|
2724 bool selectable; |
|
2725 uint8_t selectStyle; |
|
2726 rv = IsSelectable(&selectable, &selectStyle); |
|
2727 if (NS_FAILED(rv)) return rv; |
|
2728 |
|
2729 // check for select: none |
|
2730 if (!selectable) |
|
2731 return NS_OK; |
|
2732 |
|
2733 // When implementing NS_STYLE_USER_SELECT_ELEMENT, NS_STYLE_USER_SELECT_ELEMENTS and |
|
2734 // NS_STYLE_USER_SELECT_TOGGLE, need to change this logic |
|
2735 bool useFrameSelection = (selectStyle == NS_STYLE_USER_SELECT_TEXT); |
|
2736 |
|
2737 // If the mouse is dragged outside the nearest enclosing scrollable area |
|
2738 // while making a selection, the area will be scrolled. To do this, capture |
|
2739 // the mouse on the nearest scrollable frame. If there isn't a scrollable |
|
2740 // frame, or something else is already capturing the mouse, there's no |
|
2741 // reason to capture. |
|
2742 bool hasCapturedContent = false; |
|
2743 if (!nsIPresShell::GetCapturingContent()) { |
|
2744 nsIScrollableFrame* scrollFrame = |
|
2745 nsLayoutUtils::GetNearestScrollableFrame(this, |
|
2746 nsLayoutUtils::SCROLLABLE_SAME_DOC | |
|
2747 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN); |
|
2748 if (scrollFrame) { |
|
2749 nsIFrame* capturingFrame = do_QueryFrame(scrollFrame); |
|
2750 nsIPresShell::SetCapturingContent(capturingFrame->GetContent(), |
|
2751 CAPTURE_IGNOREALLOWED); |
|
2752 hasCapturedContent = true; |
|
2753 } |
|
2754 } |
|
2755 |
|
2756 // XXX This is screwy; it really should use the selection frame, not the |
|
2757 // event frame |
|
2758 const nsFrameSelection* frameselection = nullptr; |
|
2759 if (useFrameSelection) |
|
2760 frameselection = GetConstFrameSelection(); |
|
2761 else |
|
2762 frameselection = shell->ConstFrameSelection(); |
|
2763 |
|
2764 if (!frameselection || frameselection->GetDisplaySelection() == nsISelectionController::SELECTION_OFF) |
|
2765 return NS_OK;//nothing to do we cannot affect selection from here |
|
2766 |
|
2767 #ifdef XP_MACOSX |
|
2768 if (mouseEvent->IsControl()) |
|
2769 return NS_OK;//short circuit. hard coded for mac due to time restraints. |
|
2770 bool control = mouseEvent->IsMeta(); |
|
2771 #else |
|
2772 bool control = mouseEvent->IsControl(); |
|
2773 #endif |
|
2774 |
|
2775 nsRefPtr<nsFrameSelection> fc = const_cast<nsFrameSelection*>(frameselection); |
|
2776 if (mouseEvent->clickCount > 1) { |
|
2777 // These methods aren't const but can't actually delete anything, |
|
2778 // so no need for nsWeakFrame. |
|
2779 fc->SetMouseDownState(true); |
|
2780 fc->SetMouseDoubleDown(true); |
|
2781 return HandleMultiplePress(aPresContext, mouseEvent, aEventStatus, control); |
|
2782 } |
|
2783 |
|
2784 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, this); |
|
2785 ContentOffsets offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN); |
|
2786 |
|
2787 if (!offsets.content) |
|
2788 return NS_ERROR_FAILURE; |
|
2789 |
|
2790 // On touchables devices, touch the screen is usually a pan action, |
|
2791 // so let's reposition the caret if needed but do not select text |
|
2792 // if the touch did not happen over an editable element. Otherwise, |
|
2793 // let the user move the caret by tapping and dragging. |
|
2794 if (!offsets.content->IsEditable() && |
|
2795 Preferences::GetBool("browser.ignoreNativeFrameTextSelection", false)) { |
|
2796 // On touchables devices, mouse events are generated if the gesture is a tap. |
|
2797 // Such events are never going to generate a drag action, so let's release |
|
2798 // captured content if any. |
|
2799 if (hasCapturedContent) { |
|
2800 nsIPresShell::SetCapturingContent(nullptr, 0); |
|
2801 } |
|
2802 |
|
2803 return fc->HandleClick(offsets.content, offsets.StartOffset(), |
|
2804 offsets.EndOffset(), false, false, |
|
2805 offsets.associateWithNext); |
|
2806 } |
|
2807 |
|
2808 // Let Ctrl/Cmd+mouse down do table selection instead of drag initiation |
|
2809 nsCOMPtr<nsIContent>parentContent; |
|
2810 int32_t contentOffset; |
|
2811 int32_t target; |
|
2812 rv = GetDataForTableSelection(frameselection, shell, mouseEvent, |
|
2813 getter_AddRefs(parentContent), &contentOffset, |
|
2814 &target); |
|
2815 if (NS_SUCCEEDED(rv) && parentContent) |
|
2816 { |
|
2817 fc->SetMouseDownState(true); |
|
2818 return fc->HandleTableSelection(parentContent, contentOffset, target, |
|
2819 mouseEvent); |
|
2820 } |
|
2821 |
|
2822 fc->SetDelayedCaretData(0); |
|
2823 |
|
2824 // Check if any part of this frame is selected, and if the |
|
2825 // user clicked inside the selected region. If so, we delay |
|
2826 // starting a new selection since the user may be trying to |
|
2827 // drag the selected region to some other app. |
|
2828 |
|
2829 SelectionDetails *details = 0; |
|
2830 if (GetContent()->IsSelectionDescendant()) |
|
2831 { |
|
2832 bool inSelection = false; |
|
2833 details = frameselection->LookUpSelection(offsets.content, 0, |
|
2834 offsets.EndOffset(), false); |
|
2835 |
|
2836 // |
|
2837 // If there are any details, check to see if the user clicked |
|
2838 // within any selected region of the frame. |
|
2839 // |
|
2840 |
|
2841 SelectionDetails *curDetail = details; |
|
2842 |
|
2843 while (curDetail) |
|
2844 { |
|
2845 // |
|
2846 // If the user clicked inside a selection, then just |
|
2847 // return without doing anything. We will handle placing |
|
2848 // the caret later on when the mouse is released. We ignore |
|
2849 // the spellcheck, find and url formatting selections. |
|
2850 // |
|
2851 if (curDetail->mType != nsISelectionController::SELECTION_SPELLCHECK && |
|
2852 curDetail->mType != nsISelectionController::SELECTION_FIND && |
|
2853 curDetail->mType != nsISelectionController::SELECTION_URLSECONDARY && |
|
2854 curDetail->mStart <= offsets.StartOffset() && |
|
2855 offsets.EndOffset() <= curDetail->mEnd) |
|
2856 { |
|
2857 inSelection = true; |
|
2858 } |
|
2859 |
|
2860 SelectionDetails *nextDetail = curDetail->mNext; |
|
2861 delete curDetail; |
|
2862 curDetail = nextDetail; |
|
2863 } |
|
2864 |
|
2865 if (inSelection) { |
|
2866 fc->SetMouseDownState(false); |
|
2867 fc->SetDelayedCaretData(mouseEvent); |
|
2868 return NS_OK; |
|
2869 } |
|
2870 } |
|
2871 |
|
2872 fc->SetMouseDownState(true); |
|
2873 |
|
2874 // Do not touch any nsFrame members after this point without adding |
|
2875 // weakFrame checks. |
|
2876 rv = fc->HandleClick(offsets.content, offsets.StartOffset(), |
|
2877 offsets.EndOffset(), mouseEvent->IsShift(), control, |
|
2878 offsets.associateWithNext); |
|
2879 |
|
2880 if (NS_FAILED(rv)) |
|
2881 return rv; |
|
2882 |
|
2883 if (offsets.offset != offsets.secondaryOffset) |
|
2884 fc->MaintainSelection(); |
|
2885 |
|
2886 if (isEditor && !mouseEvent->IsShift() && |
|
2887 (offsets.EndOffset() - offsets.StartOffset()) == 1) |
|
2888 { |
|
2889 // A single node is selected and we aren't extending an existing |
|
2890 // selection, which means the user clicked directly on an object (either |
|
2891 // -moz-user-select: all or a non-text node without children). |
|
2892 // Therefore, disable selection extension during mouse moves. |
|
2893 // XXX This is a bit hacky; shouldn't editor be able to deal with this? |
|
2894 fc->SetMouseDownState(false); |
|
2895 } |
|
2896 |
|
2897 return rv; |
|
2898 } |
|
2899 |
|
2900 /* |
|
2901 * SelectByTypeAtPoint |
|
2902 * |
|
2903 * Search for selectable content at point and attempt to select |
|
2904 * based on the start and end selection behaviours. |
|
2905 * |
|
2906 * @param aPresContext Presentation context |
|
2907 * @param aPoint Point at which selection will occur. Coordinates |
|
2908 * should be relaitve to this frame. |
|
2909 * @param aBeginAmountType, aEndAmountType Selection behavior, see |
|
2910 * nsIFrame for definitions. |
|
2911 * @param aSelectFlags Selection flags defined in nsFame.h. |
|
2912 * @return success or failure at finding suitable content to select. |
|
2913 */ |
|
2914 nsresult |
|
2915 nsFrame::SelectByTypeAtPoint(nsPresContext* aPresContext, |
|
2916 const nsPoint& aPoint, |
|
2917 nsSelectionAmount aBeginAmountType, |
|
2918 nsSelectionAmount aEndAmountType, |
|
2919 uint32_t aSelectFlags) |
|
2920 { |
|
2921 NS_ENSURE_ARG_POINTER(aPresContext); |
|
2922 |
|
2923 // No point in selecting if selection is turned off |
|
2924 if (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF) |
|
2925 return NS_OK; |
|
2926 |
|
2927 ContentOffsets offsets = GetContentOffsetsFromPoint(aPoint, SKIP_HIDDEN); |
|
2928 if (!offsets.content) |
|
2929 return NS_ERROR_FAILURE; |
|
2930 |
|
2931 int32_t offset; |
|
2932 const nsFrameSelection* frameSelection = |
|
2933 PresContext()->GetPresShell()->ConstFrameSelection(); |
|
2934 nsIFrame* theFrame = frameSelection-> |
|
2935 GetFrameForNodeOffset(offsets.content, offsets.offset, |
|
2936 nsFrameSelection::HINT(offsets.associateWithNext), |
|
2937 &offset); |
|
2938 if (!theFrame) |
|
2939 return NS_ERROR_FAILURE; |
|
2940 |
|
2941 nsFrame* frame = static_cast<nsFrame*>(theFrame); |
|
2942 return frame->PeekBackwardAndForward(aBeginAmountType, aEndAmountType, |
|
2943 offset, aPresContext, |
|
2944 aBeginAmountType != eSelectWord, |
|
2945 aSelectFlags); |
|
2946 } |
|
2947 |
|
2948 /** |
|
2949 * Multiple Mouse Press -- line or paragraph selection -- for the frame. |
|
2950 * Wouldn't it be nice if this didn't have to be hardwired into Frame code? |
|
2951 */ |
|
2952 NS_IMETHODIMP |
|
2953 nsFrame::HandleMultiplePress(nsPresContext* aPresContext, |
|
2954 WidgetGUIEvent* aEvent, |
|
2955 nsEventStatus* aEventStatus, |
|
2956 bool aControlHeld) |
|
2957 { |
|
2958 NS_ENSURE_ARG_POINTER(aEvent); |
|
2959 NS_ENSURE_ARG_POINTER(aEventStatus); |
|
2960 |
|
2961 if (nsEventStatus_eConsumeNoDefault == *aEventStatus || |
|
2962 DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF) { |
|
2963 return NS_OK; |
|
2964 } |
|
2965 |
|
2966 // Find out whether we're doing line or paragraph selection. |
|
2967 // If browser.triple_click_selects_paragraph is true, triple-click selects paragraph. |
|
2968 // Otherwise, triple-click selects line, and quadruple-click selects paragraph |
|
2969 // (on platforms that support quadruple-click). |
|
2970 nsSelectionAmount beginAmount, endAmount; |
|
2971 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); |
|
2972 if (!mouseEvent) { |
|
2973 return NS_OK; |
|
2974 } |
|
2975 |
|
2976 if (mouseEvent->clickCount == 4) { |
|
2977 beginAmount = endAmount = eSelectParagraph; |
|
2978 } else if (mouseEvent->clickCount == 3) { |
|
2979 if (Preferences::GetBool("browser.triple_click_selects_paragraph")) { |
|
2980 beginAmount = endAmount = eSelectParagraph; |
|
2981 } else { |
|
2982 beginAmount = eSelectBeginLine; |
|
2983 endAmount = eSelectEndLine; |
|
2984 } |
|
2985 } else if (mouseEvent->clickCount == 2) { |
|
2986 // We only want inline frames; PeekBackwardAndForward dislikes blocks |
|
2987 beginAmount = endAmount = eSelectWord; |
|
2988 } else { |
|
2989 return NS_OK; |
|
2990 } |
|
2991 |
|
2992 nsPoint relPoint = |
|
2993 nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, this); |
|
2994 return SelectByTypeAtPoint(aPresContext, relPoint, beginAmount, endAmount, |
|
2995 (aControlHeld ? SELECT_ACCUMULATE : 0)); |
|
2996 } |
|
2997 |
|
2998 nsresult |
|
2999 nsFrame::PeekBackwardAndForward(nsSelectionAmount aAmountBack, |
|
3000 nsSelectionAmount aAmountForward, |
|
3001 int32_t aStartPos, |
|
3002 nsPresContext* aPresContext, |
|
3003 bool aJumpLines, |
|
3004 uint32_t aSelectFlags) |
|
3005 { |
|
3006 nsIFrame* baseFrame = this; |
|
3007 int32_t baseOffset = aStartPos; |
|
3008 nsresult rv; |
|
3009 |
|
3010 if (aAmountBack == eSelectWord) { |
|
3011 // To avoid selecting the previous word when at start of word, |
|
3012 // first move one character forward. |
|
3013 nsPeekOffsetStruct pos(eSelectCharacter, |
|
3014 eDirNext, |
|
3015 aStartPos, |
|
3016 0, |
|
3017 aJumpLines, |
|
3018 true, //limit on scrolled views |
|
3019 false, |
|
3020 false); |
|
3021 rv = PeekOffset(&pos); |
|
3022 if (NS_SUCCEEDED(rv)) { |
|
3023 baseFrame = pos.mResultFrame; |
|
3024 baseOffset = pos.mContentOffset; |
|
3025 } |
|
3026 } |
|
3027 |
|
3028 // Use peek offset one way then the other: |
|
3029 nsPeekOffsetStruct startpos(aAmountBack, |
|
3030 eDirPrevious, |
|
3031 baseOffset, |
|
3032 0, |
|
3033 aJumpLines, |
|
3034 true, //limit on scrolled views |
|
3035 false, |
|
3036 false); |
|
3037 rv = baseFrame->PeekOffset(&startpos); |
|
3038 if (NS_FAILED(rv)) |
|
3039 return rv; |
|
3040 |
|
3041 nsPeekOffsetStruct endpos(aAmountForward, |
|
3042 eDirNext, |
|
3043 aStartPos, |
|
3044 0, |
|
3045 aJumpLines, |
|
3046 true, //limit on scrolled views |
|
3047 false, |
|
3048 false); |
|
3049 rv = PeekOffset(&endpos); |
|
3050 if (NS_FAILED(rv)) |
|
3051 return rv; |
|
3052 |
|
3053 // Keep frameSelection alive. |
|
3054 nsRefPtr<nsFrameSelection> frameSelection = GetFrameSelection(); |
|
3055 |
|
3056 rv = frameSelection->HandleClick(startpos.mResultContent, |
|
3057 startpos.mContentOffset, startpos.mContentOffset, |
|
3058 false, (aSelectFlags & SELECT_ACCUMULATE), |
|
3059 nsFrameSelection::HINTRIGHT); |
|
3060 if (NS_FAILED(rv)) |
|
3061 return rv; |
|
3062 |
|
3063 rv = frameSelection->HandleClick(endpos.mResultContent, |
|
3064 endpos.mContentOffset, endpos.mContentOffset, |
|
3065 true, false, |
|
3066 nsFrameSelection::HINTLEFT); |
|
3067 if (NS_FAILED(rv)) |
|
3068 return rv; |
|
3069 |
|
3070 // maintain selection |
|
3071 return frameSelection->MaintainSelection(aAmountBack); |
|
3072 } |
|
3073 |
|
3074 NS_IMETHODIMP nsFrame::HandleDrag(nsPresContext* aPresContext, |
|
3075 WidgetGUIEvent* aEvent, |
|
3076 nsEventStatus* aEventStatus) |
|
3077 { |
|
3078 MOZ_ASSERT(aEvent->eventStructType == NS_MOUSE_EVENT, "HandleDrag can only handle mouse event"); |
|
3079 |
|
3080 bool selectable; |
|
3081 IsSelectable(&selectable, nullptr); |
|
3082 |
|
3083 // XXX Do we really need to exclude non-selectable content here? |
|
3084 // GetContentOffsetsFromPoint can handle it just fine, although some |
|
3085 // other stuff might not like it. |
|
3086 if (!selectable) |
|
3087 return NS_OK; |
|
3088 if (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF) { |
|
3089 return NS_OK; |
|
3090 } |
|
3091 nsIPresShell *presShell = aPresContext->PresShell(); |
|
3092 |
|
3093 nsRefPtr<nsFrameSelection> frameselection = GetFrameSelection(); |
|
3094 bool mouseDown = frameselection->GetMouseDownState(); |
|
3095 if (!mouseDown) |
|
3096 return NS_OK; |
|
3097 |
|
3098 frameselection->StopAutoScrollTimer(); |
|
3099 |
|
3100 // Check if we are dragging in a table cell |
|
3101 nsCOMPtr<nsIContent> parentContent; |
|
3102 int32_t contentOffset; |
|
3103 int32_t target; |
|
3104 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); |
|
3105 nsresult result; |
|
3106 result = GetDataForTableSelection(frameselection, presShell, mouseEvent, |
|
3107 getter_AddRefs(parentContent), |
|
3108 &contentOffset, &target); |
|
3109 |
|
3110 nsWeakFrame weakThis = this; |
|
3111 if (NS_SUCCEEDED(result) && parentContent) { |
|
3112 frameselection->HandleTableSelection(parentContent, contentOffset, target, |
|
3113 mouseEvent); |
|
3114 } else { |
|
3115 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, this); |
|
3116 frameselection->HandleDrag(this, pt); |
|
3117 } |
|
3118 |
|
3119 // The frameselection object notifies selection listeners synchronously above |
|
3120 // which might have killed us. |
|
3121 if (!weakThis.IsAlive()) { |
|
3122 return NS_OK; |
|
3123 } |
|
3124 |
|
3125 // get the nearest scrollframe |
|
3126 nsIScrollableFrame* scrollFrame = |
|
3127 nsLayoutUtils::GetNearestScrollableFrame(this, |
|
3128 nsLayoutUtils::SCROLLABLE_SAME_DOC | |
|
3129 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN); |
|
3130 |
|
3131 if (scrollFrame) { |
|
3132 nsIFrame* capturingFrame = scrollFrame->GetScrolledFrame(); |
|
3133 if (capturingFrame) { |
|
3134 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, |
|
3135 capturingFrame); |
|
3136 frameselection->StartAutoScrollTimer(capturingFrame, pt, 30); |
|
3137 } |
|
3138 } |
|
3139 |
|
3140 return NS_OK; |
|
3141 } |
|
3142 |
|
3143 /** |
|
3144 * This static method handles part of the nsFrame::HandleRelease in a way |
|
3145 * which doesn't rely on the nsFrame object to stay alive. |
|
3146 */ |
|
3147 static nsresult |
|
3148 HandleFrameSelection(nsFrameSelection* aFrameSelection, |
|
3149 nsIFrame::ContentOffsets& aOffsets, |
|
3150 bool aHandleTableSel, |
|
3151 int32_t aContentOffsetForTableSel, |
|
3152 int32_t aTargetForTableSel, |
|
3153 nsIContent* aParentContentForTableSel, |
|
3154 WidgetGUIEvent* aEvent, |
|
3155 nsEventStatus* aEventStatus) |
|
3156 { |
|
3157 if (!aFrameSelection) { |
|
3158 return NS_OK; |
|
3159 } |
|
3160 |
|
3161 nsresult rv = NS_OK; |
|
3162 |
|
3163 if (nsEventStatus_eConsumeNoDefault != *aEventStatus) { |
|
3164 if (!aHandleTableSel) { |
|
3165 if (!aOffsets.content || !aFrameSelection->HasDelayedCaretData()) { |
|
3166 return NS_ERROR_FAILURE; |
|
3167 } |
|
3168 |
|
3169 // We are doing this to simulate what we would have done on HandlePress. |
|
3170 // We didn't do it there to give the user an opportunity to drag |
|
3171 // the text, but since they didn't drag, we want to place the |
|
3172 // caret. |
|
3173 // However, we'll use the mouse position from the release, since: |
|
3174 // * it's easier |
|
3175 // * that's the normal click position to use (although really, in |
|
3176 // the normal case, small movements that don't count as a drag |
|
3177 // can do selection) |
|
3178 aFrameSelection->SetMouseDownState(true); |
|
3179 |
|
3180 rv = aFrameSelection->HandleClick(aOffsets.content, |
|
3181 aOffsets.StartOffset(), |
|
3182 aOffsets.EndOffset(), |
|
3183 aFrameSelection->IsShiftDownInDelayedCaretData(), |
|
3184 false, |
|
3185 aOffsets.associateWithNext); |
|
3186 if (NS_FAILED(rv)) { |
|
3187 return rv; |
|
3188 } |
|
3189 } else if (aParentContentForTableSel) { |
|
3190 aFrameSelection->SetMouseDownState(false); |
|
3191 rv = aFrameSelection->HandleTableSelection( |
|
3192 aParentContentForTableSel, |
|
3193 aContentOffsetForTableSel, |
|
3194 aTargetForTableSel, |
|
3195 aEvent->AsMouseEvent()); |
|
3196 if (NS_FAILED(rv)) { |
|
3197 return rv; |
|
3198 } |
|
3199 } |
|
3200 aFrameSelection->SetDelayedCaretData(0); |
|
3201 } |
|
3202 |
|
3203 aFrameSelection->SetMouseDownState(false); |
|
3204 aFrameSelection->StopAutoScrollTimer(); |
|
3205 |
|
3206 return NS_OK; |
|
3207 } |
|
3208 |
|
3209 NS_IMETHODIMP nsFrame::HandleRelease(nsPresContext* aPresContext, |
|
3210 WidgetGUIEvent* aEvent, |
|
3211 nsEventStatus* aEventStatus) |
|
3212 { |
|
3213 if (aEvent->eventStructType != NS_MOUSE_EVENT) { |
|
3214 return NS_OK; |
|
3215 } |
|
3216 |
|
3217 nsIFrame* activeFrame = GetActiveSelectionFrame(aPresContext, this); |
|
3218 |
|
3219 nsCOMPtr<nsIContent> captureContent = nsIPresShell::GetCapturingContent(); |
|
3220 |
|
3221 // We can unconditionally stop capturing because |
|
3222 // we should never be capturing when the mouse button is up |
|
3223 nsIPresShell::SetCapturingContent(nullptr, 0); |
|
3224 |
|
3225 bool selectionOff = |
|
3226 (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF); |
|
3227 |
|
3228 nsRefPtr<nsFrameSelection> frameselection; |
|
3229 ContentOffsets offsets; |
|
3230 nsCOMPtr<nsIContent> parentContent; |
|
3231 int32_t contentOffsetForTableSel = 0; |
|
3232 int32_t targetForTableSel = 0; |
|
3233 bool handleTableSelection = true; |
|
3234 |
|
3235 if (!selectionOff) { |
|
3236 frameselection = GetFrameSelection(); |
|
3237 if (nsEventStatus_eConsumeNoDefault != *aEventStatus && frameselection) { |
|
3238 // Check if the frameselection recorded the mouse going down. |
|
3239 // If not, the user must have clicked in a part of the selection. |
|
3240 // Place the caret before continuing! |
|
3241 |
|
3242 bool mouseDown = frameselection->GetMouseDownState(); |
|
3243 |
|
3244 if (!mouseDown && frameselection->HasDelayedCaretData() && |
|
3245 frameselection->GetClickCountInDelayedCaretData() < 2) { |
|
3246 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this); |
|
3247 offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN); |
|
3248 handleTableSelection = false; |
|
3249 } else { |
|
3250 GetDataForTableSelection(frameselection, PresContext()->PresShell(), |
|
3251 aEvent->AsMouseEvent(), |
|
3252 getter_AddRefs(parentContent), |
|
3253 &contentOffsetForTableSel, |
|
3254 &targetForTableSel); |
|
3255 } |
|
3256 } |
|
3257 } |
|
3258 |
|
3259 // We might be capturing in some other document and the event just happened to |
|
3260 // trickle down here. Make sure that document's frame selection is notified. |
|
3261 // Note, this may cause the current nsFrame object to be deleted, bug 336592. |
|
3262 nsRefPtr<nsFrameSelection> frameSelection; |
|
3263 if (activeFrame != this && |
|
3264 static_cast<nsFrame*>(activeFrame)->DisplaySelection(activeFrame->PresContext()) |
|
3265 != nsISelectionController::SELECTION_OFF) { |
|
3266 frameSelection = activeFrame->GetFrameSelection(); |
|
3267 } |
|
3268 |
|
3269 // Also check the selection of the capturing content which might be in a |
|
3270 // different document. |
|
3271 if (!frameSelection && captureContent) { |
|
3272 nsIDocument* doc = captureContent->GetCurrentDoc(); |
|
3273 if (doc) { |
|
3274 nsIPresShell* capturingShell = doc->GetShell(); |
|
3275 if (capturingShell && capturingShell != PresContext()->GetPresShell()) { |
|
3276 frameSelection = capturingShell->FrameSelection(); |
|
3277 } |
|
3278 } |
|
3279 } |
|
3280 |
|
3281 if (frameSelection) { |
|
3282 frameSelection->SetMouseDownState(false); |
|
3283 frameSelection->StopAutoScrollTimer(); |
|
3284 } |
|
3285 |
|
3286 // Do not call any methods of the current object after this point!!! |
|
3287 // The object is perhaps dead! |
|
3288 |
|
3289 return selectionOff |
|
3290 ? NS_OK |
|
3291 : HandleFrameSelection(frameselection, offsets, handleTableSelection, |
|
3292 contentOffsetForTableSel, targetForTableSel, |
|
3293 parentContent, aEvent, aEventStatus); |
|
3294 } |
|
3295 |
|
3296 struct MOZ_STACK_CLASS FrameContentRange { |
|
3297 FrameContentRange(nsIContent* aContent, int32_t aStart, int32_t aEnd) : |
|
3298 content(aContent), start(aStart), end(aEnd) { } |
|
3299 nsCOMPtr<nsIContent> content; |
|
3300 int32_t start; |
|
3301 int32_t end; |
|
3302 }; |
|
3303 |
|
3304 // Retrieve the content offsets of a frame |
|
3305 static FrameContentRange GetRangeForFrame(nsIFrame* aFrame) { |
|
3306 nsCOMPtr<nsIContent> content, parent; |
|
3307 content = aFrame->GetContent(); |
|
3308 if (!content) { |
|
3309 NS_WARNING("Frame has no content"); |
|
3310 return FrameContentRange(nullptr, -1, -1); |
|
3311 } |
|
3312 nsIAtom* type = aFrame->GetType(); |
|
3313 if (type == nsGkAtoms::textFrame) { |
|
3314 int32_t offset, offsetEnd; |
|
3315 aFrame->GetOffsets(offset, offsetEnd); |
|
3316 return FrameContentRange(content, offset, offsetEnd); |
|
3317 } |
|
3318 if (type == nsGkAtoms::brFrame) { |
|
3319 parent = content->GetParent(); |
|
3320 int32_t beginOffset = parent->IndexOf(content); |
|
3321 return FrameContentRange(parent, beginOffset, beginOffset); |
|
3322 } |
|
3323 // Loop to deal with anonymous content, which has no index; this loop |
|
3324 // probably won't run more than twice under normal conditions |
|
3325 do { |
|
3326 parent = content->GetParent(); |
|
3327 if (parent) { |
|
3328 int32_t beginOffset = parent->IndexOf(content); |
|
3329 if (beginOffset >= 0) |
|
3330 return FrameContentRange(parent, beginOffset, beginOffset + 1); |
|
3331 content = parent; |
|
3332 } |
|
3333 } while (parent); |
|
3334 |
|
3335 // The root content node must act differently |
|
3336 return FrameContentRange(content, 0, content->GetChildCount()); |
|
3337 } |
|
3338 |
|
3339 // The FrameTarget represents the closest frame to a point that can be selected |
|
3340 // The frame is the frame represented, frameEdge says whether one end of the |
|
3341 // frame is the result (in which case different handling is needed), and |
|
3342 // afterFrame says which end is repersented if frameEdge is true |
|
3343 struct FrameTarget { |
|
3344 FrameTarget(nsIFrame* aFrame, bool aFrameEdge, bool aAfterFrame, |
|
3345 bool aEmptyBlock = false) : |
|
3346 frame(aFrame), frameEdge(aFrameEdge), afterFrame(aAfterFrame), |
|
3347 emptyBlock(aEmptyBlock) { } |
|
3348 static FrameTarget Null() { |
|
3349 return FrameTarget(nullptr, false, false); |
|
3350 } |
|
3351 bool IsNull() { |
|
3352 return !frame; |
|
3353 } |
|
3354 nsIFrame* frame; |
|
3355 bool frameEdge; |
|
3356 bool afterFrame; |
|
3357 bool emptyBlock; |
|
3358 }; |
|
3359 |
|
3360 // See function implementation for information |
|
3361 static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame, nsPoint aPoint, |
|
3362 uint32_t aFlags); |
|
3363 |
|
3364 static bool SelfIsSelectable(nsIFrame* aFrame, uint32_t aFlags) |
|
3365 { |
|
3366 if ((aFlags & nsIFrame::SKIP_HIDDEN) && |
|
3367 !aFrame->StyleVisibility()->IsVisible()) { |
|
3368 return false; |
|
3369 } |
|
3370 return !aFrame->IsGeneratedContentFrame() && |
|
3371 aFrame->StyleUIReset()->mUserSelect != NS_STYLE_USER_SELECT_NONE; |
|
3372 } |
|
3373 |
|
3374 static bool SelectionDescendToKids(nsIFrame* aFrame) { |
|
3375 uint8_t style = aFrame->StyleUIReset()->mUserSelect; |
|
3376 nsIFrame* parent = aFrame->GetParent(); |
|
3377 // If we are only near (not directly over) then don't traverse |
|
3378 // frames with independent selection (e.g. text and list controls) |
|
3379 // unless we're already inside such a frame (see bug 268497). Note that this |
|
3380 // prevents any of the users of this method from entering form controls. |
|
3381 // XXX We might want some way to allow using the up-arrow to go into a form |
|
3382 // control, but the focus didn't work right anyway; it'd probably be enough |
|
3383 // if the left and right arrows could enter textboxes (which I don't believe |
|
3384 // they can at the moment) |
|
3385 return !aFrame->IsGeneratedContentFrame() && |
|
3386 style != NS_STYLE_USER_SELECT_ALL && |
|
3387 style != NS_STYLE_USER_SELECT_NONE && |
|
3388 ((parent->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION) || |
|
3389 !(aFrame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION)); |
|
3390 } |
|
3391 |
|
3392 static FrameTarget GetSelectionClosestFrameForChild(nsIFrame* aChild, |
|
3393 nsPoint aPoint, |
|
3394 uint32_t aFlags) |
|
3395 { |
|
3396 nsIFrame* parent = aChild->GetParent(); |
|
3397 if (SelectionDescendToKids(aChild)) { |
|
3398 nsPoint pt = aPoint - aChild->GetOffsetTo(parent); |
|
3399 return GetSelectionClosestFrame(aChild, pt, aFlags); |
|
3400 } |
|
3401 return FrameTarget(aChild, false, false); |
|
3402 } |
|
3403 |
|
3404 // When the cursor needs to be at the beginning of a block, it shouldn't be |
|
3405 // before the first child. A click on a block whose first child is a block |
|
3406 // should put the cursor in the child. The cursor shouldn't be between the |
|
3407 // blocks, because that's not where it's expected. |
|
3408 // Note that this method is guaranteed to succeed. |
|
3409 static FrameTarget DrillDownToSelectionFrame(nsIFrame* aFrame, |
|
3410 bool aEndFrame, uint32_t aFlags) { |
|
3411 if (SelectionDescendToKids(aFrame)) { |
|
3412 nsIFrame* result = nullptr; |
|
3413 nsIFrame *frame = aFrame->GetFirstPrincipalChild(); |
|
3414 if (!aEndFrame) { |
|
3415 while (frame && (!SelfIsSelectable(frame, aFlags) || |
|
3416 frame->IsEmpty())) |
|
3417 frame = frame->GetNextSibling(); |
|
3418 if (frame) |
|
3419 result = frame; |
|
3420 } else { |
|
3421 // Because the frame tree is singly linked, to find the last frame, |
|
3422 // we have to iterate through all the frames |
|
3423 // XXX I have a feeling this could be slow for long blocks, although |
|
3424 // I can't find any slowdowns |
|
3425 while (frame) { |
|
3426 if (!frame->IsEmpty() && SelfIsSelectable(frame, aFlags)) |
|
3427 result = frame; |
|
3428 frame = frame->GetNextSibling(); |
|
3429 } |
|
3430 } |
|
3431 if (result) |
|
3432 return DrillDownToSelectionFrame(result, aEndFrame, aFlags); |
|
3433 } |
|
3434 // If the current frame has no targetable children, target the current frame |
|
3435 return FrameTarget(aFrame, true, aEndFrame); |
|
3436 } |
|
3437 |
|
3438 // This method finds the closest valid FrameTarget on a given line; if there is |
|
3439 // no valid FrameTarget on the line, it returns a null FrameTarget |
|
3440 static FrameTarget GetSelectionClosestFrameForLine( |
|
3441 nsBlockFrame* aParent, |
|
3442 nsBlockFrame::line_iterator aLine, |
|
3443 nsPoint aPoint, |
|
3444 uint32_t aFlags) |
|
3445 { |
|
3446 nsIFrame *frame = aLine->mFirstChild; |
|
3447 // Account for end of lines (any iterator from the block is valid) |
|
3448 if (aLine == aParent->end_lines()) |
|
3449 return DrillDownToSelectionFrame(aParent, true, aFlags); |
|
3450 nsIFrame *closestFromIStart = nullptr, *closestFromIEnd = nullptr; |
|
3451 nscoord closestIStart = aLine->IStart(), closestIEnd = aLine->IEnd(); |
|
3452 WritingMode wm = aLine->mWritingMode; |
|
3453 LogicalPoint pt(wm, aPoint, aLine->mContainerWidth); |
|
3454 for (int32_t n = aLine->GetChildCount(); n; |
|
3455 --n, frame = frame->GetNextSibling()) { |
|
3456 if (!SelfIsSelectable(frame, aFlags) || frame->IsEmpty()) |
|
3457 continue; |
|
3458 LogicalRect frameRect = LogicalRect(wm, frame->GetRect(), |
|
3459 aLine->mContainerWidth); |
|
3460 if (pt.I(wm) >= frameRect.IStart(wm)) { |
|
3461 if (pt.I(wm) < frameRect.IEnd(wm)) { |
|
3462 return GetSelectionClosestFrameForChild(frame, aPoint, aFlags); |
|
3463 } |
|
3464 if (frameRect.IEnd(wm) >= closestIStart) { |
|
3465 closestFromIStart = frame; |
|
3466 closestIStart = frameRect.IEnd(wm); |
|
3467 } |
|
3468 } else { |
|
3469 if (frameRect.IStart(wm) <= closestIEnd) { |
|
3470 closestFromIEnd = frame; |
|
3471 closestIEnd = frameRect.IStart(wm); |
|
3472 } |
|
3473 } |
|
3474 } |
|
3475 if (!closestFromIStart && !closestFromIEnd) { |
|
3476 // We should only get here if there are no selectable frames on a line |
|
3477 // XXX Do we need more elaborate handling here? |
|
3478 return FrameTarget::Null(); |
|
3479 } |
|
3480 if (closestFromIStart && |
|
3481 (!closestFromIEnd || |
|
3482 (abs(pt.I(wm) - closestIStart) <= abs(pt.I(wm) - closestIEnd)))) { |
|
3483 return GetSelectionClosestFrameForChild(closestFromIStart, aPoint, |
|
3484 aFlags); |
|
3485 } |
|
3486 return GetSelectionClosestFrameForChild(closestFromIEnd, aPoint, aFlags); |
|
3487 } |
|
3488 |
|
3489 // This method is for the special handling we do for block frames; they're |
|
3490 // special because they represent paragraphs and because they are organized |
|
3491 // into lines, which have bounds that are not stored elsewhere in the |
|
3492 // frame tree. Returns a null FrameTarget for frames which are not |
|
3493 // blocks or blocks with no lines except editable one. |
|
3494 static FrameTarget GetSelectionClosestFrameForBlock(nsIFrame* aFrame, |
|
3495 nsPoint aPoint, |
|
3496 uint32_t aFlags) |
|
3497 { |
|
3498 nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(aFrame); // used only for QI |
|
3499 if (!bf) |
|
3500 return FrameTarget::Null(); |
|
3501 |
|
3502 // This code searches for the correct line |
|
3503 nsBlockFrame::line_iterator firstLine = bf->begin_lines(); |
|
3504 nsBlockFrame::line_iterator end = bf->end_lines(); |
|
3505 if (firstLine == end) { |
|
3506 nsIContent *blockContent = aFrame->GetContent(); |
|
3507 if (blockContent) { |
|
3508 // Return with empty flag true. |
|
3509 return FrameTarget(aFrame, false, false, true); |
|
3510 } |
|
3511 return FrameTarget::Null(); |
|
3512 } |
|
3513 nsBlockFrame::line_iterator curLine = firstLine; |
|
3514 nsBlockFrame::line_iterator closestLine = end; |
|
3515 // Convert aPoint into a LogicalPoint in the writing-mode of this block |
|
3516 WritingMode wm = curLine->mWritingMode; |
|
3517 LogicalPoint pt(wm, aPoint, curLine->mContainerWidth); |
|
3518 while (curLine != end) { |
|
3519 // Check to see if our point lies within the line's block-direction bounds |
|
3520 nscoord BCoord = pt.B(wm) - curLine->BStart(); |
|
3521 nscoord BSize = curLine->BSize(); |
|
3522 if (BCoord >= 0 && BCoord < BSize) { |
|
3523 closestLine = curLine; |
|
3524 break; // We found the line; stop looking |
|
3525 } |
|
3526 if (BCoord < 0) |
|
3527 break; |
|
3528 ++curLine; |
|
3529 } |
|
3530 |
|
3531 if (closestLine == end) { |
|
3532 nsBlockFrame::line_iterator prevLine = curLine.prev(); |
|
3533 nsBlockFrame::line_iterator nextLine = curLine; |
|
3534 // Avoid empty lines |
|
3535 while (nextLine != end && nextLine->IsEmpty()) |
|
3536 ++nextLine; |
|
3537 while (prevLine != end && prevLine->IsEmpty()) |
|
3538 --prevLine; |
|
3539 |
|
3540 // This hidden pref dictates whether a point above or below all lines comes |
|
3541 // up with a line or the beginning or end of the frame; 0 on Windows, |
|
3542 // 1 on other platforms by default at the writing of this code |
|
3543 int32_t dragOutOfFrame = |
|
3544 Preferences::GetInt("browser.drag_out_of_frame_style"); |
|
3545 |
|
3546 if (prevLine == end) { |
|
3547 if (dragOutOfFrame == 1 || nextLine == end) |
|
3548 return DrillDownToSelectionFrame(aFrame, false, aFlags); |
|
3549 closestLine = nextLine; |
|
3550 } else if (nextLine == end) { |
|
3551 if (dragOutOfFrame == 1) |
|
3552 return DrillDownToSelectionFrame(aFrame, true, aFlags); |
|
3553 closestLine = prevLine; |
|
3554 } else { // Figure out which line is closer |
|
3555 if (pt.B(wm) - prevLine->BEnd() < nextLine->BStart() - pt.B(wm)) |
|
3556 closestLine = prevLine; |
|
3557 else |
|
3558 closestLine = nextLine; |
|
3559 } |
|
3560 } |
|
3561 |
|
3562 do { |
|
3563 FrameTarget target = GetSelectionClosestFrameForLine(bf, closestLine, |
|
3564 aPoint, aFlags); |
|
3565 if (!target.IsNull()) |
|
3566 return target; |
|
3567 ++closestLine; |
|
3568 } while (closestLine != end); |
|
3569 // Fall back to just targeting the last targetable place |
|
3570 return DrillDownToSelectionFrame(aFrame, true, aFlags); |
|
3571 } |
|
3572 |
|
3573 // GetSelectionClosestFrame is the helper function that calculates the closest |
|
3574 // frame to the given point. |
|
3575 // It doesn't completely account for offset styles, so needs to be used in |
|
3576 // restricted environments. |
|
3577 // Cannot handle overlapping frames correctly, so it should receive the output |
|
3578 // of GetFrameForPoint |
|
3579 // Guaranteed to return a valid FrameTarget |
|
3580 static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame, nsPoint aPoint, |
|
3581 uint32_t aFlags) |
|
3582 { |
|
3583 { |
|
3584 // Handle blocks; if the frame isn't a block, the method fails |
|
3585 FrameTarget target = GetSelectionClosestFrameForBlock(aFrame, aPoint, aFlags); |
|
3586 if (!target.IsNull()) |
|
3587 return target; |
|
3588 } |
|
3589 |
|
3590 nsIFrame *kid = aFrame->GetFirstPrincipalChild(); |
|
3591 |
|
3592 if (kid) { |
|
3593 // Go through all the child frames to find the closest one |
|
3594 nsIFrame::FrameWithDistance closest = { nullptr, nscoord_MAX, nscoord_MAX }; |
|
3595 for (; kid; kid = kid->GetNextSibling()) { |
|
3596 if (!SelfIsSelectable(kid, aFlags) || kid->IsEmpty()) |
|
3597 continue; |
|
3598 |
|
3599 kid->FindCloserFrameForSelection(aPoint, &closest); |
|
3600 } |
|
3601 if (closest.mFrame) { |
|
3602 if (closest.mFrame->IsSVGText()) |
|
3603 return FrameTarget(closest.mFrame, false, false); |
|
3604 return GetSelectionClosestFrameForChild(closest.mFrame, aPoint, aFlags); |
|
3605 } |
|
3606 } |
|
3607 return FrameTarget(aFrame, false, false); |
|
3608 } |
|
3609 |
|
3610 nsIFrame::ContentOffsets OffsetsForSingleFrame(nsIFrame* aFrame, nsPoint aPoint) |
|
3611 { |
|
3612 nsIFrame::ContentOffsets offsets; |
|
3613 FrameContentRange range = GetRangeForFrame(aFrame); |
|
3614 offsets.content = range.content; |
|
3615 // If there are continuations (meaning it's not one rectangle), this is the |
|
3616 // best this function can do |
|
3617 if (aFrame->GetNextContinuation() || aFrame->GetPrevContinuation()) { |
|
3618 offsets.offset = range.start; |
|
3619 offsets.secondaryOffset = range.end; |
|
3620 offsets.associateWithNext = true; |
|
3621 return offsets; |
|
3622 } |
|
3623 |
|
3624 // Figure out whether the offsets should be over, after, or before the frame |
|
3625 nsRect rect(nsPoint(0, 0), aFrame->GetSize()); |
|
3626 |
|
3627 bool isBlock = aFrame->GetDisplay() != NS_STYLE_DISPLAY_INLINE; |
|
3628 bool isRtl = (aFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL); |
|
3629 if ((isBlock && rect.y < aPoint.y) || |
|
3630 (!isBlock && ((isRtl && rect.x + rect.width / 2 > aPoint.x) || |
|
3631 (!isRtl && rect.x + rect.width / 2 < aPoint.x)))) { |
|
3632 offsets.offset = range.end; |
|
3633 if (rect.Contains(aPoint)) |
|
3634 offsets.secondaryOffset = range.start; |
|
3635 else |
|
3636 offsets.secondaryOffset = range.end; |
|
3637 } else { |
|
3638 offsets.offset = range.start; |
|
3639 if (rect.Contains(aPoint)) |
|
3640 offsets.secondaryOffset = range.end; |
|
3641 else |
|
3642 offsets.secondaryOffset = range.start; |
|
3643 } |
|
3644 offsets.associateWithNext = (offsets.offset == range.start); |
|
3645 return offsets; |
|
3646 } |
|
3647 |
|
3648 static nsIFrame* AdjustFrameForSelectionStyles(nsIFrame* aFrame) { |
|
3649 nsIFrame* adjustedFrame = aFrame; |
|
3650 for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) |
|
3651 { |
|
3652 // These are the conditions that make all children not able to handle |
|
3653 // a cursor. |
|
3654 if (frame->StyleUIReset()->mUserSelect == NS_STYLE_USER_SELECT_ALL || |
|
3655 frame->IsGeneratedContentFrame()) { |
|
3656 adjustedFrame = frame; |
|
3657 } |
|
3658 } |
|
3659 return adjustedFrame; |
|
3660 } |
|
3661 |
|
3662 nsIFrame::ContentOffsets nsIFrame::GetContentOffsetsFromPoint(nsPoint aPoint, |
|
3663 uint32_t aFlags) |
|
3664 { |
|
3665 nsIFrame *adjustedFrame; |
|
3666 if (aFlags & IGNORE_SELECTION_STYLE) { |
|
3667 adjustedFrame = this; |
|
3668 } |
|
3669 else { |
|
3670 // This section of code deals with special selection styles. Note that |
|
3671 // -moz-all exists, even though it doesn't need to be explicitly handled. |
|
3672 // |
|
3673 // The offset is forced not to end up in generated content; content offsets |
|
3674 // cannot represent content outside of the document's content tree. |
|
3675 |
|
3676 adjustedFrame = AdjustFrameForSelectionStyles(this); |
|
3677 |
|
3678 // -moz-user-select: all needs special handling, because clicking on it |
|
3679 // should lead to the whole frame being selected |
|
3680 if (adjustedFrame && adjustedFrame->StyleUIReset()->mUserSelect == |
|
3681 NS_STYLE_USER_SELECT_ALL) { |
|
3682 nsPoint adjustedPoint = aPoint + this->GetOffsetTo(adjustedFrame); |
|
3683 return OffsetsForSingleFrame(adjustedFrame, adjustedPoint); |
|
3684 } |
|
3685 |
|
3686 // For other cases, try to find a closest frame starting from the parent of |
|
3687 // the unselectable frame |
|
3688 if (adjustedFrame != this) |
|
3689 adjustedFrame = adjustedFrame->GetParent(); |
|
3690 } |
|
3691 |
|
3692 nsPoint adjustedPoint = aPoint + this->GetOffsetTo(adjustedFrame); |
|
3693 |
|
3694 FrameTarget closest = |
|
3695 GetSelectionClosestFrame(adjustedFrame, adjustedPoint, aFlags); |
|
3696 |
|
3697 if (closest.emptyBlock) { |
|
3698 ContentOffsets offsets; |
|
3699 NS_ASSERTION(closest.frame, |
|
3700 "closest.frame must not be null when it's empty"); |
|
3701 offsets.content = closest.frame->GetContent(); |
|
3702 offsets.offset = 0; |
|
3703 offsets.secondaryOffset = 0; |
|
3704 offsets.associateWithNext = true; |
|
3705 return offsets; |
|
3706 } |
|
3707 |
|
3708 // If the correct offset is at one end of a frame, use offset-based |
|
3709 // calculation method |
|
3710 if (closest.frameEdge) { |
|
3711 ContentOffsets offsets; |
|
3712 FrameContentRange range = GetRangeForFrame(closest.frame); |
|
3713 offsets.content = range.content; |
|
3714 if (closest.afterFrame) |
|
3715 offsets.offset = range.end; |
|
3716 else |
|
3717 offsets.offset = range.start; |
|
3718 offsets.secondaryOffset = offsets.offset; |
|
3719 offsets.associateWithNext = (offsets.offset == range.start); |
|
3720 return offsets; |
|
3721 } |
|
3722 |
|
3723 nsPoint pt; |
|
3724 if (closest.frame != this) { |
|
3725 if (closest.frame->IsSVGText()) { |
|
3726 pt = nsLayoutUtils::TransformAncestorPointToFrame(closest.frame, |
|
3727 aPoint, this); |
|
3728 } else { |
|
3729 pt = aPoint - closest.frame->GetOffsetTo(this); |
|
3730 } |
|
3731 } else { |
|
3732 pt = aPoint; |
|
3733 } |
|
3734 return static_cast<nsFrame*>(closest.frame)->CalcContentOffsetsFromFramePoint(pt); |
|
3735 |
|
3736 // XXX should I add some kind of offset standardization? |
|
3737 // consider <b>xxxxx</b><i>zzzzz</i>; should any click between the last |
|
3738 // x and first z put the cursor in the same logical position in addition |
|
3739 // to the same visual position? |
|
3740 } |
|
3741 |
|
3742 nsIFrame::ContentOffsets nsFrame::CalcContentOffsetsFromFramePoint(nsPoint aPoint) |
|
3743 { |
|
3744 return OffsetsForSingleFrame(this, aPoint); |
|
3745 } |
|
3746 |
|
3747 void |
|
3748 nsIFrame::AssociateImage(const nsStyleImage& aImage, nsPresContext* aPresContext) |
|
3749 { |
|
3750 if (aImage.GetType() != eStyleImageType_Image) { |
|
3751 return; |
|
3752 } |
|
3753 |
|
3754 imgIRequest *req = aImage.GetImageData(); |
|
3755 mozilla::css::ImageLoader* loader = |
|
3756 aPresContext->Document()->StyleImageLoader(); |
|
3757 |
|
3758 // If this fails there's not much we can do ... |
|
3759 loader->AssociateRequestToFrame(req, this); |
|
3760 } |
|
3761 |
|
3762 nsresult |
|
3763 nsFrame::GetCursor(const nsPoint& aPoint, |
|
3764 nsIFrame::Cursor& aCursor) |
|
3765 { |
|
3766 FillCursorInformationFromStyle(StyleUserInterface(), aCursor); |
|
3767 if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) { |
|
3768 // If this is editable, I-beam cursor is better for most elements. |
|
3769 aCursor.mCursor = |
|
3770 (mContent && mContent->IsEditable()) ? NS_STYLE_CURSOR_TEXT : |
|
3771 NS_STYLE_CURSOR_DEFAULT; |
|
3772 } |
|
3773 |
|
3774 |
|
3775 return NS_OK; |
|
3776 } |
|
3777 |
|
3778 // Resize and incremental reflow |
|
3779 |
|
3780 /* virtual */ void |
|
3781 nsFrame::MarkIntrinsicWidthsDirty() |
|
3782 { |
|
3783 // This version is meant only for what used to be box-to-block adaptors. |
|
3784 // It should not be called by other derived classes. |
|
3785 if (IsBoxWrapped()) { |
|
3786 nsBoxLayoutMetrics *metrics = BoxMetrics(); |
|
3787 |
|
3788 SizeNeedsRecalc(metrics->mPrefSize); |
|
3789 SizeNeedsRecalc(metrics->mMinSize); |
|
3790 SizeNeedsRecalc(metrics->mMaxSize); |
|
3791 SizeNeedsRecalc(metrics->mBlockPrefSize); |
|
3792 SizeNeedsRecalc(metrics->mBlockMinSize); |
|
3793 CoordNeedsRecalc(metrics->mFlex); |
|
3794 CoordNeedsRecalc(metrics->mAscent); |
|
3795 } |
|
3796 |
|
3797 if (GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) { |
|
3798 nsFontInflationData::MarkFontInflationDataTextDirty(this); |
|
3799 } |
|
3800 } |
|
3801 |
|
3802 /* virtual */ nscoord |
|
3803 nsFrame::GetMinWidth(nsRenderingContext *aRenderingContext) |
|
3804 { |
|
3805 nscoord result = 0; |
|
3806 DISPLAY_MIN_WIDTH(this, result); |
|
3807 return result; |
|
3808 } |
|
3809 |
|
3810 /* virtual */ nscoord |
|
3811 nsFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) |
|
3812 { |
|
3813 nscoord result = 0; |
|
3814 DISPLAY_PREF_WIDTH(this, result); |
|
3815 return result; |
|
3816 } |
|
3817 |
|
3818 /* virtual */ void |
|
3819 nsFrame::AddInlineMinWidth(nsRenderingContext *aRenderingContext, |
|
3820 nsIFrame::InlineMinWidthData *aData) |
|
3821 { |
|
3822 NS_ASSERTION(GetParent(), "Must have a parent if we get here!"); |
|
3823 nsIFrame* parent = GetParent(); |
|
3824 bool canBreak = !CanContinueTextRun() && |
|
3825 parent->StyleText()->WhiteSpaceCanWrap(parent); |
|
3826 |
|
3827 if (canBreak) |
|
3828 aData->OptionallyBreak(aRenderingContext); |
|
3829 aData->trailingWhitespace = 0; |
|
3830 aData->skipWhitespace = false; |
|
3831 aData->trailingTextFrame = nullptr; |
|
3832 aData->currentLine += nsLayoutUtils::IntrinsicForContainer(aRenderingContext, |
|
3833 this, nsLayoutUtils::MIN_WIDTH); |
|
3834 aData->atStartOfLine = false; |
|
3835 if (canBreak) |
|
3836 aData->OptionallyBreak(aRenderingContext); |
|
3837 } |
|
3838 |
|
3839 /* virtual */ void |
|
3840 nsFrame::AddInlinePrefWidth(nsRenderingContext *aRenderingContext, |
|
3841 nsIFrame::InlinePrefWidthData *aData) |
|
3842 { |
|
3843 aData->trailingWhitespace = 0; |
|
3844 aData->skipWhitespace = false; |
|
3845 nscoord myPref = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, |
|
3846 this, nsLayoutUtils::PREF_WIDTH); |
|
3847 aData->currentLine = NSCoordSaturatingAdd(aData->currentLine, myPref); |
|
3848 } |
|
3849 |
|
3850 void |
|
3851 nsIFrame::InlineMinWidthData::ForceBreak(nsRenderingContext *aRenderingContext) |
|
3852 { |
|
3853 currentLine -= trailingWhitespace; |
|
3854 prevLines = std::max(prevLines, currentLine); |
|
3855 currentLine = trailingWhitespace = 0; |
|
3856 |
|
3857 for (uint32_t i = 0, i_end = floats.Length(); i != i_end; ++i) { |
|
3858 nscoord float_min = floats[i].Width(); |
|
3859 if (float_min > prevLines) |
|
3860 prevLines = float_min; |
|
3861 } |
|
3862 floats.Clear(); |
|
3863 trailingTextFrame = nullptr; |
|
3864 skipWhitespace = true; |
|
3865 } |
|
3866 |
|
3867 void |
|
3868 nsIFrame::InlineMinWidthData::OptionallyBreak(nsRenderingContext *aRenderingContext, |
|
3869 nscoord aHyphenWidth) |
|
3870 { |
|
3871 trailingTextFrame = nullptr; |
|
3872 |
|
3873 // If we can fit more content into a smaller width by staying on this |
|
3874 // line (because we're still at a negative offset due to negative |
|
3875 // text-indent or negative margin), don't break. Otherwise, do the |
|
3876 // same as ForceBreak. it doesn't really matter when we accumulate |
|
3877 // floats. |
|
3878 if (currentLine + aHyphenWidth < 0 || atStartOfLine) |
|
3879 return; |
|
3880 currentLine += aHyphenWidth; |
|
3881 ForceBreak(aRenderingContext); |
|
3882 } |
|
3883 |
|
3884 void |
|
3885 nsIFrame::InlinePrefWidthData::ForceBreak(nsRenderingContext *aRenderingContext) |
|
3886 { |
|
3887 if (floats.Length() != 0) { |
|
3888 // preferred widths accumulated for floats that have already |
|
3889 // been cleared past |
|
3890 nscoord floats_done = 0, |
|
3891 // preferred widths accumulated for floats that have not yet |
|
3892 // been cleared past |
|
3893 floats_cur_left = 0, |
|
3894 floats_cur_right = 0; |
|
3895 |
|
3896 for (uint32_t i = 0, i_end = floats.Length(); i != i_end; ++i) { |
|
3897 const FloatInfo& floatInfo = floats[i]; |
|
3898 const nsStyleDisplay *floatDisp = floatInfo.Frame()->StyleDisplay(); |
|
3899 if (floatDisp->mBreakType == NS_STYLE_CLEAR_LEFT || |
|
3900 floatDisp->mBreakType == NS_STYLE_CLEAR_RIGHT || |
|
3901 floatDisp->mBreakType == NS_STYLE_CLEAR_BOTH) { |
|
3902 nscoord floats_cur = NSCoordSaturatingAdd(floats_cur_left, |
|
3903 floats_cur_right); |
|
3904 if (floats_cur > floats_done) |
|
3905 floats_done = floats_cur; |
|
3906 if (floatDisp->mBreakType != NS_STYLE_CLEAR_RIGHT) |
|
3907 floats_cur_left = 0; |
|
3908 if (floatDisp->mBreakType != NS_STYLE_CLEAR_LEFT) |
|
3909 floats_cur_right = 0; |
|
3910 } |
|
3911 |
|
3912 nscoord &floats_cur = floatDisp->mFloats == NS_STYLE_FLOAT_LEFT |
|
3913 ? floats_cur_left : floats_cur_right; |
|
3914 nscoord floatWidth = floatInfo.Width(); |
|
3915 // Negative-width floats don't change the available space so they |
|
3916 // shouldn't change our intrinsic line width either. |
|
3917 floats_cur = |
|
3918 NSCoordSaturatingAdd(floats_cur, std::max(0, floatWidth)); |
|
3919 } |
|
3920 |
|
3921 nscoord floats_cur = |
|
3922 NSCoordSaturatingAdd(floats_cur_left, floats_cur_right); |
|
3923 if (floats_cur > floats_done) |
|
3924 floats_done = floats_cur; |
|
3925 |
|
3926 currentLine = NSCoordSaturatingAdd(currentLine, floats_done); |
|
3927 |
|
3928 floats.Clear(); |
|
3929 } |
|
3930 |
|
3931 currentLine = |
|
3932 NSCoordSaturatingSubtract(currentLine, trailingWhitespace, nscoord_MAX); |
|
3933 prevLines = std::max(prevLines, currentLine); |
|
3934 currentLine = trailingWhitespace = 0; |
|
3935 skipWhitespace = true; |
|
3936 } |
|
3937 |
|
3938 static void |
|
3939 AddCoord(const nsStyleCoord& aStyle, |
|
3940 nsRenderingContext* aRenderingContext, |
|
3941 nsIFrame* aFrame, |
|
3942 nscoord* aCoord, float* aPercent, |
|
3943 bool aClampNegativeToZero) |
|
3944 { |
|
3945 switch (aStyle.GetUnit()) { |
|
3946 case eStyleUnit_Coord: { |
|
3947 NS_ASSERTION(!aClampNegativeToZero || aStyle.GetCoordValue() >= 0, |
|
3948 "unexpected negative value"); |
|
3949 *aCoord += aStyle.GetCoordValue(); |
|
3950 return; |
|
3951 } |
|
3952 case eStyleUnit_Percent: { |
|
3953 NS_ASSERTION(!aClampNegativeToZero || aStyle.GetPercentValue() >= 0.0f, |
|
3954 "unexpected negative value"); |
|
3955 *aPercent += aStyle.GetPercentValue(); |
|
3956 return; |
|
3957 } |
|
3958 case eStyleUnit_Calc: { |
|
3959 const nsStyleCoord::Calc *calc = aStyle.GetCalcValue(); |
|
3960 if (aClampNegativeToZero) { |
|
3961 // This is far from ideal when one is negative and one is positive. |
|
3962 *aCoord += std::max(calc->mLength, 0); |
|
3963 *aPercent += std::max(calc->mPercent, 0.0f); |
|
3964 } else { |
|
3965 *aCoord += calc->mLength; |
|
3966 *aPercent += calc->mPercent; |
|
3967 } |
|
3968 return; |
|
3969 } |
|
3970 default: { |
|
3971 return; |
|
3972 } |
|
3973 } |
|
3974 } |
|
3975 |
|
3976 /* virtual */ nsIFrame::IntrinsicWidthOffsetData |
|
3977 nsFrame::IntrinsicWidthOffsets(nsRenderingContext* aRenderingContext) |
|
3978 { |
|
3979 IntrinsicWidthOffsetData result; |
|
3980 |
|
3981 const nsStyleMargin *styleMargin = StyleMargin(); |
|
3982 AddCoord(styleMargin->mMargin.GetLeft(), aRenderingContext, this, |
|
3983 &result.hMargin, &result.hPctMargin, false); |
|
3984 AddCoord(styleMargin->mMargin.GetRight(), aRenderingContext, this, |
|
3985 &result.hMargin, &result.hPctMargin, false); |
|
3986 |
|
3987 const nsStylePadding *stylePadding = StylePadding(); |
|
3988 AddCoord(stylePadding->mPadding.GetLeft(), aRenderingContext, this, |
|
3989 &result.hPadding, &result.hPctPadding, true); |
|
3990 AddCoord(stylePadding->mPadding.GetRight(), aRenderingContext, this, |
|
3991 &result.hPadding, &result.hPctPadding, true); |
|
3992 |
|
3993 const nsStyleBorder *styleBorder = StyleBorder(); |
|
3994 result.hBorder += styleBorder->GetComputedBorderWidth(NS_SIDE_LEFT); |
|
3995 result.hBorder += styleBorder->GetComputedBorderWidth(NS_SIDE_RIGHT); |
|
3996 |
|
3997 const nsStyleDisplay *disp = StyleDisplay(); |
|
3998 if (IsThemed(disp)) { |
|
3999 nsPresContext *presContext = PresContext(); |
|
4000 |
|
4001 nsIntMargin border; |
|
4002 presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(), |
|
4003 this, disp->mAppearance, |
|
4004 &border); |
|
4005 result.hBorder = presContext->DevPixelsToAppUnits(border.LeftRight()); |
|
4006 |
|
4007 nsIntMargin padding; |
|
4008 if (presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(), |
|
4009 this, disp->mAppearance, |
|
4010 &padding)) { |
|
4011 result.hPadding = presContext->DevPixelsToAppUnits(padding.LeftRight()); |
|
4012 result.hPctPadding = 0; |
|
4013 } |
|
4014 } |
|
4015 |
|
4016 return result; |
|
4017 } |
|
4018 |
|
4019 /* virtual */ IntrinsicSize |
|
4020 nsFrame::GetIntrinsicSize() |
|
4021 { |
|
4022 return IntrinsicSize(); // default is width/height set to eStyleUnit_None |
|
4023 } |
|
4024 |
|
4025 /* virtual */ nsSize |
|
4026 nsFrame::GetIntrinsicRatio() |
|
4027 { |
|
4028 return nsSize(0, 0); |
|
4029 } |
|
4030 |
|
4031 /* virtual */ nsSize |
|
4032 nsFrame::ComputeSize(nsRenderingContext *aRenderingContext, |
|
4033 nsSize aCBSize, nscoord aAvailableWidth, |
|
4034 nsSize aMargin, nsSize aBorder, nsSize aPadding, |
|
4035 uint32_t aFlags) |
|
4036 { |
|
4037 nsSize result = ComputeAutoSize(aRenderingContext, aCBSize, aAvailableWidth, |
|
4038 aMargin, aBorder, aPadding, |
|
4039 aFlags & eShrinkWrap); |
|
4040 nsSize boxSizingAdjust(0,0); |
|
4041 const nsStylePosition *stylePos = StylePosition(); |
|
4042 |
|
4043 switch (stylePos->mBoxSizing) { |
|
4044 case NS_STYLE_BOX_SIZING_BORDER: |
|
4045 boxSizingAdjust += aBorder; |
|
4046 // fall through |
|
4047 case NS_STYLE_BOX_SIZING_PADDING: |
|
4048 boxSizingAdjust += aPadding; |
|
4049 } |
|
4050 nscoord boxSizingToMarginEdgeWidth = |
|
4051 aMargin.width + aBorder.width + aPadding.width - boxSizingAdjust.width; |
|
4052 const nsStyleCoord* widthStyleCoord = &(stylePos->mWidth); |
|
4053 const nsStyleCoord* heightStyleCoord = &(stylePos->mHeight); |
|
4054 |
|
4055 bool isFlexItem = IsFlexItem(); |
|
4056 bool isHorizontalFlexItem = false; |
|
4057 |
|
4058 if (isFlexItem) { |
|
4059 // Flex items use their "flex-basis" property in place of their main-size |
|
4060 // property (e.g. "width") for sizing purposes, *unless* they have |
|
4061 // "flex-basis:auto", in which case they use their main-size property after |
|
4062 // all. |
|
4063 uint32_t flexDirection = mParent->StylePosition()->mFlexDirection; |
|
4064 isHorizontalFlexItem = |
|
4065 flexDirection == NS_STYLE_FLEX_DIRECTION_ROW || |
|
4066 flexDirection == NS_STYLE_FLEX_DIRECTION_ROW_REVERSE; |
|
4067 |
|
4068 // NOTE: The logic here should match the similar chunk for determining |
|
4069 // widthStyleCoord and heightStyleCoord in |
|
4070 // nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(). |
|
4071 const nsStyleCoord* flexBasis = &(stylePos->mFlexBasis); |
|
4072 if (flexBasis->GetUnit() != eStyleUnit_Auto) { |
|
4073 if (isHorizontalFlexItem) { |
|
4074 widthStyleCoord = flexBasis; |
|
4075 } else { |
|
4076 // One caveat for vertical flex items: We don't support enumerated |
|
4077 // values (e.g. "max-content") for height properties yet. So, if our |
|
4078 // computed flex-basis is an enumerated value, we'll just behave as if |
|
4079 // it were "auto", which means "use the main-size property after all" |
|
4080 // (which is "height", in this case). |
|
4081 // NOTE: Once we support intrinsic sizing keywords for "height", |
|
4082 // we should remove this check. |
|
4083 if (flexBasis->GetUnit() != eStyleUnit_Enumerated) { |
|
4084 heightStyleCoord = flexBasis; |
|
4085 } |
|
4086 } |
|
4087 } |
|
4088 } |
|
4089 |
|
4090 // Compute width |
|
4091 |
|
4092 if (widthStyleCoord->GetUnit() != eStyleUnit_Auto) { |
|
4093 result.width = |
|
4094 nsLayoutUtils::ComputeWidthValue(aRenderingContext, this, |
|
4095 aCBSize.width, boxSizingAdjust.width, boxSizingToMarginEdgeWidth, |
|
4096 *widthStyleCoord); |
|
4097 } |
|
4098 |
|
4099 // Flex items ignore their min & max sizing properties in their |
|
4100 // flex container's main-axis. (Those properties get applied later in |
|
4101 // the flexbox algorithm.) |
|
4102 if (stylePos->mMaxWidth.GetUnit() != eStyleUnit_None && |
|
4103 !(isFlexItem && isHorizontalFlexItem)) { |
|
4104 nscoord maxWidth = |
|
4105 nsLayoutUtils::ComputeWidthValue(aRenderingContext, this, |
|
4106 aCBSize.width, boxSizingAdjust.width, boxSizingToMarginEdgeWidth, |
|
4107 stylePos->mMaxWidth); |
|
4108 result.width = std::min(maxWidth, result.width); |
|
4109 } |
|
4110 |
|
4111 nscoord minWidth; |
|
4112 if (!(isFlexItem && isHorizontalFlexItem)) { |
|
4113 minWidth = |
|
4114 nsLayoutUtils::ComputeWidthValue(aRenderingContext, this, |
|
4115 aCBSize.width, boxSizingAdjust.width, boxSizingToMarginEdgeWidth, |
|
4116 stylePos->mMinWidth); |
|
4117 } else { |
|
4118 minWidth = 0; |
|
4119 } |
|
4120 result.width = std::max(minWidth, result.width); |
|
4121 |
|
4122 // Compute height |
|
4123 // (but not if we're auto-height or if we recieved the "eUseAutoHeight" |
|
4124 // flag -- then, we'll just stick with the height that we already calculated |
|
4125 // in the initial ComputeAutoSize() call.) |
|
4126 if (!nsLayoutUtils::IsAutoHeight(*heightStyleCoord, aCBSize.height) && |
|
4127 !(aFlags & nsIFrame::eUseAutoHeight)) { |
|
4128 result.height = |
|
4129 nsLayoutUtils::ComputeHeightValue(aCBSize.height, |
|
4130 boxSizingAdjust.height, |
|
4131 *heightStyleCoord); |
|
4132 } |
|
4133 |
|
4134 if (result.height != NS_UNCONSTRAINEDSIZE) { |
|
4135 if (!nsLayoutUtils::IsAutoHeight(stylePos->mMaxHeight, aCBSize.height) && |
|
4136 !(isFlexItem && !isHorizontalFlexItem)) { |
|
4137 nscoord maxHeight = |
|
4138 nsLayoutUtils::ComputeHeightValue(aCBSize.height, |
|
4139 boxSizingAdjust.height, |
|
4140 stylePos->mMaxHeight); |
|
4141 result.height = std::min(maxHeight, result.height); |
|
4142 } |
|
4143 |
|
4144 if (!nsLayoutUtils::IsAutoHeight(stylePos->mMinHeight, aCBSize.height) && |
|
4145 !(isFlexItem && !isHorizontalFlexItem)) { |
|
4146 nscoord minHeight = |
|
4147 nsLayoutUtils::ComputeHeightValue(aCBSize.height, |
|
4148 boxSizingAdjust.height, |
|
4149 stylePos->mMinHeight); |
|
4150 result.height = std::max(minHeight, result.height); |
|
4151 } |
|
4152 } |
|
4153 |
|
4154 const nsStyleDisplay *disp = StyleDisplay(); |
|
4155 if (IsThemed(disp)) { |
|
4156 nsIntSize widget(0, 0); |
|
4157 bool canOverride = true; |
|
4158 nsPresContext *presContext = PresContext(); |
|
4159 presContext->GetTheme()-> |
|
4160 GetMinimumWidgetSize(aRenderingContext, this, disp->mAppearance, |
|
4161 &widget, &canOverride); |
|
4162 |
|
4163 nsSize size; |
|
4164 size.width = presContext->DevPixelsToAppUnits(widget.width); |
|
4165 size.height = presContext->DevPixelsToAppUnits(widget.height); |
|
4166 |
|
4167 // GMWS() returns border-box; we need content-box |
|
4168 size.width -= aBorder.width + aPadding.width; |
|
4169 size.height -= aBorder.height + aPadding.height; |
|
4170 |
|
4171 if (size.height > result.height || !canOverride) |
|
4172 result.height = size.height; |
|
4173 if (size.width > result.width || !canOverride) |
|
4174 result.width = size.width; |
|
4175 } |
|
4176 |
|
4177 result.width = std::max(0, result.width); |
|
4178 result.height = std::max(0, result.height); |
|
4179 |
|
4180 return result; |
|
4181 } |
|
4182 |
|
4183 nsRect |
|
4184 nsIFrame::ComputeTightBounds(gfxContext* aContext) const |
|
4185 { |
|
4186 return GetVisualOverflowRect(); |
|
4187 } |
|
4188 |
|
4189 nsRect |
|
4190 nsFrame::ComputeSimpleTightBounds(gfxContext* aContext) const |
|
4191 { |
|
4192 if (StyleOutline()->GetOutlineStyle() != NS_STYLE_BORDER_STYLE_NONE || |
|
4193 StyleBorder()->HasBorder() || !StyleBackground()->IsTransparent() || |
|
4194 StyleDisplay()->mAppearance) { |
|
4195 // Not necessarily tight, due to clipping, negative |
|
4196 // outline-offset, and lots of other issues, but that's OK |
|
4197 return GetVisualOverflowRect(); |
|
4198 } |
|
4199 |
|
4200 nsRect r(0, 0, 0, 0); |
|
4201 ChildListIterator lists(this); |
|
4202 for (; !lists.IsDone(); lists.Next()) { |
|
4203 nsFrameList::Enumerator childFrames(lists.CurrentList()); |
|
4204 for (; !childFrames.AtEnd(); childFrames.Next()) { |
|
4205 nsIFrame* child = childFrames.get(); |
|
4206 r.UnionRect(r, child->ComputeTightBounds(aContext) + child->GetPosition()); |
|
4207 } |
|
4208 } |
|
4209 return r; |
|
4210 } |
|
4211 |
|
4212 /* virtual */ nsresult |
|
4213 nsIFrame::GetPrefWidthTightBounds(nsRenderingContext* aContext, |
|
4214 nscoord* aX, |
|
4215 nscoord* aXMost) |
|
4216 { |
|
4217 return NS_ERROR_NOT_IMPLEMENTED; |
|
4218 } |
|
4219 |
|
4220 /* virtual */ nsSize |
|
4221 nsFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, |
|
4222 nsSize aCBSize, nscoord aAvailableWidth, |
|
4223 nsSize aMargin, nsSize aBorder, nsSize aPadding, |
|
4224 bool aShrinkWrap) |
|
4225 { |
|
4226 // Use basic shrink-wrapping as a default implementation. |
|
4227 nsSize result(0xdeadbeef, NS_UNCONSTRAINEDSIZE); |
|
4228 |
|
4229 // don't bother setting it if the result won't be used |
|
4230 if (StylePosition()->mWidth.GetUnit() == eStyleUnit_Auto) { |
|
4231 nscoord availBased = aAvailableWidth - aMargin.width - aBorder.width - |
|
4232 aPadding.width; |
|
4233 result.width = ShrinkWidthToFit(aRenderingContext, availBased); |
|
4234 } |
|
4235 return result; |
|
4236 } |
|
4237 |
|
4238 nscoord |
|
4239 nsFrame::ShrinkWidthToFit(nsRenderingContext *aRenderingContext, |
|
4240 nscoord aWidthInCB) |
|
4241 { |
|
4242 // If we're a container for font size inflation, then shrink |
|
4243 // wrapping inside of us should not apply font size inflation. |
|
4244 AutoMaybeDisableFontInflation an(this); |
|
4245 |
|
4246 nscoord result; |
|
4247 nscoord minWidth = GetMinWidth(aRenderingContext); |
|
4248 if (minWidth > aWidthInCB) { |
|
4249 result = minWidth; |
|
4250 } else { |
|
4251 nscoord prefWidth = GetPrefWidth(aRenderingContext); |
|
4252 if (prefWidth > aWidthInCB) { |
|
4253 result = aWidthInCB; |
|
4254 } else { |
|
4255 result = prefWidth; |
|
4256 } |
|
4257 } |
|
4258 return result; |
|
4259 } |
|
4260 |
|
4261 nsresult |
|
4262 nsFrame::WillReflow(nsPresContext* aPresContext) |
|
4263 { |
|
4264 #ifdef DEBUG_dbaron_off |
|
4265 // bug 81268 |
|
4266 NS_ASSERTION(!(mState & NS_FRAME_IN_REFLOW), |
|
4267 "nsFrame::WillReflow: frame is already in reflow"); |
|
4268 #endif |
|
4269 |
|
4270 NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS, |
|
4271 ("WillReflow: oldState=%x", mState)); |
|
4272 mState |= NS_FRAME_IN_REFLOW; |
|
4273 return NS_OK; |
|
4274 } |
|
4275 |
|
4276 nsresult |
|
4277 nsFrame::DidReflow(nsPresContext* aPresContext, |
|
4278 const nsHTMLReflowState* aReflowState, |
|
4279 nsDidReflowStatus aStatus) |
|
4280 { |
|
4281 NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS, |
|
4282 ("nsFrame::DidReflow: aStatus=%d", static_cast<uint32_t>(aStatus))); |
|
4283 |
|
4284 nsSVGEffects::InvalidateDirectRenderingObservers(this, nsSVGEffects::INVALIDATE_REFLOW); |
|
4285 |
|
4286 if (nsDidReflowStatus::FINISHED == aStatus) { |
|
4287 mState &= ~(NS_FRAME_IN_REFLOW | NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY | |
|
4288 NS_FRAME_HAS_DIRTY_CHILDREN); |
|
4289 } |
|
4290 |
|
4291 // Notify the percent height observer if there is a percent height. |
|
4292 // The observer may be able to initiate another reflow with a computed |
|
4293 // height. This happens in the case where a table cell has no computed |
|
4294 // height but can fabricate one when the cell height is known. |
|
4295 if (aReflowState && aReflowState->mPercentHeightObserver && |
|
4296 !GetPrevInFlow()) { |
|
4297 const nsStyleCoord &height = aReflowState->mStylePosition->mHeight; |
|
4298 if (height.HasPercent()) { |
|
4299 aReflowState->mPercentHeightObserver->NotifyPercentHeight(*aReflowState); |
|
4300 } |
|
4301 } |
|
4302 |
|
4303 return NS_OK; |
|
4304 } |
|
4305 |
|
4306 void |
|
4307 nsFrame::FinishReflowWithAbsoluteFrames(nsPresContext* aPresContext, |
|
4308 nsHTMLReflowMetrics& aDesiredSize, |
|
4309 const nsHTMLReflowState& aReflowState, |
|
4310 nsReflowStatus& aStatus, |
|
4311 bool aConstrainHeight) |
|
4312 { |
|
4313 ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus, aConstrainHeight); |
|
4314 |
|
4315 FinishAndStoreOverflow(&aDesiredSize); |
|
4316 } |
|
4317 |
|
4318 void |
|
4319 nsFrame::ReflowAbsoluteFrames(nsPresContext* aPresContext, |
|
4320 nsHTMLReflowMetrics& aDesiredSize, |
|
4321 const nsHTMLReflowState& aReflowState, |
|
4322 nsReflowStatus& aStatus, |
|
4323 bool aConstrainHeight) |
|
4324 { |
|
4325 if (HasAbsolutelyPositionedChildren()) { |
|
4326 nsAbsoluteContainingBlock* absoluteContainer = GetAbsoluteContainingBlock(); |
|
4327 |
|
4328 // Let the absolutely positioned container reflow any absolutely positioned |
|
4329 // child frames that need to be reflowed |
|
4330 |
|
4331 // The containing block for the abs pos kids is formed by our padding edge. |
|
4332 nsMargin computedBorder = |
|
4333 aReflowState.ComputedPhysicalBorderPadding() - aReflowState.ComputedPhysicalPadding(); |
|
4334 nscoord containingBlockWidth = |
|
4335 aDesiredSize.Width() - computedBorder.LeftRight(); |
|
4336 nscoord containingBlockHeight = |
|
4337 aDesiredSize.Height() - computedBorder.TopBottom(); |
|
4338 |
|
4339 nsContainerFrame* container = do_QueryFrame(this); |
|
4340 NS_ASSERTION(container, "Abs-pos children only supported on container frames for now"); |
|
4341 |
|
4342 nsRect containingBlock(0, 0, containingBlockWidth, containingBlockHeight); |
|
4343 absoluteContainer->Reflow(container, aPresContext, aReflowState, aStatus, |
|
4344 containingBlock, |
|
4345 aConstrainHeight, true, true, // XXX could be optimized |
|
4346 &aDesiredSize.mOverflowAreas); |
|
4347 } |
|
4348 } |
|
4349 |
|
4350 /* virtual */ bool |
|
4351 nsFrame::CanContinueTextRun() const |
|
4352 { |
|
4353 // By default, a frame will *not* allow a text run to be continued |
|
4354 // through it. |
|
4355 return false; |
|
4356 } |
|
4357 |
|
4358 nsresult |
|
4359 nsFrame::Reflow(nsPresContext* aPresContext, |
|
4360 nsHTMLReflowMetrics& aDesiredSize, |
|
4361 const nsHTMLReflowState& aReflowState, |
|
4362 nsReflowStatus& aStatus) |
|
4363 { |
|
4364 DO_GLOBAL_REFLOW_COUNT("nsFrame"); |
|
4365 aDesiredSize.Width() = 0; |
|
4366 aDesiredSize.Height() = 0; |
|
4367 aStatus = NS_FRAME_COMPLETE; |
|
4368 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); |
|
4369 return NS_OK; |
|
4370 } |
|
4371 |
|
4372 nsresult |
|
4373 nsFrame::CharacterDataChanged(CharacterDataChangeInfo* aInfo) |
|
4374 { |
|
4375 NS_NOTREACHED("should only be called for text frames"); |
|
4376 return NS_OK; |
|
4377 } |
|
4378 |
|
4379 nsresult |
|
4380 nsFrame::AttributeChanged(int32_t aNameSpaceID, |
|
4381 nsIAtom* aAttribute, |
|
4382 int32_t aModType) |
|
4383 { |
|
4384 return NS_OK; |
|
4385 } |
|
4386 |
|
4387 // Flow member functions |
|
4388 |
|
4389 nsSplittableType |
|
4390 nsFrame::GetSplittableType() const |
|
4391 { |
|
4392 return NS_FRAME_NOT_SPLITTABLE; |
|
4393 } |
|
4394 |
|
4395 nsIFrame* nsFrame::GetPrevContinuation() const |
|
4396 { |
|
4397 return nullptr; |
|
4398 } |
|
4399 |
|
4400 void |
|
4401 nsFrame::SetPrevContinuation(nsIFrame* aPrevContinuation) |
|
4402 { |
|
4403 MOZ_ASSERT(false, "not splittable"); |
|
4404 } |
|
4405 |
|
4406 nsIFrame* nsFrame::GetNextContinuation() const |
|
4407 { |
|
4408 return nullptr; |
|
4409 } |
|
4410 |
|
4411 void |
|
4412 nsFrame::SetNextContinuation(nsIFrame*) |
|
4413 { |
|
4414 MOZ_ASSERT(false, "not splittable"); |
|
4415 } |
|
4416 |
|
4417 nsIFrame* nsFrame::GetPrevInFlowVirtual() const |
|
4418 { |
|
4419 return nullptr; |
|
4420 } |
|
4421 |
|
4422 void |
|
4423 nsFrame::SetPrevInFlow(nsIFrame* aPrevInFlow) |
|
4424 { |
|
4425 MOZ_ASSERT(false, "not splittable"); |
|
4426 } |
|
4427 |
|
4428 nsIFrame* nsFrame::GetNextInFlowVirtual() const |
|
4429 { |
|
4430 return nullptr; |
|
4431 } |
|
4432 |
|
4433 void |
|
4434 nsFrame::SetNextInFlow(nsIFrame*) |
|
4435 { |
|
4436 MOZ_ASSERT(false, "not splittable"); |
|
4437 } |
|
4438 |
|
4439 nsIFrame* nsIFrame::GetTailContinuation() |
|
4440 { |
|
4441 nsIFrame* frame = this; |
|
4442 while (frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) { |
|
4443 frame = frame->GetPrevContinuation(); |
|
4444 NS_ASSERTION(frame, "first continuation can't be overflow container"); |
|
4445 } |
|
4446 for (nsIFrame* next = frame->GetNextContinuation(); |
|
4447 next && !(next->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER); |
|
4448 next = frame->GetNextContinuation()) { |
|
4449 frame = next; |
|
4450 } |
|
4451 NS_POSTCONDITION(frame, "illegal state in continuation chain."); |
|
4452 return frame; |
|
4453 } |
|
4454 |
|
4455 NS_DECLARE_FRAME_PROPERTY(ViewProperty, nullptr) |
|
4456 |
|
4457 // Associated view object |
|
4458 nsView* |
|
4459 nsIFrame::GetView() const |
|
4460 { |
|
4461 // Check the frame state bit and see if the frame has a view |
|
4462 if (!(GetStateBits() & NS_FRAME_HAS_VIEW)) |
|
4463 return nullptr; |
|
4464 |
|
4465 // Check for a property on the frame |
|
4466 void* value = Properties().Get(ViewProperty()); |
|
4467 NS_ASSERTION(value, "frame state bit was set but frame has no view"); |
|
4468 return static_cast<nsView*>(value); |
|
4469 } |
|
4470 |
|
4471 /* virtual */ nsView* |
|
4472 nsIFrame::GetViewExternal() const |
|
4473 { |
|
4474 return GetView(); |
|
4475 } |
|
4476 |
|
4477 nsresult |
|
4478 nsIFrame::SetView(nsView* aView) |
|
4479 { |
|
4480 if (aView) { |
|
4481 aView->SetFrame(this); |
|
4482 |
|
4483 #ifdef DEBUG |
|
4484 nsIAtom* frameType = GetType(); |
|
4485 NS_ASSERTION(frameType == nsGkAtoms::scrollFrame || |
|
4486 frameType == nsGkAtoms::subDocumentFrame || |
|
4487 frameType == nsGkAtoms::listControlFrame || |
|
4488 frameType == nsGkAtoms::objectFrame || |
|
4489 frameType == nsGkAtoms::viewportFrame || |
|
4490 frameType == nsGkAtoms::menuPopupFrame, |
|
4491 "Only specific frame types can have an nsView"); |
|
4492 #endif |
|
4493 |
|
4494 // Set a property on the frame |
|
4495 Properties().Set(ViewProperty(), aView); |
|
4496 |
|
4497 // Set the frame state bit that says the frame has a view |
|
4498 AddStateBits(NS_FRAME_HAS_VIEW); |
|
4499 |
|
4500 // Let all of the ancestors know they have a descendant with a view. |
|
4501 for (nsIFrame* f = GetParent(); |
|
4502 f && !(f->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW); |
|
4503 f = f->GetParent()) |
|
4504 f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW); |
|
4505 } |
|
4506 |
|
4507 return NS_OK; |
|
4508 } |
|
4509 |
|
4510 nsIFrame* nsIFrame::GetAncestorWithViewExternal() const |
|
4511 { |
|
4512 return GetAncestorWithView(); |
|
4513 } |
|
4514 |
|
4515 // Find the first geometric parent that has a view |
|
4516 nsIFrame* nsIFrame::GetAncestorWithView() const |
|
4517 { |
|
4518 for (nsIFrame* f = mParent; nullptr != f; f = f->GetParent()) { |
|
4519 if (f->HasView()) { |
|
4520 return f; |
|
4521 } |
|
4522 } |
|
4523 return nullptr; |
|
4524 } |
|
4525 |
|
4526 // virtual |
|
4527 nsPoint nsIFrame::GetOffsetToExternal(const nsIFrame* aOther) const |
|
4528 { |
|
4529 return GetOffsetTo(aOther); |
|
4530 } |
|
4531 |
|
4532 nsPoint nsIFrame::GetOffsetTo(const nsIFrame* aOther) const |
|
4533 { |
|
4534 NS_PRECONDITION(aOther, |
|
4535 "Must have frame for destination coordinate system!"); |
|
4536 |
|
4537 NS_ASSERTION(PresContext() == aOther->PresContext(), |
|
4538 "GetOffsetTo called on frames in different documents"); |
|
4539 |
|
4540 nsPoint offset(0, 0); |
|
4541 const nsIFrame* f; |
|
4542 for (f = this; f != aOther && f; f = f->GetParent()) { |
|
4543 offset += f->GetPosition(); |
|
4544 } |
|
4545 |
|
4546 if (f != aOther) { |
|
4547 // Looks like aOther wasn't an ancestor of |this|. So now we have |
|
4548 // the root-frame-relative position of |this| in |offset|. Convert back |
|
4549 // to the coordinates of aOther |
|
4550 while (aOther) { |
|
4551 offset -= aOther->GetPosition(); |
|
4552 aOther = aOther->GetParent(); |
|
4553 } |
|
4554 } |
|
4555 |
|
4556 return offset; |
|
4557 } |
|
4558 |
|
4559 nsPoint nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther) const |
|
4560 { |
|
4561 return GetOffsetToCrossDoc(aOther, PresContext()->AppUnitsPerDevPixel()); |
|
4562 } |
|
4563 |
|
4564 nsPoint |
|
4565 nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther, const int32_t aAPD) const |
|
4566 { |
|
4567 NS_PRECONDITION(aOther, |
|
4568 "Must have frame for destination coordinate system!"); |
|
4569 NS_ASSERTION(PresContext()->GetRootPresContext() == |
|
4570 aOther->PresContext()->GetRootPresContext(), |
|
4571 "trying to get the offset between frames in different document " |
|
4572 "hierarchies?"); |
|
4573 if (PresContext()->GetRootPresContext() != |
|
4574 aOther->PresContext()->GetRootPresContext()) { |
|
4575 // crash right away, we are almost certainly going to crash anyway. |
|
4576 NS_RUNTIMEABORT("trying to get the offset between frames in different " |
|
4577 "document hierarchies?"); |
|
4578 } |
|
4579 |
|
4580 const nsIFrame* root = nullptr; |
|
4581 // offset will hold the final offset |
|
4582 // docOffset holds the currently accumulated offset at the current APD, it |
|
4583 // will be converted and added to offset when the current APD changes. |
|
4584 nsPoint offset(0, 0), docOffset(0, 0); |
|
4585 const nsIFrame* f = this; |
|
4586 int32_t currAPD = PresContext()->AppUnitsPerDevPixel(); |
|
4587 while (f && f != aOther) { |
|
4588 docOffset += f->GetPosition(); |
|
4589 nsIFrame* parent = f->GetParent(); |
|
4590 if (parent) { |
|
4591 f = parent; |
|
4592 } else { |
|
4593 nsPoint newOffset(0, 0); |
|
4594 root = f; |
|
4595 f = nsLayoutUtils::GetCrossDocParentFrame(f, &newOffset); |
|
4596 int32_t newAPD = f ? f->PresContext()->AppUnitsPerDevPixel() : 0; |
|
4597 if (!f || newAPD != currAPD) { |
|
4598 // Convert docOffset to the right APD and add it to offset. |
|
4599 offset += docOffset.ConvertAppUnits(currAPD, aAPD); |
|
4600 docOffset.x = docOffset.y = 0; |
|
4601 } |
|
4602 currAPD = newAPD; |
|
4603 docOffset += newOffset; |
|
4604 } |
|
4605 } |
|
4606 if (f == aOther) { |
|
4607 offset += docOffset.ConvertAppUnits(currAPD, aAPD); |
|
4608 } else { |
|
4609 // Looks like aOther wasn't an ancestor of |this|. So now we have |
|
4610 // the root-document-relative position of |this| in |offset|. Subtract the |
|
4611 // root-document-relative position of |aOther| from |offset|. |
|
4612 // This call won't try to recurse again because root is an ancestor of |
|
4613 // aOther. |
|
4614 nsPoint negOffset = aOther->GetOffsetToCrossDoc(root, aAPD); |
|
4615 offset -= negOffset; |
|
4616 } |
|
4617 |
|
4618 return offset; |
|
4619 } |
|
4620 |
|
4621 // virtual |
|
4622 nsIntRect nsIFrame::GetScreenRectExternal() const |
|
4623 { |
|
4624 return GetScreenRect(); |
|
4625 } |
|
4626 |
|
4627 nsIntRect nsIFrame::GetScreenRect() const |
|
4628 { |
|
4629 return GetScreenRectInAppUnits().ToNearestPixels(PresContext()->AppUnitsPerCSSPixel()); |
|
4630 } |
|
4631 |
|
4632 // virtual |
|
4633 nsRect nsIFrame::GetScreenRectInAppUnitsExternal() const |
|
4634 { |
|
4635 return GetScreenRectInAppUnits(); |
|
4636 } |
|
4637 |
|
4638 nsRect nsIFrame::GetScreenRectInAppUnits() const |
|
4639 { |
|
4640 nsPresContext* presContext = PresContext(); |
|
4641 nsIFrame* rootFrame = |
|
4642 presContext->PresShell()->FrameManager()->GetRootFrame(); |
|
4643 nsPoint rootScreenPos(0, 0); |
|
4644 nsPoint rootFrameOffsetInParent(0, 0); |
|
4645 nsIFrame* rootFrameParent = |
|
4646 nsLayoutUtils::GetCrossDocParentFrame(rootFrame, &rootFrameOffsetInParent); |
|
4647 if (rootFrameParent) { |
|
4648 nsRect parentScreenRectAppUnits = rootFrameParent->GetScreenRectInAppUnits(); |
|
4649 nsPresContext* parentPresContext = rootFrameParent->PresContext(); |
|
4650 double parentScale = double(presContext->AppUnitsPerDevPixel())/ |
|
4651 parentPresContext->AppUnitsPerDevPixel(); |
|
4652 nsPoint rootPt = parentScreenRectAppUnits.TopLeft() + rootFrameOffsetInParent; |
|
4653 rootScreenPos.x = NS_round(parentScale*rootPt.x); |
|
4654 rootScreenPos.y = NS_round(parentScale*rootPt.y); |
|
4655 } else { |
|
4656 nsCOMPtr<nsIWidget> rootWidget; |
|
4657 presContext->PresShell()->GetViewManager()->GetRootWidget(getter_AddRefs(rootWidget)); |
|
4658 if (rootWidget) { |
|
4659 nsIntPoint rootDevPx = rootWidget->WidgetToScreenOffset(); |
|
4660 rootScreenPos.x = presContext->DevPixelsToAppUnits(rootDevPx.x); |
|
4661 rootScreenPos.y = presContext->DevPixelsToAppUnits(rootDevPx.y); |
|
4662 } |
|
4663 } |
|
4664 |
|
4665 return nsRect(rootScreenPos + GetOffsetTo(rootFrame), GetSize()); |
|
4666 } |
|
4667 |
|
4668 // Returns the offset from this frame to the closest geometric parent that |
|
4669 // has a view. Also returns the containing view or null in case of error |
|
4670 void |
|
4671 nsIFrame::GetOffsetFromView(nsPoint& aOffset, nsView** aView) const |
|
4672 { |
|
4673 NS_PRECONDITION(nullptr != aView, "null OUT parameter pointer"); |
|
4674 nsIFrame* frame = const_cast<nsIFrame*>(this); |
|
4675 |
|
4676 *aView = nullptr; |
|
4677 aOffset.MoveTo(0, 0); |
|
4678 do { |
|
4679 aOffset += frame->GetPosition(); |
|
4680 frame = frame->GetParent(); |
|
4681 } while (frame && !frame->HasView()); |
|
4682 |
|
4683 if (frame) { |
|
4684 *aView = frame->GetView(); |
|
4685 } |
|
4686 } |
|
4687 |
|
4688 nsIWidget* |
|
4689 nsIFrame::GetNearestWidget() const |
|
4690 { |
|
4691 return GetClosestView()->GetNearestWidget(nullptr); |
|
4692 } |
|
4693 |
|
4694 nsIWidget* |
|
4695 nsIFrame::GetNearestWidget(nsPoint& aOffset) const |
|
4696 { |
|
4697 nsPoint offsetToView; |
|
4698 nsPoint offsetToWidget; |
|
4699 nsIWidget* widget = |
|
4700 GetClosestView(&offsetToView)->GetNearestWidget(&offsetToWidget); |
|
4701 aOffset = offsetToView + offsetToWidget; |
|
4702 return widget; |
|
4703 } |
|
4704 |
|
4705 nsIAtom* |
|
4706 nsFrame::GetType() const |
|
4707 { |
|
4708 return nullptr; |
|
4709 } |
|
4710 |
|
4711 bool |
|
4712 nsIFrame::IsLeaf() const |
|
4713 { |
|
4714 return true; |
|
4715 } |
|
4716 |
|
4717 gfx3DMatrix |
|
4718 nsIFrame::GetTransformMatrix(const nsIFrame* aStopAtAncestor, |
|
4719 nsIFrame** aOutAncestor) |
|
4720 { |
|
4721 NS_PRECONDITION(aOutAncestor, "Need a place to put the ancestor!"); |
|
4722 |
|
4723 /* If we're transformed, we want to hand back the combination |
|
4724 * transform/translate matrix that will apply our current transform, then |
|
4725 * shift us to our parent. |
|
4726 */ |
|
4727 if (IsTransformed()) { |
|
4728 /* Compute the delta to the parent, which we need because we are converting |
|
4729 * coordinates to our parent. |
|
4730 */ |
|
4731 NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrame(this), |
|
4732 "Cannot transform the viewport frame!"); |
|
4733 int32_t scaleFactor = PresContext()->AppUnitsPerDevPixel(); |
|
4734 |
|
4735 gfx3DMatrix result = |
|
4736 nsDisplayTransform::GetResultingTransformMatrix(this, nsPoint(0, 0), scaleFactor, nullptr, aOutAncestor); |
|
4737 // XXXjwatt: seems like this will double count offsets in the face of preserve-3d: |
|
4738 nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor); |
|
4739 /* Combine the raw transform with a translation to our parent. */ |
|
4740 result *= gfx3DMatrix::Translation |
|
4741 (NSAppUnitsToFloatPixels(delta.x, scaleFactor), |
|
4742 NSAppUnitsToFloatPixels(delta.y, scaleFactor), |
|
4743 0.0f); |
|
4744 return result; |
|
4745 } |
|
4746 |
|
4747 if (nsLayoutUtils::IsPopup(this) && |
|
4748 GetType() == nsGkAtoms::listControlFrame) { |
|
4749 nsPresContext* presContext = PresContext(); |
|
4750 nsIFrame* docRootFrame = presContext->PresShell()->GetRootFrame(); |
|
4751 |
|
4752 // Compute a matrix that transforms from the popup widget to the toplevel |
|
4753 // widget. We use the widgets because they're the simplest and most |
|
4754 // accurate approach --- this should work no matter how the widget position |
|
4755 // was chosen. |
|
4756 nsIWidget* widget = GetView()->GetWidget(); |
|
4757 nsPresContext* rootPresContext = PresContext()->GetRootPresContext(); |
|
4758 // Maybe the widget hasn't been created yet? Popups without widgets are |
|
4759 // treated as regular frames. That should work since they'll be rendered |
|
4760 // as part of the page if they're rendered at all. |
|
4761 if (widget && rootPresContext) { |
|
4762 nsIWidget* toplevel = rootPresContext->GetNearestWidget(); |
|
4763 if (toplevel) { |
|
4764 nsIntRect screenBounds; |
|
4765 widget->GetClientBounds(screenBounds); |
|
4766 nsIntRect toplevelScreenBounds; |
|
4767 toplevel->GetClientBounds(toplevelScreenBounds); |
|
4768 nsIntPoint translation = screenBounds.TopLeft() - toplevelScreenBounds.TopLeft(); |
|
4769 |
|
4770 gfx3DMatrix transformToTop; |
|
4771 transformToTop._41 = translation.x; |
|
4772 transformToTop._42 = translation.y; |
|
4773 |
|
4774 *aOutAncestor = docRootFrame; |
|
4775 gfx3DMatrix docRootTransformToTop = |
|
4776 nsLayoutUtils::GetTransformToAncestor(docRootFrame, nullptr); |
|
4777 if (docRootTransformToTop.IsSingular()) { |
|
4778 NS_WARNING("Containing document is invisible, we can't compute a valid transform"); |
|
4779 } else { |
|
4780 gfx3DMatrix topToDocRootTransform = docRootTransformToTop.Inverse(); |
|
4781 return transformToTop*topToDocRootTransform; |
|
4782 } |
|
4783 } |
|
4784 } |
|
4785 } |
|
4786 |
|
4787 *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(this); |
|
4788 |
|
4789 /* Otherwise, we're not transformed. In that case, we'll walk up the frame |
|
4790 * tree until we either hit the root frame or something that may be |
|
4791 * transformed. We'll then change coordinates into that frame, since we're |
|
4792 * guaranteed that nothing in-between can be transformed. First, however, |
|
4793 * we have to check to see if we have a parent. If not, we'll set the |
|
4794 * outparam to null (indicating that there's nothing left) and will hand back |
|
4795 * the identity matrix. |
|
4796 */ |
|
4797 if (!*aOutAncestor) |
|
4798 return gfx3DMatrix(); |
|
4799 |
|
4800 /* Keep iterating while the frame can't possibly be transformed. */ |
|
4801 while (!(*aOutAncestor)->IsTransformed() && |
|
4802 !nsLayoutUtils::IsPopup(*aOutAncestor) && |
|
4803 *aOutAncestor != aStopAtAncestor) { |
|
4804 /* If no parent, stop iterating. Otherwise, update the ancestor. */ |
|
4805 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(*aOutAncestor); |
|
4806 if (!parent) |
|
4807 break; |
|
4808 |
|
4809 *aOutAncestor = parent; |
|
4810 } |
|
4811 |
|
4812 NS_ASSERTION(*aOutAncestor, "Somehow ended up with a null ancestor...?"); |
|
4813 |
|
4814 /* Translate from this frame to our ancestor, if it exists. That's the |
|
4815 * entire transform, so we're done. |
|
4816 */ |
|
4817 nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor); |
|
4818 int32_t scaleFactor = PresContext()->AppUnitsPerDevPixel(); |
|
4819 return gfx3DMatrix().Translation |
|
4820 (NSAppUnitsToFloatPixels(delta.x, scaleFactor), |
|
4821 NSAppUnitsToFloatPixels(delta.y, scaleFactor), |
|
4822 0.0f); |
|
4823 } |
|
4824 |
|
4825 static void InvalidateFrameInternal(nsIFrame *aFrame, bool aHasDisplayItem = true) |
|
4826 { |
|
4827 if (aHasDisplayItem) { |
|
4828 aFrame->AddStateBits(NS_FRAME_NEEDS_PAINT); |
|
4829 } |
|
4830 nsSVGEffects::InvalidateDirectRenderingObservers(aFrame); |
|
4831 bool needsSchedulePaint = false; |
|
4832 if (nsLayoutUtils::IsPopup(aFrame)) { |
|
4833 needsSchedulePaint = true; |
|
4834 } else { |
|
4835 nsIFrame *parent = nsLayoutUtils::GetCrossDocParentFrame(aFrame); |
|
4836 while (parent && !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) { |
|
4837 if (aHasDisplayItem) { |
|
4838 parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT); |
|
4839 } |
|
4840 nsSVGEffects::InvalidateDirectRenderingObservers(parent); |
|
4841 |
|
4842 // If we're inside a popup, then we need to make sure that we |
|
4843 // call schedule paint so that the NS_FRAME_UPDATE_LAYER_TREE |
|
4844 // flag gets added to the popup display root frame. |
|
4845 if (nsLayoutUtils::IsPopup(parent)) { |
|
4846 needsSchedulePaint = true; |
|
4847 break; |
|
4848 } |
|
4849 parent = nsLayoutUtils::GetCrossDocParentFrame(parent); |
|
4850 } |
|
4851 if (!parent) { |
|
4852 needsSchedulePaint = true; |
|
4853 } |
|
4854 } |
|
4855 if (!aHasDisplayItem) { |
|
4856 return; |
|
4857 } |
|
4858 if (needsSchedulePaint) { |
|
4859 aFrame->SchedulePaint(); |
|
4860 } |
|
4861 if (aFrame->HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) { |
|
4862 aFrame->Properties().Delete(nsIFrame::InvalidationRect()); |
|
4863 aFrame->RemoveStateBits(NS_FRAME_HAS_INVALID_RECT); |
|
4864 } |
|
4865 } |
|
4866 |
|
4867 void |
|
4868 nsIFrame::InvalidateFrameSubtree(uint32_t aDisplayItemKey) |
|
4869 { |
|
4870 bool hasDisplayItem = |
|
4871 !aDisplayItemKey || FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey); |
|
4872 InvalidateFrame(aDisplayItemKey); |
|
4873 |
|
4874 if (HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT) || !hasDisplayItem) { |
|
4875 return; |
|
4876 } |
|
4877 |
|
4878 AddStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT); |
|
4879 |
|
4880 nsAutoTArray<nsIFrame::ChildList,4> childListArray; |
|
4881 GetCrossDocChildLists(&childListArray); |
|
4882 |
|
4883 nsIFrame::ChildListArrayIterator lists(childListArray); |
|
4884 for (; !lists.IsDone(); lists.Next()) { |
|
4885 nsFrameList::Enumerator childFrames(lists.CurrentList()); |
|
4886 for (; !childFrames.AtEnd(); childFrames.Next()) { |
|
4887 childFrames.get()->InvalidateFrameSubtree(); |
|
4888 } |
|
4889 } |
|
4890 } |
|
4891 |
|
4892 void |
|
4893 nsIFrame::ClearInvalidationStateBits() |
|
4894 { |
|
4895 if (HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) { |
|
4896 nsAutoTArray<nsIFrame::ChildList,4> childListArray; |
|
4897 GetCrossDocChildLists(&childListArray); |
|
4898 |
|
4899 nsIFrame::ChildListArrayIterator lists(childListArray); |
|
4900 for (; !lists.IsDone(); lists.Next()) { |
|
4901 nsFrameList::Enumerator childFrames(lists.CurrentList()); |
|
4902 for (; !childFrames.AtEnd(); childFrames.Next()) { |
|
4903 childFrames.get()->ClearInvalidationStateBits(); |
|
4904 } |
|
4905 } |
|
4906 } |
|
4907 |
|
4908 RemoveStateBits(NS_FRAME_NEEDS_PAINT | |
|
4909 NS_FRAME_DESCENDANT_NEEDS_PAINT | |
|
4910 NS_FRAME_ALL_DESCENDANTS_NEED_PAINT); |
|
4911 } |
|
4912 |
|
4913 void |
|
4914 nsIFrame::InvalidateFrame(uint32_t aDisplayItemKey) |
|
4915 { |
|
4916 bool hasDisplayItem = |
|
4917 !aDisplayItemKey || FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey); |
|
4918 InvalidateFrameInternal(this, hasDisplayItem); |
|
4919 } |
|
4920 |
|
4921 void |
|
4922 nsIFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey) |
|
4923 { |
|
4924 bool hasDisplayItem = |
|
4925 !aDisplayItemKey || FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey); |
|
4926 bool alreadyInvalid = false; |
|
4927 if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) { |
|
4928 InvalidateFrameInternal(this, hasDisplayItem); |
|
4929 } else { |
|
4930 alreadyInvalid = true; |
|
4931 } |
|
4932 |
|
4933 if (!hasDisplayItem) { |
|
4934 return; |
|
4935 } |
|
4936 |
|
4937 nsRect *rect = static_cast<nsRect*>(Properties().Get(InvalidationRect())); |
|
4938 if (!rect) { |
|
4939 if (alreadyInvalid) { |
|
4940 return; |
|
4941 } |
|
4942 rect = new nsRect(); |
|
4943 Properties().Set(InvalidationRect(), rect); |
|
4944 AddStateBits(NS_FRAME_HAS_INVALID_RECT); |
|
4945 } |
|
4946 |
|
4947 *rect = rect->Union(aRect); |
|
4948 } |
|
4949 |
|
4950 /*static*/ uint8_t nsIFrame::sLayerIsPrerenderedDataKey; |
|
4951 |
|
4952 bool |
|
4953 nsIFrame::TryUpdateTransformOnly(Layer** aLayerResult) |
|
4954 { |
|
4955 Layer* layer = FrameLayerBuilder::GetDedicatedLayer( |
|
4956 this, nsDisplayItem::TYPE_TRANSFORM); |
|
4957 if (!layer || !layer->HasUserData(LayerIsPrerenderedDataKey())) { |
|
4958 // If this layer isn't prerendered or we clip composites to our OS |
|
4959 // window, then we can't correctly optimize to an empty |
|
4960 // transaction in general. |
|
4961 return false; |
|
4962 } |
|
4963 |
|
4964 gfx3DMatrix transform3d; |
|
4965 if (!nsLayoutUtils::GetLayerTransformForFrame(this, &transform3d)) { |
|
4966 // We're not able to compute a layer transform that we know would |
|
4967 // be used at the next layers transaction, so we can't only update |
|
4968 // the transform and will need to schedule an invalidating paint. |
|
4969 return false; |
|
4970 } |
|
4971 gfxMatrix transform; |
|
4972 gfx::Matrix previousTransform; |
|
4973 // FIXME/bug 796690 and 796705: in general, changes to 3D |
|
4974 // transforms, or transform changes to properties other than |
|
4975 // translation, may lead us to choose a different rendering |
|
4976 // resolution for our layer. So if the transform is 3D or has a |
|
4977 // non-translation change, bail and schedule an invalidating paint. |
|
4978 // (We can often do better than this, for example for scale-down |
|
4979 // changes.) |
|
4980 static const gfx::Float kError = 0.0001f; |
|
4981 if (!transform3d.Is2D(&transform) || |
|
4982 !layer->GetBaseTransform().Is2D(&previousTransform) || |
|
4983 !gfx::FuzzyEqual(transform.xx, previousTransform._11, kError) || |
|
4984 !gfx::FuzzyEqual(transform.yy, previousTransform._22, kError) || |
|
4985 !gfx::FuzzyEqual(transform.xy, previousTransform._21, kError) || |
|
4986 !gfx::FuzzyEqual(transform.yx, previousTransform._12, kError)) { |
|
4987 return false; |
|
4988 } |
|
4989 gfx::Matrix4x4 matrix; |
|
4990 gfx::ToMatrix4x4(transform3d, matrix); |
|
4991 layer->SetBaseTransformForNextTransaction(matrix); |
|
4992 *aLayerResult = layer; |
|
4993 return true; |
|
4994 } |
|
4995 |
|
4996 bool |
|
4997 nsIFrame::IsInvalid(nsRect& aRect) |
|
4998 { |
|
4999 if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) { |
|
5000 return false; |
|
5001 } |
|
5002 |
|
5003 if (HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) { |
|
5004 nsRect *rect = static_cast<nsRect*>(Properties().Get(InvalidationRect())); |
|
5005 NS_ASSERTION(rect, "Must have an invalid rect if NS_FRAME_HAS_INVALID_RECT is set!"); |
|
5006 aRect = *rect; |
|
5007 } else { |
|
5008 aRect.SetEmpty(); |
|
5009 } |
|
5010 return true; |
|
5011 } |
|
5012 |
|
5013 void |
|
5014 nsIFrame::SchedulePaint(PaintType aType) |
|
5015 { |
|
5016 nsIFrame *displayRoot = nsLayoutUtils::GetDisplayRootFrame(this); |
|
5017 nsPresContext *pres = displayRoot->PresContext()->GetRootPresContext(); |
|
5018 |
|
5019 // No need to schedule a paint for an external document since they aren't |
|
5020 // painted directly. |
|
5021 if (!pres || (pres->Document() && pres->Document()->IsResourceDoc())) { |
|
5022 return; |
|
5023 } |
|
5024 if (!pres->GetContainerWeak()) { |
|
5025 NS_WARNING("Shouldn't call SchedulePaint in a detached pres context"); |
|
5026 return; |
|
5027 } |
|
5028 |
|
5029 pres->PresShell()->ScheduleViewManagerFlush(aType == PAINT_DELAYED_COMPRESS ? |
|
5030 nsIPresShell::PAINT_DELAYED_COMPRESS : |
|
5031 nsIPresShell::PAINT_DEFAULT); |
|
5032 |
|
5033 if (aType == PAINT_DELAYED_COMPRESS) { |
|
5034 return; |
|
5035 } |
|
5036 |
|
5037 if (aType == PAINT_DEFAULT) { |
|
5038 displayRoot->AddStateBits(NS_FRAME_UPDATE_LAYER_TREE); |
|
5039 } |
|
5040 nsIPresShell* shell = PresContext()->PresShell(); |
|
5041 if (shell) { |
|
5042 shell->AddInvalidateHiddenPresShellObserver(pres->RefreshDriver()); |
|
5043 } |
|
5044 } |
|
5045 |
|
5046 Layer* |
|
5047 nsIFrame::InvalidateLayer(uint32_t aDisplayItemKey, |
|
5048 const nsIntRect* aDamageRect, |
|
5049 uint32_t aFlags /* = 0 */) |
|
5050 { |
|
5051 NS_ASSERTION(aDisplayItemKey > 0, "Need a key"); |
|
5052 |
|
5053 Layer* layer = FrameLayerBuilder::GetDedicatedLayer(this, aDisplayItemKey); |
|
5054 |
|
5055 // If the layer is being updated asynchronously, and it's being forwarded |
|
5056 // to a compositor, then we don't need to invalidate. |
|
5057 if ((aFlags & UPDATE_IS_ASYNC) && layer && |
|
5058 layer->Manager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) { |
|
5059 return layer; |
|
5060 } |
|
5061 |
|
5062 if (aDamageRect && aDamageRect->IsEmpty()) { |
|
5063 return layer; |
|
5064 } |
|
5065 |
|
5066 if (!layer) { |
|
5067 // Plugins can transition from not rendering anything to rendering, |
|
5068 // and still only call this. So always invalidate, with specifying |
|
5069 // the display item type just in case. |
|
5070 // |
|
5071 // In the bug 930056, dialer app startup but not shown on the |
|
5072 // screen because sometimes we don't have any retainned data |
|
5073 // for remote type displayitem and thus Repaint event is not |
|
5074 // triggered. So, always invalidate here as well. |
|
5075 if (aDisplayItemKey == nsDisplayItem::TYPE_PLUGIN || |
|
5076 aDisplayItemKey == nsDisplayItem::TYPE_REMOTE) { |
|
5077 InvalidateFrame(); |
|
5078 } else { |
|
5079 InvalidateFrame(aDisplayItemKey); |
|
5080 } |
|
5081 return nullptr; |
|
5082 } |
|
5083 |
|
5084 if (aDamageRect) { |
|
5085 layer->AddInvalidRect(*aDamageRect); |
|
5086 } else { |
|
5087 layer->SetInvalidRectToVisibleRegion(); |
|
5088 } |
|
5089 |
|
5090 SchedulePaint(PAINT_COMPOSITE_ONLY); |
|
5091 return layer; |
|
5092 } |
|
5093 |
|
5094 static nsRect |
|
5095 ComputeEffectsRect(nsIFrame* aFrame, const nsRect& aOverflowRect, |
|
5096 const nsSize& aNewSize) |
|
5097 { |
|
5098 nsRect r = aOverflowRect; |
|
5099 |
|
5100 if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) { |
|
5101 // For SVG frames, we only need to account for filters. |
|
5102 // TODO: We could also take account of clipPath and mask to reduce the |
|
5103 // visual overflow, but that's not essential. |
|
5104 if (aFrame->StyleSVGReset()->HasFilters()) { |
|
5105 aFrame->Properties(). |
|
5106 Set(nsIFrame::PreEffectsBBoxProperty(), new nsRect(r)); |
|
5107 r = nsSVGUtils::GetPostFilterVisualOverflowRect(aFrame, aOverflowRect); |
|
5108 } |
|
5109 return r; |
|
5110 } |
|
5111 |
|
5112 // box-shadow |
|
5113 r.UnionRect(r, nsLayoutUtils::GetBoxShadowRectForFrame(aFrame, aNewSize)); |
|
5114 |
|
5115 // border-image-outset. |
|
5116 // We need to include border-image-outset because it can cause the |
|
5117 // border image to be drawn beyond the border box. |
|
5118 |
|
5119 // (1) It's important we not check whether there's a border-image |
|
5120 // since the style hint for a change in border image doesn't cause |
|
5121 // reflow, and that's probably more important than optimizing the |
|
5122 // overflow areas for the silly case of border-image-outset without |
|
5123 // border-image |
|
5124 // (2) It's important that we not check whether the border-image |
|
5125 // is actually loaded, since that would require us to reflow when |
|
5126 // the image loads. |
|
5127 const nsStyleBorder* styleBorder = aFrame->StyleBorder(); |
|
5128 nsMargin outsetMargin = styleBorder->GetImageOutset(); |
|
5129 |
|
5130 if (outsetMargin != nsMargin(0, 0, 0, 0)) { |
|
5131 nsRect outsetRect(nsPoint(0, 0), aNewSize); |
|
5132 outsetRect.Inflate(outsetMargin); |
|
5133 r.UnionRect(r, outsetRect); |
|
5134 } |
|
5135 |
|
5136 // Note that we don't remove the outlineInnerRect if a frame loses outline |
|
5137 // style. That would require an extra property lookup for every frame, |
|
5138 // or a new frame state bit to track whether a property had been stored, |
|
5139 // or something like that. It's not worth doing that here. At most it's |
|
5140 // only one heap-allocated rect per frame and it will be cleaned up when |
|
5141 // the frame dies. |
|
5142 |
|
5143 if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) { |
|
5144 aFrame->Properties(). |
|
5145 Set(nsIFrame::PreEffectsBBoxProperty(), new nsRect(r)); |
|
5146 r = nsSVGIntegrationUtils::ComputePostEffectsVisualOverflowRect(aFrame, r); |
|
5147 } |
|
5148 |
|
5149 return r; |
|
5150 } |
|
5151 |
|
5152 void |
|
5153 nsIFrame::MovePositionBy(const nsPoint& aTranslation) |
|
5154 { |
|
5155 nsPoint position = GetNormalPosition() + aTranslation; |
|
5156 |
|
5157 const nsMargin* computedOffsets = nullptr; |
|
5158 if (IsRelativelyPositioned()) { |
|
5159 computedOffsets = static_cast<nsMargin*> |
|
5160 (Properties().Get(nsIFrame::ComputedOffsetProperty())); |
|
5161 } |
|
5162 nsHTMLReflowState::ApplyRelativePositioning(this, computedOffsets ? |
|
5163 *computedOffsets : nsMargin(), |
|
5164 &position); |
|
5165 NS_ASSERTION(StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY || |
|
5166 GetPosition() + aTranslation == position, |
|
5167 "MovePositionBy should always lead to the movement " |
|
5168 "specified, unless the frame is position:sticky"); |
|
5169 SetPosition(position); |
|
5170 } |
|
5171 |
|
5172 nsPoint |
|
5173 nsIFrame::GetNormalPosition() const |
|
5174 { |
|
5175 // It might be faster to first check |
|
5176 // StyleDisplay()->IsRelativelyPositionedStyle(). |
|
5177 nsPoint* normalPosition = static_cast<nsPoint*> |
|
5178 (Properties().Get(NormalPositionProperty())); |
|
5179 if (normalPosition) { |
|
5180 return *normalPosition; |
|
5181 } |
|
5182 return GetPosition(); |
|
5183 } |
|
5184 |
|
5185 nsRect |
|
5186 nsIFrame::GetOverflowRect(nsOverflowType aType) const |
|
5187 { |
|
5188 NS_ABORT_IF_FALSE(aType == eVisualOverflow || aType == eScrollableOverflow, |
|
5189 "unexpected type"); |
|
5190 |
|
5191 // Note that in some cases the overflow area might not have been |
|
5192 // updated (yet) to reflect any outline set on the frame or the area |
|
5193 // of child frames. That's OK because any reflow that updates these |
|
5194 // areas will invalidate the appropriate area, so any (mis)uses of |
|
5195 // this method will be fixed up. |
|
5196 |
|
5197 if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) { |
|
5198 // there is an overflow rect, and it's not stored as deltas but as |
|
5199 // a separately-allocated rect |
|
5200 return static_cast<nsOverflowAreas*>(const_cast<nsIFrame*>(this)-> |
|
5201 GetOverflowAreasProperty())->Overflow(aType); |
|
5202 } |
|
5203 |
|
5204 if (aType == eVisualOverflow && |
|
5205 mOverflow.mType != NS_FRAME_OVERFLOW_NONE) { |
|
5206 return GetVisualOverflowFromDeltas(); |
|
5207 } |
|
5208 |
|
5209 return nsRect(nsPoint(0, 0), GetSize()); |
|
5210 } |
|
5211 |
|
5212 nsOverflowAreas |
|
5213 nsIFrame::GetOverflowAreas() const |
|
5214 { |
|
5215 if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) { |
|
5216 // there is an overflow rect, and it's not stored as deltas but as |
|
5217 // a separately-allocated rect |
|
5218 return *const_cast<nsIFrame*>(this)->GetOverflowAreasProperty(); |
|
5219 } |
|
5220 |
|
5221 return nsOverflowAreas(GetVisualOverflowFromDeltas(), |
|
5222 nsRect(nsPoint(0, 0), GetSize())); |
|
5223 } |
|
5224 |
|
5225 nsOverflowAreas |
|
5226 nsIFrame::GetOverflowAreasRelativeToSelf() const |
|
5227 { |
|
5228 if (IsTransformed()) { |
|
5229 nsOverflowAreas* preTransformOverflows = static_cast<nsOverflowAreas*> |
|
5230 (Properties().Get(PreTransformOverflowAreasProperty())); |
|
5231 if (preTransformOverflows) { |
|
5232 return nsOverflowAreas(preTransformOverflows->VisualOverflow(), |
|
5233 preTransformOverflows->ScrollableOverflow()); |
|
5234 } |
|
5235 } |
|
5236 return nsOverflowAreas(GetVisualOverflowRect(), |
|
5237 GetScrollableOverflowRect()); |
|
5238 } |
|
5239 |
|
5240 nsRect |
|
5241 nsIFrame::GetScrollableOverflowRectRelativeToParent() const |
|
5242 { |
|
5243 return GetScrollableOverflowRect() + mRect.TopLeft(); |
|
5244 } |
|
5245 |
|
5246 nsRect |
|
5247 nsIFrame::GetScrollableOverflowRectRelativeToSelf() const |
|
5248 { |
|
5249 if (IsTransformed()) { |
|
5250 nsOverflowAreas* preTransformOverflows = static_cast<nsOverflowAreas*> |
|
5251 (Properties().Get(PreTransformOverflowAreasProperty())); |
|
5252 if (preTransformOverflows) |
|
5253 return preTransformOverflows->ScrollableOverflow(); |
|
5254 } |
|
5255 return GetScrollableOverflowRect(); |
|
5256 } |
|
5257 |
|
5258 nsRect |
|
5259 nsIFrame::GetVisualOverflowRectRelativeToSelf() const |
|
5260 { |
|
5261 if (IsTransformed()) { |
|
5262 nsOverflowAreas* preTransformOverflows = static_cast<nsOverflowAreas*> |
|
5263 (Properties().Get(PreTransformOverflowAreasProperty())); |
|
5264 if (preTransformOverflows) |
|
5265 return preTransformOverflows->VisualOverflow(); |
|
5266 } |
|
5267 return GetVisualOverflowRect(); |
|
5268 } |
|
5269 |
|
5270 nsRect |
|
5271 nsIFrame::GetPreEffectsVisualOverflowRect() const |
|
5272 { |
|
5273 nsRect* r = static_cast<nsRect*> |
|
5274 (Properties().Get(nsIFrame::PreEffectsBBoxProperty())); |
|
5275 return r ? *r : GetVisualOverflowRectRelativeToSelf(); |
|
5276 } |
|
5277 |
|
5278 inline static bool |
|
5279 FrameMaintainsOverflow(nsIFrame* aFrame) |
|
5280 { |
|
5281 return (aFrame->GetStateBits() & |
|
5282 (NS_FRAME_SVG_LAYOUT | NS_FRAME_IS_NONDISPLAY)) != |
|
5283 (NS_FRAME_SVG_LAYOUT | NS_FRAME_IS_NONDISPLAY); |
|
5284 } |
|
5285 |
|
5286 /* virtual */ bool |
|
5287 nsFrame::UpdateOverflow() |
|
5288 { |
|
5289 MOZ_ASSERT(FrameMaintainsOverflow(this), |
|
5290 "Non-display SVG do not maintain visual overflow rects"); |
|
5291 |
|
5292 nsRect rect(nsPoint(0, 0), GetSize()); |
|
5293 nsOverflowAreas overflowAreas(rect, rect); |
|
5294 |
|
5295 if (!DoesClipChildren() && |
|
5296 !(IsCollapsed() && (IsBoxFrame() || IsBoxWrapped()))) { |
|
5297 nsLayoutUtils::UnionChildOverflow(this, overflowAreas); |
|
5298 } |
|
5299 |
|
5300 if (FinishAndStoreOverflow(overflowAreas, GetSize())) { |
|
5301 nsView* view = GetView(); |
|
5302 if (view) { |
|
5303 uint32_t flags = 0; |
|
5304 GetLayoutFlags(flags); |
|
5305 |
|
5306 if ((flags & NS_FRAME_NO_SIZE_VIEW) == 0) { |
|
5307 // Make sure the frame's view is properly sized. |
|
5308 nsViewManager* vm = view->GetViewManager(); |
|
5309 vm->ResizeView(view, overflowAreas.VisualOverflow(), true); |
|
5310 } |
|
5311 } |
|
5312 |
|
5313 return true; |
|
5314 } |
|
5315 |
|
5316 return false; |
|
5317 } |
|
5318 |
|
5319 // Define the MAX_FRAME_DEPTH to be the ContentSink's MAX_REFLOW_DEPTH plus |
|
5320 // 4 for the frames above the document's frames: |
|
5321 // the Viewport, GFXScroll, ScrollPort, and Canvas |
|
5322 #define MAX_FRAME_DEPTH (MAX_REFLOW_DEPTH+4) |
|
5323 |
|
5324 bool |
|
5325 nsFrame::IsFrameTreeTooDeep(const nsHTMLReflowState& aReflowState, |
|
5326 nsHTMLReflowMetrics& aMetrics, |
|
5327 nsReflowStatus& aStatus) |
|
5328 { |
|
5329 if (aReflowState.mReflowDepth > MAX_FRAME_DEPTH) { |
|
5330 NS_WARNING("frame tree too deep; setting zero size and returning"); |
|
5331 mState |= NS_FRAME_TOO_DEEP_IN_FRAME_TREE; |
|
5332 ClearOverflowRects(); |
|
5333 aMetrics.Width() = 0; |
|
5334 aMetrics.Height() = 0; |
|
5335 aMetrics.SetTopAscent(0); |
|
5336 aMetrics.mCarriedOutBottomMargin.Zero(); |
|
5337 aMetrics.mOverflowAreas.Clear(); |
|
5338 |
|
5339 if (GetNextInFlow()) { |
|
5340 // Reflow depth might vary between reflows, so we might have |
|
5341 // successfully reflowed and split this frame before. If so, we |
|
5342 // shouldn't delete its continuations. |
|
5343 aStatus = NS_FRAME_NOT_COMPLETE; |
|
5344 } else { |
|
5345 aStatus = NS_FRAME_COMPLETE; |
|
5346 } |
|
5347 |
|
5348 return true; |
|
5349 } |
|
5350 mState &= ~NS_FRAME_TOO_DEEP_IN_FRAME_TREE; |
|
5351 return false; |
|
5352 } |
|
5353 |
|
5354 bool |
|
5355 nsIFrame::IsBlockWrapper() const |
|
5356 { |
|
5357 nsIAtom *pseudoType = StyleContext()->GetPseudo(); |
|
5358 return (pseudoType == nsCSSAnonBoxes::mozAnonymousBlock || |
|
5359 pseudoType == nsCSSAnonBoxes::mozAnonymousPositionedBlock || |
|
5360 pseudoType == nsCSSAnonBoxes::buttonContent || |
|
5361 pseudoType == nsCSSAnonBoxes::cellContent); |
|
5362 } |
|
5363 |
|
5364 static nsIFrame* |
|
5365 GetNearestBlockContainer(nsIFrame* frame) |
|
5366 { |
|
5367 // The block wrappers we use to wrap blocks inside inlines aren't |
|
5368 // described in the CSS spec. We need to make them not be containing |
|
5369 // blocks. |
|
5370 // Since the parent of such a block is either a normal block or |
|
5371 // another such pseudo, this shouldn't cause anything bad to happen. |
|
5372 // Also the anonymous blocks inside table cells are not containing blocks. |
|
5373 while (frame->IsFrameOfType(nsIFrame::eLineParticipant) || |
|
5374 frame->IsBlockWrapper() || |
|
5375 // Table rows are not containing blocks either |
|
5376 frame->GetType() == nsGkAtoms::tableRowFrame) { |
|
5377 frame = frame->GetParent(); |
|
5378 NS_ASSERTION(frame, "How come we got to the root frame without seeing a containing block?"); |
|
5379 } |
|
5380 return frame; |
|
5381 } |
|
5382 |
|
5383 nsIFrame* |
|
5384 nsIFrame::GetContainingBlock() const |
|
5385 { |
|
5386 // MathML frames might have absolute positioning style, but they would |
|
5387 // still be in-flow. So we have to check to make sure that the frame |
|
5388 // is really out-of-flow too. |
|
5389 if (IsAbsolutelyPositioned() && |
|
5390 (GetStateBits() & NS_FRAME_OUT_OF_FLOW)) { |
|
5391 return GetParent(); // the parent is always the containing block |
|
5392 } |
|
5393 return GetNearestBlockContainer(GetParent()); |
|
5394 } |
|
5395 |
|
5396 #ifdef DEBUG_FRAME_DUMP |
|
5397 |
|
5398 int32_t nsFrame::ContentIndexInContainer(const nsIFrame* aFrame) |
|
5399 { |
|
5400 int32_t result = -1; |
|
5401 |
|
5402 nsIContent* content = aFrame->GetContent(); |
|
5403 if (content) { |
|
5404 nsIContent* parentContent = content->GetParent(); |
|
5405 if (parentContent) { |
|
5406 result = parentContent->IndexOf(content); |
|
5407 } |
|
5408 } |
|
5409 |
|
5410 return result; |
|
5411 } |
|
5412 |
|
5413 /** |
|
5414 * List a frame tree to stderr. Meant to be called from gdb. |
|
5415 */ |
|
5416 void |
|
5417 DebugListFrameTree(nsIFrame* aFrame) |
|
5418 { |
|
5419 ((nsFrame*)aFrame)->List(stderr); |
|
5420 } |
|
5421 |
|
5422 void |
|
5423 nsIFrame::ListTag(nsACString& aTo) const |
|
5424 { |
|
5425 ListTag(aTo, this); |
|
5426 } |
|
5427 |
|
5428 /* static */ |
|
5429 void |
|
5430 nsIFrame::ListTag(nsACString& aTo, const nsIFrame* aFrame) { |
|
5431 nsAutoString tmp; |
|
5432 aFrame->GetFrameName(tmp); |
|
5433 aTo += NS_ConvertUTF16toUTF8(tmp).get(); |
|
5434 aTo += nsPrintfCString("@%p", static_cast<const void*>(aFrame)); |
|
5435 } |
|
5436 |
|
5437 // Debugging |
|
5438 void |
|
5439 nsIFrame::ListGeneric(nsACString& aTo, const char* aPrefix, uint32_t aFlags) const |
|
5440 { |
|
5441 aTo =+ aPrefix; |
|
5442 ListTag(aTo); |
|
5443 if (HasView()) { |
|
5444 aTo += nsPrintfCString(" [view=%p]", static_cast<void*>(GetView())); |
|
5445 } |
|
5446 if (GetNextSibling()) { |
|
5447 aTo += nsPrintfCString(" next=%p", static_cast<void*>(GetNextSibling())); |
|
5448 } |
|
5449 if (GetPrevContinuation()) { |
|
5450 bool fluid = GetPrevInFlow() == GetPrevContinuation(); |
|
5451 aTo += nsPrintfCString(" prev-%s=%p", fluid?"in-flow":"continuation", |
|
5452 static_cast<void*>(GetPrevContinuation())); |
|
5453 } |
|
5454 if (GetNextContinuation()) { |
|
5455 bool fluid = GetNextInFlow() == GetNextContinuation(); |
|
5456 aTo += nsPrintfCString(" next-%s=%p", fluid?"in-flow":"continuation", |
|
5457 static_cast<void*>(GetNextContinuation())); |
|
5458 } |
|
5459 void* IBsibling = Properties().Get(IBSplitSibling()); |
|
5460 if (IBsibling) { |
|
5461 aTo += nsPrintfCString(" IBSplitSibling=%p", IBsibling); |
|
5462 } |
|
5463 void* IBprevsibling = Properties().Get(IBSplitPrevSibling()); |
|
5464 if (IBprevsibling) { |
|
5465 aTo += nsPrintfCString(" IBSplitPrevSibling=%p", IBprevsibling); |
|
5466 } |
|
5467 aTo += nsPrintfCString(" {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height); |
|
5468 nsIFrame* f = const_cast<nsIFrame*>(this); |
|
5469 if (f->HasOverflowAreas()) { |
|
5470 nsRect vo = f->GetVisualOverflowRect(); |
|
5471 if (!vo.IsEqualEdges(mRect)) { |
|
5472 aTo += nsPrintfCString(" vis-overflow=%d,%d,%d,%d", vo.x, vo.y, vo.width, vo.height); |
|
5473 } |
|
5474 nsRect so = f->GetScrollableOverflowRect(); |
|
5475 if (!so.IsEqualEdges(mRect)) { |
|
5476 aTo += nsPrintfCString(" scr-overflow=%d,%d,%d,%d", so.x, so.y, so.width, so.height); |
|
5477 } |
|
5478 } |
|
5479 if (0 != mState) { |
|
5480 aTo += nsPrintfCString(" [state=%016llx]", (unsigned long long)mState); |
|
5481 } |
|
5482 if (IsTransformed()) { |
|
5483 aTo += nsPrintfCString(" transformed"); |
|
5484 } |
|
5485 if (ChildrenHavePerspective()) { |
|
5486 aTo += nsPrintfCString(" perspective"); |
|
5487 } |
|
5488 if (Preserves3DChildren()) { |
|
5489 aTo += nsPrintfCString(" preserves-3d-children"); |
|
5490 } |
|
5491 if (Preserves3D()) { |
|
5492 aTo += nsPrintfCString(" preserves-3d"); |
|
5493 } |
|
5494 if (mContent) { |
|
5495 aTo += nsPrintfCString(" [content=%p]", static_cast<void*>(mContent)); |
|
5496 } |
|
5497 aTo += nsPrintfCString(" [sc=%p", static_cast<void*>(mStyleContext)); |
|
5498 if (mStyleContext) { |
|
5499 nsIAtom* pseudoTag = mStyleContext->GetPseudo(); |
|
5500 if (pseudoTag) { |
|
5501 nsAutoString atomString; |
|
5502 pseudoTag->ToString(atomString); |
|
5503 aTo += nsPrintfCString("%s", NS_LossyConvertUTF16toASCII(atomString).get()); |
|
5504 } |
|
5505 if (mParent && mStyleContext->GetParent() != mParent->StyleContext()) { |
|
5506 aTo += nsPrintfCString(",parent=%p", mStyleContext->GetParent()); |
|
5507 } |
|
5508 } |
|
5509 aTo += "]"; |
|
5510 } |
|
5511 |
|
5512 void |
|
5513 nsIFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const |
|
5514 { |
|
5515 nsCString str; |
|
5516 ListGeneric(str, aPrefix, aFlags); |
|
5517 fprintf_stderr(out, "%s\n", str.get()); |
|
5518 } |
|
5519 |
|
5520 nsresult |
|
5521 nsFrame::GetFrameName(nsAString& aResult) const |
|
5522 { |
|
5523 return MakeFrameName(NS_LITERAL_STRING("Frame"), aResult); |
|
5524 } |
|
5525 |
|
5526 nsresult |
|
5527 nsFrame::MakeFrameName(const nsAString& aType, nsAString& aResult) const |
|
5528 { |
|
5529 aResult = aType; |
|
5530 if (mContent && !mContent->IsNodeOfType(nsINode::eTEXT)) { |
|
5531 nsAutoString buf; |
|
5532 mContent->Tag()->ToString(buf); |
|
5533 if (GetType() == nsGkAtoms::subDocumentFrame) { |
|
5534 nsAutoString src; |
|
5535 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src); |
|
5536 buf.Append(NS_LITERAL_STRING(" src=") + src); |
|
5537 } |
|
5538 aResult.Append(NS_LITERAL_STRING("(") + buf + NS_LITERAL_STRING(")")); |
|
5539 } |
|
5540 char buf[40]; |
|
5541 PR_snprintf(buf, sizeof(buf), "(%d)", ContentIndexInContainer(this)); |
|
5542 AppendASCIItoUTF16(buf, aResult); |
|
5543 return NS_OK; |
|
5544 } |
|
5545 |
|
5546 void |
|
5547 nsIFrame::DumpFrameTree() |
|
5548 { |
|
5549 RootFrameList(PresContext(), stderr); |
|
5550 } |
|
5551 |
|
5552 void |
|
5553 nsIFrame::DumpFrameTreeLimited() |
|
5554 { |
|
5555 List(stderr); |
|
5556 } |
|
5557 |
|
5558 void |
|
5559 nsIFrame::RootFrameList(nsPresContext* aPresContext, FILE* out, const char* aPrefix) |
|
5560 { |
|
5561 if (!aPresContext || !out) |
|
5562 return; |
|
5563 |
|
5564 nsIPresShell *shell = aPresContext->GetPresShell(); |
|
5565 if (shell) { |
|
5566 nsIFrame* frame = shell->FrameManager()->GetRootFrame(); |
|
5567 if(frame) { |
|
5568 frame->List(out, aPrefix); |
|
5569 } |
|
5570 } |
|
5571 } |
|
5572 #endif |
|
5573 |
|
5574 #ifdef DEBUG |
|
5575 nsFrameState |
|
5576 nsFrame::GetDebugStateBits() const |
|
5577 { |
|
5578 // We'll ignore these flags for the purposes of comparing frame state: |
|
5579 // |
|
5580 // NS_FRAME_EXTERNAL_REFERENCE |
|
5581 // because this is set by the event state manager or the |
|
5582 // caret code when a frame is focused. Depending on whether |
|
5583 // or not the regression tests are run as the focused window |
|
5584 // will make this value vary randomly. |
|
5585 #define IRRELEVANT_FRAME_STATE_FLAGS NS_FRAME_EXTERNAL_REFERENCE |
|
5586 |
|
5587 #define FRAME_STATE_MASK (~(IRRELEVANT_FRAME_STATE_FLAGS)) |
|
5588 |
|
5589 return GetStateBits() & FRAME_STATE_MASK; |
|
5590 } |
|
5591 |
|
5592 void |
|
5593 nsFrame::XMLQuote(nsString& aString) |
|
5594 { |
|
5595 int32_t i, len = aString.Length(); |
|
5596 for (i = 0; i < len; i++) { |
|
5597 char16_t ch = aString.CharAt(i); |
|
5598 if (ch == '<') { |
|
5599 nsAutoString tmp(NS_LITERAL_STRING("<")); |
|
5600 aString.Cut(i, 1); |
|
5601 aString.Insert(tmp, i); |
|
5602 len += 3; |
|
5603 i += 3; |
|
5604 } |
|
5605 else if (ch == '>') { |
|
5606 nsAutoString tmp(NS_LITERAL_STRING(">")); |
|
5607 aString.Cut(i, 1); |
|
5608 aString.Insert(tmp, i); |
|
5609 len += 3; |
|
5610 i += 3; |
|
5611 } |
|
5612 else if (ch == '\"') { |
|
5613 nsAutoString tmp(NS_LITERAL_STRING(""")); |
|
5614 aString.Cut(i, 1); |
|
5615 aString.Insert(tmp, i); |
|
5616 len += 5; |
|
5617 i += 5; |
|
5618 } |
|
5619 } |
|
5620 } |
|
5621 #endif |
|
5622 |
|
5623 bool |
|
5624 nsIFrame::IsVisibleForPainting(nsDisplayListBuilder* aBuilder) { |
|
5625 if (!StyleVisibility()->IsVisible()) |
|
5626 return false; |
|
5627 nsISelection* sel = aBuilder->GetBoundingSelection(); |
|
5628 return !sel || IsVisibleInSelection(sel); |
|
5629 } |
|
5630 |
|
5631 bool |
|
5632 nsIFrame::IsVisibleForPainting() { |
|
5633 if (!StyleVisibility()->IsVisible()) |
|
5634 return false; |
|
5635 |
|
5636 nsPresContext* pc = PresContext(); |
|
5637 if (!pc->IsRenderingOnlySelection()) |
|
5638 return true; |
|
5639 |
|
5640 nsCOMPtr<nsISelectionController> selcon(do_QueryInterface(pc->PresShell())); |
|
5641 if (selcon) { |
|
5642 nsCOMPtr<nsISelection> sel; |
|
5643 selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, |
|
5644 getter_AddRefs(sel)); |
|
5645 if (sel) |
|
5646 return IsVisibleInSelection(sel); |
|
5647 } |
|
5648 return true; |
|
5649 } |
|
5650 |
|
5651 bool |
|
5652 nsIFrame::IsVisibleInSelection(nsDisplayListBuilder* aBuilder) { |
|
5653 nsISelection* sel = aBuilder->GetBoundingSelection(); |
|
5654 return !sel || IsVisibleInSelection(sel); |
|
5655 } |
|
5656 |
|
5657 bool |
|
5658 nsIFrame::IsVisibleOrCollapsedForPainting(nsDisplayListBuilder* aBuilder) { |
|
5659 if (!StyleVisibility()->IsVisibleOrCollapsed()) |
|
5660 return false; |
|
5661 nsISelection* sel = aBuilder->GetBoundingSelection(); |
|
5662 return !sel || IsVisibleInSelection(sel); |
|
5663 } |
|
5664 |
|
5665 bool |
|
5666 nsIFrame::IsVisibleInSelection(nsISelection* aSelection) |
|
5667 { |
|
5668 if (!GetContent() || !GetContent()->IsSelectionDescendant()) { |
|
5669 return false; |
|
5670 } |
|
5671 |
|
5672 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mContent)); |
|
5673 bool vis; |
|
5674 nsresult rv = aSelection->ContainsNode(node, true, &vis); |
|
5675 return NS_FAILED(rv) || vis; |
|
5676 } |
|
5677 |
|
5678 /* virtual */ bool |
|
5679 nsFrame::IsEmpty() |
|
5680 { |
|
5681 return false; |
|
5682 } |
|
5683 |
|
5684 bool |
|
5685 nsIFrame::CachedIsEmpty() |
|
5686 { |
|
5687 NS_PRECONDITION(!(GetStateBits() & NS_FRAME_IS_DIRTY), |
|
5688 "Must only be called on reflowed lines"); |
|
5689 return IsEmpty(); |
|
5690 } |
|
5691 |
|
5692 /* virtual */ bool |
|
5693 nsFrame::IsSelfEmpty() |
|
5694 { |
|
5695 return false; |
|
5696 } |
|
5697 |
|
5698 nsresult |
|
5699 nsFrame::GetSelectionController(nsPresContext *aPresContext, nsISelectionController **aSelCon) |
|
5700 { |
|
5701 if (!aPresContext || !aSelCon) |
|
5702 return NS_ERROR_INVALID_ARG; |
|
5703 |
|
5704 nsIFrame *frame = this; |
|
5705 while (frame && (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION)) { |
|
5706 nsITextControlFrame *tcf = do_QueryFrame(frame); |
|
5707 if (tcf) { |
|
5708 return tcf->GetOwnedSelectionController(aSelCon); |
|
5709 } |
|
5710 frame = frame->GetParent(); |
|
5711 } |
|
5712 |
|
5713 return CallQueryInterface(aPresContext->GetPresShell(), aSelCon); |
|
5714 } |
|
5715 |
|
5716 already_AddRefed<nsFrameSelection> |
|
5717 nsIFrame::GetFrameSelection() |
|
5718 { |
|
5719 nsRefPtr<nsFrameSelection> fs = |
|
5720 const_cast<nsFrameSelection*>(GetConstFrameSelection()); |
|
5721 return fs.forget(); |
|
5722 } |
|
5723 |
|
5724 const nsFrameSelection* |
|
5725 nsIFrame::GetConstFrameSelection() const |
|
5726 { |
|
5727 nsIFrame* frame = const_cast<nsIFrame*>(this); |
|
5728 while (frame && (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION)) { |
|
5729 nsITextControlFrame* tcf = do_QueryFrame(frame); |
|
5730 if (tcf) { |
|
5731 return tcf->GetOwnedFrameSelection(); |
|
5732 } |
|
5733 frame = frame->GetParent(); |
|
5734 } |
|
5735 |
|
5736 return PresContext()->PresShell()->ConstFrameSelection(); |
|
5737 } |
|
5738 |
|
5739 #ifdef DEBUG |
|
5740 nsresult |
|
5741 nsFrame::DumpRegressionData(nsPresContext* aPresContext, FILE* out, int32_t aIndent) |
|
5742 { |
|
5743 IndentBy(out, aIndent); |
|
5744 fprintf(out, "<frame va=\"%p\" type=\"", (void*)this); |
|
5745 nsAutoString name; |
|
5746 GetFrameName(name); |
|
5747 XMLQuote(name); |
|
5748 fputs(NS_LossyConvertUTF16toASCII(name).get(), out); |
|
5749 fprintf(out, "\" state=\"%016llx\" parent=\"%p\">\n", |
|
5750 (unsigned long long)GetDebugStateBits(), (void*)mParent); |
|
5751 |
|
5752 aIndent++; |
|
5753 DumpBaseRegressionData(aPresContext, out, aIndent); |
|
5754 aIndent--; |
|
5755 |
|
5756 IndentBy(out, aIndent); |
|
5757 fprintf(out, "</frame>\n"); |
|
5758 |
|
5759 return NS_OK; |
|
5760 } |
|
5761 |
|
5762 void |
|
5763 nsFrame::DumpBaseRegressionData(nsPresContext* aPresContext, FILE* out, int32_t aIndent) |
|
5764 { |
|
5765 if (GetNextSibling()) { |
|
5766 IndentBy(out, aIndent); |
|
5767 fprintf(out, "<next-sibling va=\"%p\"/>\n", (void*)GetNextSibling()); |
|
5768 } |
|
5769 |
|
5770 if (HasView()) { |
|
5771 IndentBy(out, aIndent); |
|
5772 fprintf(out, "<view va=\"%p\">\n", (void*)GetView()); |
|
5773 aIndent++; |
|
5774 // XXX add in code to dump out view state too... |
|
5775 aIndent--; |
|
5776 IndentBy(out, aIndent); |
|
5777 fprintf(out, "</view>\n"); |
|
5778 } |
|
5779 |
|
5780 IndentBy(out, aIndent); |
|
5781 fprintf(out, "<bbox x=\"%d\" y=\"%d\" w=\"%d\" h=\"%d\"/>\n", |
|
5782 mRect.x, mRect.y, mRect.width, mRect.height); |
|
5783 |
|
5784 // Now dump all of the children on all of the child lists |
|
5785 ChildListIterator lists(this); |
|
5786 for (; !lists.IsDone(); lists.Next()) { |
|
5787 IndentBy(out, aIndent); |
|
5788 if (lists.CurrentID() != kPrincipalList) { |
|
5789 fprintf(out, "<child-list name=\"%s\">\n", mozilla::layout::ChildListName(lists.CurrentID())); |
|
5790 } |
|
5791 else { |
|
5792 fprintf(out, "<child-list>\n"); |
|
5793 } |
|
5794 aIndent++; |
|
5795 nsFrameList::Enumerator childFrames(lists.CurrentList()); |
|
5796 for (; !childFrames.AtEnd(); childFrames.Next()) { |
|
5797 nsIFrame* kid = childFrames.get(); |
|
5798 kid->DumpRegressionData(aPresContext, out, aIndent); |
|
5799 } |
|
5800 aIndent--; |
|
5801 IndentBy(out, aIndent); |
|
5802 fprintf(out, "</child-list>\n"); |
|
5803 } |
|
5804 } |
|
5805 #endif |
|
5806 |
|
5807 bool |
|
5808 nsIFrame::IsFrameSelected() const |
|
5809 { |
|
5810 NS_ASSERTION(!GetContent() || GetContent()->IsSelectionDescendant(), |
|
5811 "use the public IsSelected() instead"); |
|
5812 return nsRange::IsNodeSelected(GetContent(), 0, |
|
5813 GetContent()->GetChildCount()); |
|
5814 } |
|
5815 |
|
5816 nsresult |
|
5817 nsFrame::GetPointFromOffset(int32_t inOffset, nsPoint* outPoint) |
|
5818 { |
|
5819 NS_PRECONDITION(outPoint != nullptr, "Null parameter"); |
|
5820 nsRect contentRect = GetContentRect() - GetPosition(); |
|
5821 nsPoint pt = contentRect.TopLeft(); |
|
5822 if (mContent) |
|
5823 { |
|
5824 nsIContent* newContent = mContent->GetParent(); |
|
5825 if (newContent){ |
|
5826 int32_t newOffset = newContent->IndexOf(mContent); |
|
5827 |
|
5828 bool isRTL = (NS_GET_EMBEDDING_LEVEL(this) & 1) == 1; |
|
5829 if ((!isRTL && inOffset > newOffset) || |
|
5830 (isRTL && inOffset <= newOffset)) { |
|
5831 pt = contentRect.TopRight(); |
|
5832 } |
|
5833 } |
|
5834 } |
|
5835 *outPoint = pt; |
|
5836 return NS_OK; |
|
5837 } |
|
5838 |
|
5839 nsresult |
|
5840 nsFrame::GetChildFrameContainingOffset(int32_t inContentOffset, bool inHint, int32_t* outFrameContentOffset, nsIFrame **outChildFrame) |
|
5841 { |
|
5842 NS_PRECONDITION(outChildFrame && outFrameContentOffset, "Null parameter"); |
|
5843 *outFrameContentOffset = (int32_t)inHint; |
|
5844 //the best frame to reflect any given offset would be a visible frame if possible |
|
5845 //i.e. we are looking for a valid frame to place the blinking caret |
|
5846 nsRect rect = GetRect(); |
|
5847 if (!rect.width || !rect.height) |
|
5848 { |
|
5849 //if we have a 0 width or height then lets look for another frame that possibly has |
|
5850 //the same content. If we have no frames in flow then just let us return 'this' frame |
|
5851 nsIFrame* nextFlow = GetNextInFlow(); |
|
5852 if (nextFlow) |
|
5853 return nextFlow->GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame); |
|
5854 } |
|
5855 *outChildFrame = this; |
|
5856 return NS_OK; |
|
5857 } |
|
5858 |
|
5859 // |
|
5860 // What I've pieced together about this routine: |
|
5861 // Starting with a block frame (from which a line frame can be gotten) |
|
5862 // and a line number, drill down and get the first/last selectable |
|
5863 // frame on that line, depending on aPos->mDirection. |
|
5864 // aOutSideLimit != 0 means ignore aLineStart, instead work from |
|
5865 // the end (if > 0) or beginning (if < 0). |
|
5866 // |
|
5867 nsresult |
|
5868 nsFrame::GetNextPrevLineFromeBlockFrame(nsPresContext* aPresContext, |
|
5869 nsPeekOffsetStruct *aPos, |
|
5870 nsIFrame *aBlockFrame, |
|
5871 int32_t aLineStart, |
|
5872 int8_t aOutSideLimit |
|
5873 ) |
|
5874 { |
|
5875 //magic numbers aLineStart will be -1 for end of block 0 will be start of block |
|
5876 if (!aBlockFrame || !aPos) |
|
5877 return NS_ERROR_NULL_POINTER; |
|
5878 |
|
5879 aPos->mResultFrame = nullptr; |
|
5880 aPos->mResultContent = nullptr; |
|
5881 aPos->mAttachForward = (aPos->mDirection == eDirNext); |
|
5882 |
|
5883 nsAutoLineIterator it = aBlockFrame->GetLineIterator(); |
|
5884 if (!it) |
|
5885 return NS_ERROR_FAILURE; |
|
5886 int32_t searchingLine = aLineStart; |
|
5887 int32_t countLines = it->GetNumLines(); |
|
5888 if (aOutSideLimit > 0) //start at end |
|
5889 searchingLine = countLines; |
|
5890 else if (aOutSideLimit <0)//start at beginning |
|
5891 searchingLine = -1;//"next" will be 0 |
|
5892 else |
|
5893 if ((aPos->mDirection == eDirPrevious && searchingLine == 0) || |
|
5894 (aPos->mDirection == eDirNext && searchingLine >= (countLines -1) )){ |
|
5895 //we need to jump to new block frame. |
|
5896 return NS_ERROR_FAILURE; |
|
5897 } |
|
5898 int32_t lineFrameCount; |
|
5899 nsIFrame *resultFrame = nullptr; |
|
5900 nsIFrame *farStoppingFrame = nullptr; //we keep searching until we find a "this" frame then we go to next line |
|
5901 nsIFrame *nearStoppingFrame = nullptr; //if we are backing up from edge, stop here |
|
5902 nsIFrame *firstFrame; |
|
5903 nsIFrame *lastFrame; |
|
5904 nsRect rect; |
|
5905 bool isBeforeFirstFrame, isAfterLastFrame; |
|
5906 bool found = false; |
|
5907 |
|
5908 nsresult result = NS_OK; |
|
5909 while (!found) |
|
5910 { |
|
5911 if (aPos->mDirection == eDirPrevious) |
|
5912 searchingLine --; |
|
5913 else |
|
5914 searchingLine ++; |
|
5915 if ((aPos->mDirection == eDirPrevious && searchingLine < 0) || |
|
5916 (aPos->mDirection == eDirNext && searchingLine >= countLines )) |
|
5917 { |
|
5918 //we need to jump to new block frame. |
|
5919 return NS_ERROR_FAILURE; |
|
5920 } |
|
5921 uint32_t lineFlags; |
|
5922 result = it->GetLine(searchingLine, &firstFrame, &lineFrameCount, |
|
5923 rect, &lineFlags); |
|
5924 if (!lineFrameCount) |
|
5925 continue; |
|
5926 if (NS_SUCCEEDED(result)){ |
|
5927 lastFrame = firstFrame; |
|
5928 for (;lineFrameCount > 1;lineFrameCount --){ |
|
5929 //result = lastFrame->GetNextSibling(&lastFrame, searchingLine); |
|
5930 result = it->GetNextSiblingOnLine(lastFrame, searchingLine); |
|
5931 if (NS_FAILED(result) || !lastFrame){ |
|
5932 NS_ERROR("GetLine promised more frames than could be found"); |
|
5933 return NS_ERROR_FAILURE; |
|
5934 } |
|
5935 } |
|
5936 GetLastLeaf(aPresContext, &lastFrame); |
|
5937 |
|
5938 if (aPos->mDirection == eDirNext){ |
|
5939 nearStoppingFrame = firstFrame; |
|
5940 farStoppingFrame = lastFrame; |
|
5941 } |
|
5942 else{ |
|
5943 nearStoppingFrame = lastFrame; |
|
5944 farStoppingFrame = firstFrame; |
|
5945 } |
|
5946 nsPoint offset; |
|
5947 nsView * view; //used for call of get offset from view |
|
5948 aBlockFrame->GetOffsetFromView(offset,&view); |
|
5949 nscoord newDesiredX = aPos->mDesiredX - offset.x;//get desired x into blockframe coordinates! |
|
5950 result = it->FindFrameAt(searchingLine, newDesiredX, &resultFrame, &isBeforeFirstFrame, &isAfterLastFrame); |
|
5951 if(NS_FAILED(result)) |
|
5952 continue; |
|
5953 } |
|
5954 |
|
5955 if (NS_SUCCEEDED(result) && resultFrame) |
|
5956 { |
|
5957 //check to see if this is ANOTHER blockframe inside the other one if so then call into its lines |
|
5958 nsAutoLineIterator newIt = resultFrame->GetLineIterator(); |
|
5959 if (newIt) |
|
5960 { |
|
5961 aPos->mResultFrame = resultFrame; |
|
5962 return NS_OK; |
|
5963 } |
|
5964 //resultFrame is not a block frame |
|
5965 result = NS_ERROR_FAILURE; |
|
5966 |
|
5967 nsCOMPtr<nsIFrameEnumerator> frameTraversal; |
|
5968 result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal), |
|
5969 aPresContext, resultFrame, |
|
5970 ePostOrder, |
|
5971 false, // aVisual |
|
5972 aPos->mScrollViewStop, |
|
5973 false // aFollowOOFs |
|
5974 ); |
|
5975 if (NS_FAILED(result)) |
|
5976 return result; |
|
5977 |
|
5978 nsIFrame *storeOldResultFrame = resultFrame; |
|
5979 while ( !found ){ |
|
5980 nsPoint point; |
|
5981 point.x = aPos->mDesiredX; |
|
5982 |
|
5983 nsRect tempRect = resultFrame->GetRect(); |
|
5984 nsPoint offset; |
|
5985 nsView * view; //used for call of get offset from view |
|
5986 resultFrame->GetOffsetFromView(offset, &view); |
|
5987 if (!view) { |
|
5988 return NS_ERROR_FAILURE; |
|
5989 } |
|
5990 point.y = tempRect.height + offset.y; |
|
5991 |
|
5992 //special check. if we allow non-text selection then we can allow a hit location to fall before a table. |
|
5993 //otherwise there is no way to get and click signal to fall before a table (it being a line iterator itself) |
|
5994 nsIPresShell *shell = aPresContext->GetPresShell(); |
|
5995 if (!shell) |
|
5996 return NS_ERROR_FAILURE; |
|
5997 int16_t isEditor = shell->GetSelectionFlags(); |
|
5998 isEditor = isEditor == nsISelectionDisplay::DISPLAY_ALL; |
|
5999 if ( isEditor ) |
|
6000 { |
|
6001 if (resultFrame->GetType() == nsGkAtoms::tableOuterFrame) |
|
6002 { |
|
6003 if (((point.x - offset.x + tempRect.x)<0) || ((point.x - offset.x+ tempRect.x)>tempRect.width))//off left/right side |
|
6004 { |
|
6005 nsIContent* content = resultFrame->GetContent(); |
|
6006 if (content) |
|
6007 { |
|
6008 nsIContent* parent = content->GetParent(); |
|
6009 if (parent) |
|
6010 { |
|
6011 aPos->mResultContent = parent; |
|
6012 aPos->mContentOffset = parent->IndexOf(content); |
|
6013 aPos->mAttachForward = false; |
|
6014 if ((point.x - offset.x+ tempRect.x)>tempRect.width) |
|
6015 { |
|
6016 aPos->mContentOffset++;//go to end of this frame |
|
6017 aPos->mAttachForward = true; |
|
6018 } |
|
6019 //result frame is the result frames parent. |
|
6020 aPos->mResultFrame = resultFrame->GetParent(); |
|
6021 return NS_POSITION_BEFORE_TABLE; |
|
6022 } |
|
6023 } |
|
6024 } |
|
6025 } |
|
6026 } |
|
6027 |
|
6028 if (!resultFrame->HasView()) |
|
6029 { |
|
6030 nsView* view; |
|
6031 nsPoint offset; |
|
6032 resultFrame->GetOffsetFromView(offset, &view); |
|
6033 ContentOffsets offsets = |
|
6034 resultFrame->GetContentOffsetsFromPoint(point - offset); |
|
6035 aPos->mResultContent = offsets.content; |
|
6036 aPos->mContentOffset = offsets.offset; |
|
6037 aPos->mAttachForward = offsets.associateWithNext; |
|
6038 if (offsets.content) |
|
6039 { |
|
6040 bool selectable; |
|
6041 resultFrame->IsSelectable(&selectable, nullptr); |
|
6042 if (selectable) |
|
6043 { |
|
6044 found = true; |
|
6045 break; |
|
6046 } |
|
6047 } |
|
6048 } |
|
6049 |
|
6050 if (aPos->mDirection == eDirPrevious && (resultFrame == farStoppingFrame)) |
|
6051 break; |
|
6052 if (aPos->mDirection == eDirNext && (resultFrame == nearStoppingFrame)) |
|
6053 break; |
|
6054 //always try previous on THAT line if that fails go the other way |
|
6055 frameTraversal->Prev(); |
|
6056 resultFrame = frameTraversal->CurrentItem(); |
|
6057 if (!resultFrame) |
|
6058 return NS_ERROR_FAILURE; |
|
6059 } |
|
6060 |
|
6061 if (!found){ |
|
6062 resultFrame = storeOldResultFrame; |
|
6063 |
|
6064 result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal), |
|
6065 aPresContext, resultFrame, |
|
6066 eLeaf, |
|
6067 false, // aVisual |
|
6068 aPos->mScrollViewStop, |
|
6069 false // aFollowOOFs |
|
6070 ); |
|
6071 } |
|
6072 while ( !found ){ |
|
6073 nsPoint point(aPos->mDesiredX, 0); |
|
6074 nsView* view; |
|
6075 nsPoint offset; |
|
6076 resultFrame->GetOffsetFromView(offset, &view); |
|
6077 ContentOffsets offsets = |
|
6078 resultFrame->GetContentOffsetsFromPoint(point - offset); |
|
6079 aPos->mResultContent = offsets.content; |
|
6080 aPos->mContentOffset = offsets.offset; |
|
6081 aPos->mAttachForward = offsets.associateWithNext; |
|
6082 if (offsets.content) |
|
6083 { |
|
6084 bool selectable; |
|
6085 resultFrame->IsSelectable(&selectable, nullptr); |
|
6086 if (selectable) |
|
6087 { |
|
6088 found = true; |
|
6089 if (resultFrame == farStoppingFrame) |
|
6090 aPos->mAttachForward = false; |
|
6091 else |
|
6092 aPos->mAttachForward = true; |
|
6093 break; |
|
6094 } |
|
6095 } |
|
6096 if (aPos->mDirection == eDirPrevious && (resultFrame == nearStoppingFrame)) |
|
6097 break; |
|
6098 if (aPos->mDirection == eDirNext && (resultFrame == farStoppingFrame)) |
|
6099 break; |
|
6100 //previous didnt work now we try "next" |
|
6101 frameTraversal->Next(); |
|
6102 nsIFrame *tempFrame = frameTraversal->CurrentItem(); |
|
6103 if (!tempFrame) |
|
6104 break; |
|
6105 resultFrame = tempFrame; |
|
6106 } |
|
6107 aPos->mResultFrame = resultFrame; |
|
6108 } |
|
6109 else { |
|
6110 //we need to jump to new block frame. |
|
6111 aPos->mAmount = eSelectLine; |
|
6112 aPos->mStartOffset = 0; |
|
6113 aPos->mAttachForward = !(aPos->mDirection == eDirNext); |
|
6114 if (aPos->mDirection == eDirPrevious) |
|
6115 aPos->mStartOffset = -1;//start from end |
|
6116 return aBlockFrame->PeekOffset(aPos); |
|
6117 } |
|
6118 } |
|
6119 return NS_OK; |
|
6120 } |
|
6121 |
|
6122 nsIFrame::CaretPosition |
|
6123 nsIFrame::GetExtremeCaretPosition(bool aStart) |
|
6124 { |
|
6125 CaretPosition result; |
|
6126 |
|
6127 FrameTarget targetFrame = DrillDownToSelectionFrame(this, !aStart, 0); |
|
6128 FrameContentRange range = GetRangeForFrame(targetFrame.frame); |
|
6129 result.mResultContent = range.content; |
|
6130 result.mContentOffset = aStart ? range.start : range.end; |
|
6131 return result; |
|
6132 } |
|
6133 |
|
6134 // Find the first (or last) descendant of the given frame |
|
6135 // which is either a block frame or a BRFrame. |
|
6136 static nsContentAndOffset |
|
6137 FindBlockFrameOrBR(nsIFrame* aFrame, nsDirection aDirection) |
|
6138 { |
|
6139 nsContentAndOffset result; |
|
6140 result.mContent = nullptr; |
|
6141 result.mOffset = 0; |
|
6142 |
|
6143 if (aFrame->IsGeneratedContentFrame()) |
|
6144 return result; |
|
6145 |
|
6146 // Treat form controls as inline leaves |
|
6147 // XXX we really need a way to determine whether a frame is inline-level |
|
6148 nsIFormControlFrame* fcf = do_QueryFrame(aFrame); |
|
6149 if (fcf) |
|
6150 return result; |
|
6151 |
|
6152 // Check the frame itself |
|
6153 // Fall through block-in-inline split frames because their mContent is |
|
6154 // the content of the inline frames they were created from. The |
|
6155 // first/last child of such frames is the real block frame we're |
|
6156 // looking for. |
|
6157 if ((nsLayoutUtils::GetAsBlock(aFrame) && |
|
6158 !(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) || |
|
6159 aFrame->GetType() == nsGkAtoms::brFrame) { |
|
6160 nsIContent* content = aFrame->GetContent(); |
|
6161 result.mContent = content->GetParent(); |
|
6162 // In some cases (bug 310589, bug 370174) we end up here with a null content. |
|
6163 // This probably shouldn't ever happen, but since it sometimes does, we want |
|
6164 // to avoid crashing here. |
|
6165 NS_ASSERTION(result.mContent, "Unexpected orphan content"); |
|
6166 if (result.mContent) |
|
6167 result.mOffset = result.mContent->IndexOf(content) + |
|
6168 (aDirection == eDirPrevious ? 1 : 0); |
|
6169 return result; |
|
6170 } |
|
6171 |
|
6172 // If this is a preformatted text frame, see if it ends with a newline |
|
6173 if (aFrame->HasSignificantTerminalNewline()) { |
|
6174 int32_t startOffset, endOffset; |
|
6175 aFrame->GetOffsets(startOffset, endOffset); |
|
6176 result.mContent = aFrame->GetContent(); |
|
6177 result.mOffset = endOffset - (aDirection == eDirPrevious ? 0 : 1); |
|
6178 return result; |
|
6179 } |
|
6180 |
|
6181 // Iterate over children and call ourselves recursively |
|
6182 if (aDirection == eDirPrevious) { |
|
6183 nsIFrame* child = aFrame->GetLastChild(nsIFrame::kPrincipalList); |
|
6184 while(child && !result.mContent) { |
|
6185 result = FindBlockFrameOrBR(child, aDirection); |
|
6186 child = child->GetPrevSibling(); |
|
6187 } |
|
6188 } else { // eDirNext |
|
6189 nsIFrame* child = aFrame->GetFirstPrincipalChild(); |
|
6190 while(child && !result.mContent) { |
|
6191 result = FindBlockFrameOrBR(child, aDirection); |
|
6192 child = child->GetNextSibling(); |
|
6193 } |
|
6194 } |
|
6195 return result; |
|
6196 } |
|
6197 |
|
6198 nsresult |
|
6199 nsIFrame::PeekOffsetParagraph(nsPeekOffsetStruct *aPos) |
|
6200 { |
|
6201 nsIFrame* frame = this; |
|
6202 nsContentAndOffset blockFrameOrBR; |
|
6203 blockFrameOrBR.mContent = nullptr; |
|
6204 bool reachedBlockAncestor = false; |
|
6205 |
|
6206 // Go through containing frames until reaching a block frame. |
|
6207 // In each step, search the previous (or next) siblings for the closest |
|
6208 // "stop frame" (a block frame or a BRFrame). |
|
6209 // If found, set it to be the selection boundray and abort. |
|
6210 |
|
6211 if (aPos->mDirection == eDirPrevious) { |
|
6212 while (!reachedBlockAncestor) { |
|
6213 nsIFrame* parent = frame->GetParent(); |
|
6214 // Treat a frame associated with the root content as if it were a block frame. |
|
6215 if (!frame->mContent || !frame->mContent->GetParent()) { |
|
6216 reachedBlockAncestor = true; |
|
6217 break; |
|
6218 } |
|
6219 nsIFrame* sibling = frame->GetPrevSibling(); |
|
6220 while (sibling && !blockFrameOrBR.mContent) { |
|
6221 blockFrameOrBR = FindBlockFrameOrBR(sibling, eDirPrevious); |
|
6222 sibling = sibling->GetPrevSibling(); |
|
6223 } |
|
6224 if (blockFrameOrBR.mContent) { |
|
6225 aPos->mResultContent = blockFrameOrBR.mContent; |
|
6226 aPos->mContentOffset = blockFrameOrBR.mOffset; |
|
6227 break; |
|
6228 } |
|
6229 frame = parent; |
|
6230 reachedBlockAncestor = (nsLayoutUtils::GetAsBlock(frame) != nullptr); |
|
6231 } |
|
6232 if (reachedBlockAncestor) { // no "stop frame" found |
|
6233 aPos->mResultContent = frame->GetContent(); |
|
6234 aPos->mContentOffset = 0; |
|
6235 } |
|
6236 } else { // eDirNext |
|
6237 while (!reachedBlockAncestor) { |
|
6238 nsIFrame* parent = frame->GetParent(); |
|
6239 // Treat a frame associated with the root content as if it were a block frame. |
|
6240 if (!frame->mContent || !frame->mContent->GetParent()) { |
|
6241 reachedBlockAncestor = true; |
|
6242 break; |
|
6243 } |
|
6244 nsIFrame* sibling = frame; |
|
6245 while (sibling && !blockFrameOrBR.mContent) { |
|
6246 blockFrameOrBR = FindBlockFrameOrBR(sibling, eDirNext); |
|
6247 sibling = sibling->GetNextSibling(); |
|
6248 } |
|
6249 if (blockFrameOrBR.mContent) { |
|
6250 aPos->mResultContent = blockFrameOrBR.mContent; |
|
6251 aPos->mContentOffset = blockFrameOrBR.mOffset; |
|
6252 break; |
|
6253 } |
|
6254 frame = parent; |
|
6255 reachedBlockAncestor = (nsLayoutUtils::GetAsBlock(frame) != nullptr); |
|
6256 } |
|
6257 if (reachedBlockAncestor) { // no "stop frame" found |
|
6258 aPos->mResultContent = frame->GetContent(); |
|
6259 if (aPos->mResultContent) |
|
6260 aPos->mContentOffset = aPos->mResultContent->GetChildCount(); |
|
6261 } |
|
6262 } |
|
6263 return NS_OK; |
|
6264 } |
|
6265 |
|
6266 // Determine movement direction relative to frame |
|
6267 static bool IsMovingInFrameDirection(nsIFrame* frame, nsDirection aDirection, bool aVisual) |
|
6268 { |
|
6269 bool isReverseDirection = aVisual ? |
|
6270 (NS_GET_EMBEDDING_LEVEL(frame) & 1) != (NS_GET_BASE_LEVEL(frame) & 1) : false; |
|
6271 return aDirection == (isReverseDirection ? eDirPrevious : eDirNext); |
|
6272 } |
|
6273 |
|
6274 nsresult |
|
6275 nsIFrame::PeekOffset(nsPeekOffsetStruct* aPos) |
|
6276 { |
|
6277 if (!aPos) |
|
6278 return NS_ERROR_NULL_POINTER; |
|
6279 nsresult result = NS_ERROR_FAILURE; |
|
6280 |
|
6281 if (mState & NS_FRAME_IS_DIRTY) |
|
6282 return NS_ERROR_UNEXPECTED; |
|
6283 |
|
6284 // Translate content offset to be relative to frame |
|
6285 FrameContentRange range = GetRangeForFrame(this); |
|
6286 int32_t offset = aPos->mStartOffset - range.start; |
|
6287 nsIFrame* current = this; |
|
6288 |
|
6289 switch (aPos->mAmount) { |
|
6290 case eSelectCharacter: |
|
6291 case eSelectCluster: |
|
6292 { |
|
6293 bool eatingNonRenderableWS = false; |
|
6294 nsIFrame::FrameSearchResult peekSearchState = CONTINUE; |
|
6295 bool jumpedLine = false; |
|
6296 bool movedOverNonSelectableText = false; |
|
6297 |
|
6298 while (peekSearchState != FOUND) { |
|
6299 bool movingInFrameDirection = |
|
6300 IsMovingInFrameDirection(current, aPos->mDirection, aPos->mVisual); |
|
6301 |
|
6302 if (eatingNonRenderableWS) |
|
6303 peekSearchState = current->PeekOffsetNoAmount(movingInFrameDirection, &offset); |
|
6304 else |
|
6305 peekSearchState = current->PeekOffsetCharacter(movingInFrameDirection, &offset, |
|
6306 aPos->mAmount == eSelectCluster); |
|
6307 |
|
6308 movedOverNonSelectableText |= (peekSearchState == CONTINUE_UNSELECTABLE); |
|
6309 |
|
6310 if (peekSearchState != FOUND) { |
|
6311 result = |
|
6312 current->GetFrameFromDirection(aPos->mDirection, aPos->mVisual, |
|
6313 aPos->mJumpLines, aPos->mScrollViewStop, |
|
6314 ¤t, &offset, &jumpedLine); |
|
6315 if (NS_FAILED(result)) |
|
6316 return result; |
|
6317 |
|
6318 // If we jumped lines, it's as if we found a character, but we still need |
|
6319 // to eat non-renderable content on the new line. |
|
6320 if (jumpedLine) |
|
6321 eatingNonRenderableWS = true; |
|
6322 } |
|
6323 |
|
6324 // Found frame, but because we moved over non selectable text we want the offset |
|
6325 // to be at the frame edge. |
|
6326 if (peekSearchState == FOUND && movedOverNonSelectableText) |
|
6327 { |
|
6328 int32_t start, end; |
|
6329 current->GetOffsets(start, end); |
|
6330 offset = aPos->mDirection == eDirNext ? 0 : end - start; |
|
6331 } |
|
6332 } |
|
6333 |
|
6334 // Set outputs |
|
6335 range = GetRangeForFrame(current); |
|
6336 aPos->mResultFrame = current; |
|
6337 aPos->mResultContent = range.content; |
|
6338 // Output offset is relative to content, not frame |
|
6339 aPos->mContentOffset = offset < 0 ? range.end : range.start + offset; |
|
6340 // If we're dealing with a text frame and moving backward positions us at |
|
6341 // the end of that line, decrease the offset by one to make sure that |
|
6342 // we're placed before the linefeed character on the previous line. |
|
6343 if (offset < 0 && jumpedLine && |
|
6344 aPos->mDirection == eDirPrevious && |
|
6345 current->HasSignificantTerminalNewline()) { |
|
6346 --aPos->mContentOffset; |
|
6347 } |
|
6348 |
|
6349 break; |
|
6350 } |
|
6351 case eSelectWordNoSpace: |
|
6352 // eSelectWordNoSpace means that we should not be eating any whitespace when |
|
6353 // moving to the adjacent word. This means that we should set aPos-> |
|
6354 // mWordMovementType to eEndWord if we're moving forwards, and to eStartWord |
|
6355 // if we're moving backwards. |
|
6356 if (aPos->mDirection == eDirPrevious) { |
|
6357 aPos->mWordMovementType = eStartWord; |
|
6358 } else { |
|
6359 aPos->mWordMovementType = eEndWord; |
|
6360 } |
|
6361 // Intentionally fall through the eSelectWord case. |
|
6362 case eSelectWord: |
|
6363 { |
|
6364 // wordSelectEatSpace means "are we looking for a boundary between whitespace |
|
6365 // and non-whitespace (in the direction we're moving in)". |
|
6366 // It is true when moving forward and looking for a beginning of a word, or |
|
6367 // when moving backwards and looking for an end of a word. |
|
6368 bool wordSelectEatSpace; |
|
6369 if (aPos->mWordMovementType != eDefaultBehavior) { |
|
6370 // aPos->mWordMovementType possible values: |
|
6371 // eEndWord: eat the space if we're moving backwards |
|
6372 // eStartWord: eat the space if we're moving forwards |
|
6373 wordSelectEatSpace = ((aPos->mWordMovementType == eEndWord) == (aPos->mDirection == eDirPrevious)); |
|
6374 } |
|
6375 else { |
|
6376 // Use the hidden preference which is based on operating system behavior. |
|
6377 // This pref only affects whether moving forward by word should go to the end of this word or start of the next word. |
|
6378 // When going backwards, the start of the word is always used, on every operating system. |
|
6379 wordSelectEatSpace = aPos->mDirection == eDirNext && |
|
6380 Preferences::GetBool("layout.word_select.eat_space_to_next_word"); |
|
6381 } |
|
6382 |
|
6383 // mSawBeforeType means "we already saw characters of the type |
|
6384 // before the boundary we're looking for". Examples: |
|
6385 // 1. If we're moving forward, looking for a word beginning (i.e. a boundary |
|
6386 // between whitespace and non-whitespace), then eatingWS==true means |
|
6387 // "we already saw some whitespace". |
|
6388 // 2. If we're moving backward, looking for a word beginning (i.e. a boundary |
|
6389 // between non-whitespace and whitespace), then eatingWS==true means |
|
6390 // "we already saw some non-whitespace". |
|
6391 PeekWordState state; |
|
6392 int32_t offsetAdjustment = 0; |
|
6393 bool done = false; |
|
6394 while (!done) { |
|
6395 bool movingInFrameDirection = |
|
6396 IsMovingInFrameDirection(current, aPos->mDirection, aPos->mVisual); |
|
6397 |
|
6398 done = current->PeekOffsetWord(movingInFrameDirection, wordSelectEatSpace, |
|
6399 aPos->mIsKeyboardSelect, &offset, &state) == FOUND; |
|
6400 |
|
6401 if (!done) { |
|
6402 nsIFrame* nextFrame; |
|
6403 int32_t nextFrameOffset; |
|
6404 bool jumpedLine; |
|
6405 result = |
|
6406 current->GetFrameFromDirection(aPos->mDirection, aPos->mVisual, |
|
6407 aPos->mJumpLines, aPos->mScrollViewStop, |
|
6408 &nextFrame, &nextFrameOffset, &jumpedLine); |
|
6409 // We can't jump lines if we're looking for whitespace following |
|
6410 // non-whitespace, and we already encountered non-whitespace. |
|
6411 if (NS_FAILED(result) || |
|
6412 (jumpedLine && !wordSelectEatSpace && state.mSawBeforeType)) { |
|
6413 done = true; |
|
6414 // If we've crossed the line boundary, check to make sure that we |
|
6415 // have not consumed a trailing newline as whitesapce if it's significant. |
|
6416 if (jumpedLine && wordSelectEatSpace && |
|
6417 current->HasSignificantTerminalNewline()) { |
|
6418 offsetAdjustment = -1; |
|
6419 } |
|
6420 } else { |
|
6421 if (jumpedLine) { |
|
6422 state.mContext.Truncate(); |
|
6423 } |
|
6424 current = nextFrame; |
|
6425 offset = nextFrameOffset; |
|
6426 // Jumping a line is equivalent to encountering whitespace |
|
6427 if (wordSelectEatSpace && jumpedLine) |
|
6428 state.SetSawBeforeType(); |
|
6429 } |
|
6430 } |
|
6431 } |
|
6432 |
|
6433 // Set outputs |
|
6434 range = GetRangeForFrame(current); |
|
6435 aPos->mResultFrame = current; |
|
6436 aPos->mResultContent = range.content; |
|
6437 // Output offset is relative to content, not frame |
|
6438 aPos->mContentOffset = (offset < 0 ? range.end : range.start + offset) + offsetAdjustment; |
|
6439 break; |
|
6440 } |
|
6441 case eSelectLine : |
|
6442 { |
|
6443 nsAutoLineIterator iter; |
|
6444 nsIFrame *blockFrame = this; |
|
6445 |
|
6446 while (NS_FAILED(result)){ |
|
6447 int32_t thisLine = nsFrame::GetLineNumber(blockFrame, aPos->mScrollViewStop, &blockFrame); |
|
6448 if (thisLine < 0) |
|
6449 return NS_ERROR_FAILURE; |
|
6450 iter = blockFrame->GetLineIterator(); |
|
6451 NS_ASSERTION(iter, "GetLineNumber() succeeded but no block frame?"); |
|
6452 result = NS_OK; |
|
6453 |
|
6454 int edgeCase = 0;//no edge case. this should look at thisLine |
|
6455 |
|
6456 bool doneLooping = false;//tells us when no more block frames hit. |
|
6457 //this part will find a frame or a block frame. if it's a block frame |
|
6458 //it will "drill down" to find a viable frame or it will return an error. |
|
6459 nsIFrame *lastFrame = this; |
|
6460 do { |
|
6461 result = nsFrame::GetNextPrevLineFromeBlockFrame(PresContext(), |
|
6462 aPos, |
|
6463 blockFrame, |
|
6464 thisLine, |
|
6465 edgeCase //start from thisLine |
|
6466 ); |
|
6467 if (NS_SUCCEEDED(result) && (!aPos->mResultFrame || aPos->mResultFrame == lastFrame))//we came back to same spot! keep going |
|
6468 { |
|
6469 aPos->mResultFrame = nullptr; |
|
6470 if (aPos->mDirection == eDirPrevious) |
|
6471 thisLine--; |
|
6472 else |
|
6473 thisLine++; |
|
6474 } |
|
6475 else //if failure or success with different frame. |
|
6476 doneLooping = true; //do not continue with while loop |
|
6477 |
|
6478 lastFrame = aPos->mResultFrame; //set last frame |
|
6479 |
|
6480 if (NS_SUCCEEDED(result) && aPos->mResultFrame |
|
6481 && blockFrame != aPos->mResultFrame)// make sure block element is not the same as the one we had before |
|
6482 { |
|
6483 /* SPECIAL CHECK FOR TABLE NAVIGATION |
|
6484 tables need to navigate also and the frame that supports it is nsTableRowGroupFrame which is INSIDE |
|
6485 nsTableOuterFrame. if we have stumbled onto an nsTableOuter we need to drill into nsTableRowGroup |
|
6486 if we hit a header or footer that's ok just go into them, |
|
6487 */ |
|
6488 bool searchTableBool = false; |
|
6489 if (aPos->mResultFrame->GetType() == nsGkAtoms::tableOuterFrame || |
|
6490 aPos->mResultFrame->GetType() == nsGkAtoms::tableCellFrame) |
|
6491 { |
|
6492 nsIFrame *frame = aPos->mResultFrame->GetFirstPrincipalChild(); |
|
6493 //got the table frame now |
|
6494 while(frame) //ok time to drill down to find iterator |
|
6495 { |
|
6496 iter = frame->GetLineIterator(); |
|
6497 if (iter) |
|
6498 { |
|
6499 aPos->mResultFrame = frame; |
|
6500 searchTableBool = true; |
|
6501 result = NS_OK; |
|
6502 break; //while(frame) |
|
6503 } |
|
6504 result = NS_ERROR_FAILURE; |
|
6505 frame = frame->GetFirstPrincipalChild(); |
|
6506 } |
|
6507 } |
|
6508 |
|
6509 if (!searchTableBool) { |
|
6510 iter = aPos->mResultFrame->GetLineIterator(); |
|
6511 result = iter ? NS_OK : NS_ERROR_FAILURE; |
|
6512 } |
|
6513 if (NS_SUCCEEDED(result) && iter)//we've struck another block element! |
|
6514 { |
|
6515 doneLooping = false; |
|
6516 if (aPos->mDirection == eDirPrevious) |
|
6517 edgeCase = 1;//far edge, search from end backwards |
|
6518 else |
|
6519 edgeCase = -1;//near edge search from beginning onwards |
|
6520 thisLine=0;//this line means nothing now. |
|
6521 //everything else means something so keep looking "inside" the block |
|
6522 blockFrame = aPos->mResultFrame; |
|
6523 |
|
6524 } |
|
6525 else |
|
6526 { |
|
6527 result = NS_OK;//THIS is to mean that everything is ok to the containing while loop |
|
6528 break; |
|
6529 } |
|
6530 } |
|
6531 } while (!doneLooping); |
|
6532 } |
|
6533 return result; |
|
6534 } |
|
6535 |
|
6536 case eSelectParagraph: |
|
6537 return PeekOffsetParagraph(aPos); |
|
6538 |
|
6539 case eSelectBeginLine: |
|
6540 case eSelectEndLine: |
|
6541 { |
|
6542 // Adjusted so that the caret can't get confused when content changes |
|
6543 nsIFrame* blockFrame = AdjustFrameForSelectionStyles(this); |
|
6544 int32_t thisLine = nsFrame::GetLineNumber(blockFrame, aPos->mScrollViewStop, &blockFrame); |
|
6545 if (thisLine < 0) |
|
6546 return NS_ERROR_FAILURE; |
|
6547 nsAutoLineIterator it = blockFrame->GetLineIterator(); |
|
6548 NS_ASSERTION(it, "GetLineNumber() succeeded but no block frame?"); |
|
6549 |
|
6550 int32_t lineFrameCount; |
|
6551 nsIFrame *firstFrame; |
|
6552 nsRect usedRect; |
|
6553 uint32_t lineFlags; |
|
6554 nsIFrame* baseFrame = nullptr; |
|
6555 bool endOfLine = (eSelectEndLine == aPos->mAmount); |
|
6556 |
|
6557 if (aPos->mVisual && PresContext()->BidiEnabled()) { |
|
6558 bool lineIsRTL = it->GetDirection(); |
|
6559 bool isReordered; |
|
6560 nsIFrame *lastFrame; |
|
6561 result = it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame); |
|
6562 baseFrame = endOfLine ? lastFrame : firstFrame; |
|
6563 if (baseFrame) { |
|
6564 nsBidiLevel embeddingLevel = nsBidiPresUtils::GetFrameEmbeddingLevel(baseFrame); |
|
6565 // If the direction of the frame on the edge is opposite to that of the line, |
|
6566 // we'll need to drill down to its opposite end, so reverse endOfLine. |
|
6567 if ((embeddingLevel & 1) == !lineIsRTL) |
|
6568 endOfLine = !endOfLine; |
|
6569 } |
|
6570 } else { |
|
6571 it->GetLine(thisLine, &firstFrame, &lineFrameCount, usedRect, &lineFlags); |
|
6572 |
|
6573 nsIFrame* frame = firstFrame; |
|
6574 for (int32_t count = lineFrameCount; count; |
|
6575 --count, frame = frame->GetNextSibling()) { |
|
6576 if (!frame->IsGeneratedContentFrame()) { |
|
6577 baseFrame = frame; |
|
6578 if (!endOfLine) |
|
6579 break; |
|
6580 } |
|
6581 } |
|
6582 } |
|
6583 if (!baseFrame) |
|
6584 return NS_ERROR_FAILURE; |
|
6585 FrameTarget targetFrame = DrillDownToSelectionFrame(baseFrame, |
|
6586 endOfLine, 0); |
|
6587 FrameContentRange range = GetRangeForFrame(targetFrame.frame); |
|
6588 aPos->mResultContent = range.content; |
|
6589 aPos->mContentOffset = endOfLine ? range.end : range.start; |
|
6590 if (endOfLine && targetFrame.frame->HasSignificantTerminalNewline()) { |
|
6591 // Do not position the caret after the terminating newline if we're |
|
6592 // trying to move to the end of line (see bug 596506) |
|
6593 --aPos->mContentOffset; |
|
6594 } |
|
6595 aPos->mResultFrame = targetFrame.frame; |
|
6596 aPos->mAttachForward = (aPos->mContentOffset == range.start); |
|
6597 if (!range.content) |
|
6598 return NS_ERROR_FAILURE; |
|
6599 return NS_OK; |
|
6600 } |
|
6601 |
|
6602 default: |
|
6603 { |
|
6604 NS_ASSERTION(false, "Invalid amount"); |
|
6605 return NS_ERROR_FAILURE; |
|
6606 } |
|
6607 } |
|
6608 return NS_OK; |
|
6609 } |
|
6610 |
|
6611 nsIFrame::FrameSearchResult |
|
6612 nsFrame::PeekOffsetNoAmount(bool aForward, int32_t* aOffset) |
|
6613 { |
|
6614 NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); |
|
6615 // Sure, we can stop right here. |
|
6616 return FOUND; |
|
6617 } |
|
6618 |
|
6619 nsIFrame::FrameSearchResult |
|
6620 nsFrame::PeekOffsetCharacter(bool aForward, int32_t* aOffset, |
|
6621 bool aRespectClusters) |
|
6622 { |
|
6623 NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); |
|
6624 int32_t startOffset = *aOffset; |
|
6625 // A negative offset means "end of frame", which in our case means offset 1. |
|
6626 if (startOffset < 0) |
|
6627 startOffset = 1; |
|
6628 if (aForward == (startOffset == 0)) { |
|
6629 // We're before the frame and moving forward, or after it and moving backwards: |
|
6630 // skip to the other side and we're done. |
|
6631 *aOffset = 1 - startOffset; |
|
6632 return FOUND; |
|
6633 } |
|
6634 return CONTINUE; |
|
6635 } |
|
6636 |
|
6637 nsIFrame::FrameSearchResult |
|
6638 nsFrame::PeekOffsetWord(bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect, |
|
6639 int32_t* aOffset, PeekWordState* aState) |
|
6640 { |
|
6641 NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); |
|
6642 int32_t startOffset = *aOffset; |
|
6643 // This isn't text, so truncate the context |
|
6644 aState->mContext.Truncate(); |
|
6645 if (startOffset < 0) |
|
6646 startOffset = 1; |
|
6647 if (aForward == (startOffset == 0)) { |
|
6648 // We're before the frame and moving forward, or after it and moving backwards. |
|
6649 // If we're looking for non-whitespace, we found it (without skipping this frame). |
|
6650 if (!aState->mAtStart) { |
|
6651 if (aState->mLastCharWasPunctuation) { |
|
6652 // We're not punctuation, so this is a punctuation boundary. |
|
6653 if (BreakWordBetweenPunctuation(aState, aForward, false, false, aIsKeyboardSelect)) |
|
6654 return FOUND; |
|
6655 } else { |
|
6656 // This is not a punctuation boundary. |
|
6657 if (aWordSelectEatSpace && aState->mSawBeforeType) |
|
6658 return FOUND; |
|
6659 } |
|
6660 } |
|
6661 // Otherwise skip to the other side and note that we encountered non-whitespace. |
|
6662 *aOffset = 1 - startOffset; |
|
6663 aState->Update(false, // not punctuation |
|
6664 false // not whitespace |
|
6665 ); |
|
6666 if (!aWordSelectEatSpace) |
|
6667 aState->SetSawBeforeType(); |
|
6668 } |
|
6669 return CONTINUE; |
|
6670 } |
|
6671 |
|
6672 bool |
|
6673 nsFrame::BreakWordBetweenPunctuation(const PeekWordState* aState, |
|
6674 bool aForward, |
|
6675 bool aPunctAfter, bool aWhitespaceAfter, |
|
6676 bool aIsKeyboardSelect) |
|
6677 { |
|
6678 NS_ASSERTION(aPunctAfter != aState->mLastCharWasPunctuation, |
|
6679 "Call this only at punctuation boundaries"); |
|
6680 if (aState->mLastCharWasWhitespace) { |
|
6681 // We always stop between whitespace and punctuation |
|
6682 return true; |
|
6683 } |
|
6684 if (!Preferences::GetBool("layout.word_select.stop_at_punctuation")) { |
|
6685 // When this pref is false, we never stop at a punctuation boundary unless |
|
6686 // it's followed by whitespace (in the relevant direction). |
|
6687 return aWhitespaceAfter; |
|
6688 } |
|
6689 if (!aIsKeyboardSelect) { |
|
6690 // mouse caret movement (e.g. word selection) always stops at every punctuation boundary |
|
6691 return true; |
|
6692 } |
|
6693 bool afterPunct = aForward ? aState->mLastCharWasPunctuation : aPunctAfter; |
|
6694 if (!afterPunct) { |
|
6695 // keyboard caret movement only stops after punctuation (in content order) |
|
6696 return false; |
|
6697 } |
|
6698 // Stop only if we've seen some non-punctuation since the last whitespace; |
|
6699 // don't stop after punctuation that follows whitespace. |
|
6700 return aState->mSeenNonPunctuationSinceWhitespace; |
|
6701 } |
|
6702 |
|
6703 nsresult |
|
6704 nsFrame::CheckVisibility(nsPresContext* , int32_t , int32_t , bool , bool *, bool *) |
|
6705 { |
|
6706 return NS_ERROR_NOT_IMPLEMENTED; |
|
6707 } |
|
6708 |
|
6709 |
|
6710 int32_t |
|
6711 nsFrame::GetLineNumber(nsIFrame *aFrame, bool aLockScroll, nsIFrame** aContainingBlock) |
|
6712 { |
|
6713 NS_ASSERTION(aFrame, "null aFrame"); |
|
6714 nsFrameManager* frameManager = aFrame->PresContext()->FrameManager(); |
|
6715 nsIFrame *blockFrame = aFrame; |
|
6716 nsIFrame *thisBlock; |
|
6717 nsAutoLineIterator it; |
|
6718 nsresult result = NS_ERROR_FAILURE; |
|
6719 while (NS_FAILED(result) && blockFrame) |
|
6720 { |
|
6721 thisBlock = blockFrame; |
|
6722 if (thisBlock->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { |
|
6723 //if we are searching for a frame that is not in flow we will not find it. |
|
6724 //we must instead look for its placeholder |
|
6725 if (thisBlock->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) { |
|
6726 // abspos continuations don't have placeholders, get the fif |
|
6727 thisBlock = thisBlock->FirstInFlow(); |
|
6728 } |
|
6729 thisBlock = frameManager->GetPlaceholderFrameFor(thisBlock); |
|
6730 if (!thisBlock) |
|
6731 return -1; |
|
6732 } |
|
6733 blockFrame = thisBlock->GetParent(); |
|
6734 result = NS_OK; |
|
6735 if (blockFrame) { |
|
6736 if (aLockScroll && blockFrame->GetType() == nsGkAtoms::scrollFrame) |
|
6737 return -1; |
|
6738 it = blockFrame->GetLineIterator(); |
|
6739 if (!it) |
|
6740 result = NS_ERROR_FAILURE; |
|
6741 } |
|
6742 } |
|
6743 if (!blockFrame || !it) |
|
6744 return -1; |
|
6745 |
|
6746 if (aContainingBlock) |
|
6747 *aContainingBlock = blockFrame; |
|
6748 return it->FindLineContaining(thisBlock); |
|
6749 } |
|
6750 |
|
6751 nsresult |
|
6752 nsIFrame::GetFrameFromDirection(nsDirection aDirection, bool aVisual, |
|
6753 bool aJumpLines, bool aScrollViewStop, |
|
6754 nsIFrame** aOutFrame, int32_t* aOutOffset, bool* aOutJumpedLine) |
|
6755 { |
|
6756 nsresult result; |
|
6757 |
|
6758 if (!aOutFrame || !aOutOffset || !aOutJumpedLine) |
|
6759 return NS_ERROR_NULL_POINTER; |
|
6760 |
|
6761 nsPresContext* presContext = PresContext(); |
|
6762 *aOutFrame = nullptr; |
|
6763 *aOutOffset = 0; |
|
6764 *aOutJumpedLine = false; |
|
6765 |
|
6766 // Find the prev/next selectable frame |
|
6767 bool selectable = false; |
|
6768 nsIFrame *traversedFrame = this; |
|
6769 while (!selectable) { |
|
6770 nsIFrame *blockFrame; |
|
6771 |
|
6772 int32_t thisLine = nsFrame::GetLineNumber(traversedFrame, aScrollViewStop, &blockFrame); |
|
6773 if (thisLine < 0) |
|
6774 return NS_ERROR_FAILURE; |
|
6775 |
|
6776 nsAutoLineIterator it = blockFrame->GetLineIterator(); |
|
6777 NS_ASSERTION(it, "GetLineNumber() succeeded but no block frame?"); |
|
6778 |
|
6779 bool atLineEdge; |
|
6780 nsIFrame *firstFrame; |
|
6781 nsIFrame *lastFrame; |
|
6782 if (aVisual && presContext->BidiEnabled()) { |
|
6783 bool lineIsRTL = it->GetDirection(); |
|
6784 bool isReordered; |
|
6785 result = it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame); |
|
6786 nsIFrame** framePtr = aDirection == eDirPrevious ? &firstFrame : &lastFrame; |
|
6787 if (*framePtr) { |
|
6788 nsBidiLevel embeddingLevel = nsBidiPresUtils::GetFrameEmbeddingLevel(*framePtr); |
|
6789 if ((((embeddingLevel & 1) && lineIsRTL) || (!(embeddingLevel & 1) && !lineIsRTL)) == |
|
6790 (aDirection == eDirPrevious)) { |
|
6791 nsFrame::GetFirstLeaf(presContext, framePtr); |
|
6792 } else { |
|
6793 nsFrame::GetLastLeaf(presContext, framePtr); |
|
6794 } |
|
6795 atLineEdge = *framePtr == traversedFrame; |
|
6796 } else { |
|
6797 atLineEdge = true; |
|
6798 } |
|
6799 } else { |
|
6800 nsRect nonUsedRect; |
|
6801 int32_t lineFrameCount; |
|
6802 uint32_t lineFlags; |
|
6803 result = it->GetLine(thisLine, &firstFrame, &lineFrameCount,nonUsedRect, |
|
6804 &lineFlags); |
|
6805 if (NS_FAILED(result)) |
|
6806 return result; |
|
6807 |
|
6808 if (aDirection == eDirPrevious) { |
|
6809 nsFrame::GetFirstLeaf(presContext, &firstFrame); |
|
6810 atLineEdge = firstFrame == traversedFrame; |
|
6811 } else { // eDirNext |
|
6812 lastFrame = firstFrame; |
|
6813 for (;lineFrameCount > 1;lineFrameCount --){ |
|
6814 result = it->GetNextSiblingOnLine(lastFrame, thisLine); |
|
6815 if (NS_FAILED(result) || !lastFrame){ |
|
6816 NS_ERROR("should not be reached nsFrame"); |
|
6817 return NS_ERROR_FAILURE; |
|
6818 } |
|
6819 } |
|
6820 nsFrame::GetLastLeaf(presContext, &lastFrame); |
|
6821 atLineEdge = lastFrame == traversedFrame; |
|
6822 } |
|
6823 } |
|
6824 |
|
6825 if (atLineEdge) { |
|
6826 *aOutJumpedLine = true; |
|
6827 if (!aJumpLines) |
|
6828 return NS_ERROR_FAILURE; //we are done. cannot jump lines |
|
6829 } |
|
6830 |
|
6831 nsCOMPtr<nsIFrameEnumerator> frameTraversal; |
|
6832 result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal), |
|
6833 presContext, traversedFrame, |
|
6834 eLeaf, |
|
6835 aVisual && presContext->BidiEnabled(), |
|
6836 aScrollViewStop, |
|
6837 true // aFollowOOFs |
|
6838 ); |
|
6839 if (NS_FAILED(result)) |
|
6840 return result; |
|
6841 |
|
6842 if (aDirection == eDirNext) |
|
6843 frameTraversal->Next(); |
|
6844 else |
|
6845 frameTraversal->Prev(); |
|
6846 |
|
6847 traversedFrame = frameTraversal->CurrentItem(); |
|
6848 if (!traversedFrame) |
|
6849 return NS_ERROR_FAILURE; |
|
6850 traversedFrame->IsSelectable(&selectable, nullptr); |
|
6851 } // while (!selectable) |
|
6852 |
|
6853 *aOutOffset = (aDirection == eDirNext) ? 0 : -1; |
|
6854 |
|
6855 if (aVisual) { |
|
6856 uint8_t newLevel = NS_GET_EMBEDDING_LEVEL(traversedFrame); |
|
6857 uint8_t newBaseLevel = NS_GET_BASE_LEVEL(traversedFrame); |
|
6858 if ((newLevel & 1) != (newBaseLevel & 1)) // The new frame is reverse-direction, go to the other end |
|
6859 *aOutOffset = -1 - *aOutOffset; |
|
6860 } |
|
6861 *aOutFrame = traversedFrame; |
|
6862 return NS_OK; |
|
6863 } |
|
6864 |
|
6865 nsView* nsIFrame::GetClosestView(nsPoint* aOffset) const |
|
6866 { |
|
6867 nsPoint offset(0,0); |
|
6868 for (const nsIFrame *f = this; f; f = f->GetParent()) { |
|
6869 if (f->HasView()) { |
|
6870 if (aOffset) |
|
6871 *aOffset = offset; |
|
6872 return f->GetView(); |
|
6873 } |
|
6874 offset += f->GetPosition(); |
|
6875 } |
|
6876 |
|
6877 NS_NOTREACHED("No view on any parent? How did that happen?"); |
|
6878 return nullptr; |
|
6879 } |
|
6880 |
|
6881 |
|
6882 /* virtual */ void |
|
6883 nsFrame::ChildIsDirty(nsIFrame* aChild) |
|
6884 { |
|
6885 NS_NOTREACHED("should never be called on a frame that doesn't inherit from " |
|
6886 "nsContainerFrame"); |
|
6887 } |
|
6888 |
|
6889 |
|
6890 #ifdef ACCESSIBILITY |
|
6891 a11y::AccType |
|
6892 nsFrame::AccessibleType() |
|
6893 { |
|
6894 return a11y::eNoType; |
|
6895 } |
|
6896 #endif |
|
6897 |
|
6898 NS_DECLARE_FRAME_PROPERTY(OverflowAreasProperty, |
|
6899 nsIFrame::DestroyOverflowAreas) |
|
6900 |
|
6901 bool |
|
6902 nsIFrame::ClearOverflowRects() |
|
6903 { |
|
6904 if (mOverflow.mType == NS_FRAME_OVERFLOW_NONE) { |
|
6905 return false; |
|
6906 } |
|
6907 if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) { |
|
6908 Properties().Delete(OverflowAreasProperty()); |
|
6909 } |
|
6910 mOverflow.mType = NS_FRAME_OVERFLOW_NONE; |
|
6911 return true; |
|
6912 } |
|
6913 |
|
6914 /** Create or retrieve the previously stored overflow area, if the frame does |
|
6915 * not overflow and no creation is required return nullptr. |
|
6916 * @return pointer to the overflow area rectangle |
|
6917 */ |
|
6918 nsOverflowAreas* |
|
6919 nsIFrame::GetOverflowAreasProperty() |
|
6920 { |
|
6921 FrameProperties props = Properties(); |
|
6922 nsOverflowAreas *overflow = |
|
6923 static_cast<nsOverflowAreas*>(props.Get(OverflowAreasProperty())); |
|
6924 |
|
6925 if (overflow) { |
|
6926 return overflow; // the property already exists |
|
6927 } |
|
6928 |
|
6929 // The property isn't set yet, so allocate a new rect, set the property, |
|
6930 // and return the newly allocated rect |
|
6931 overflow = new nsOverflowAreas; |
|
6932 props.Set(OverflowAreasProperty(), overflow); |
|
6933 return overflow; |
|
6934 } |
|
6935 |
|
6936 /** Set the overflowArea rect, storing it as deltas or a separate rect |
|
6937 * depending on its size in relation to the primary frame rect. |
|
6938 */ |
|
6939 bool |
|
6940 nsIFrame::SetOverflowAreas(const nsOverflowAreas& aOverflowAreas) |
|
6941 { |
|
6942 if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) { |
|
6943 nsOverflowAreas *overflow = |
|
6944 static_cast<nsOverflowAreas*>(Properties().Get(OverflowAreasProperty())); |
|
6945 bool changed = *overflow != aOverflowAreas; |
|
6946 *overflow = aOverflowAreas; |
|
6947 |
|
6948 // Don't bother with converting to the deltas form if we already |
|
6949 // have a property. |
|
6950 return changed; |
|
6951 } |
|
6952 |
|
6953 const nsRect& vis = aOverflowAreas.VisualOverflow(); |
|
6954 uint32_t l = -vis.x, // left edge: positive delta is leftwards |
|
6955 t = -vis.y, // top: positive is upwards |
|
6956 r = vis.XMost() - mRect.width, // right: positive is rightwards |
|
6957 b = vis.YMost() - mRect.height; // bottom: positive is downwards |
|
6958 if (aOverflowAreas.ScrollableOverflow().IsEqualEdges(nsRect(nsPoint(0, 0), GetSize())) && |
|
6959 l <= NS_FRAME_OVERFLOW_DELTA_MAX && |
|
6960 t <= NS_FRAME_OVERFLOW_DELTA_MAX && |
|
6961 r <= NS_FRAME_OVERFLOW_DELTA_MAX && |
|
6962 b <= NS_FRAME_OVERFLOW_DELTA_MAX && |
|
6963 // we have to check these against zero because we *never* want to |
|
6964 // set a frame as having no overflow in this function. This is |
|
6965 // because FinishAndStoreOverflow calls this function prior to |
|
6966 // SetRect based on whether the overflow areas match aNewSize. |
|
6967 // In the case where the overflow areas exactly match mRect but |
|
6968 // do not match aNewSize, we need to store overflow in a property |
|
6969 // so that our eventual SetRect/SetSize will know that it has to |
|
6970 // reset our overflow areas. |
|
6971 (l | t | r | b) != 0) { |
|
6972 VisualDeltas oldDeltas = mOverflow.mVisualDeltas; |
|
6973 // It's a "small" overflow area so we store the deltas for each edge |
|
6974 // directly in the frame, rather than allocating a separate rect. |
|
6975 // If they're all zero, that's fine; we're setting things to |
|
6976 // no-overflow. |
|
6977 mOverflow.mVisualDeltas.mLeft = l; |
|
6978 mOverflow.mVisualDeltas.mTop = t; |
|
6979 mOverflow.mVisualDeltas.mRight = r; |
|
6980 mOverflow.mVisualDeltas.mBottom = b; |
|
6981 // There was no scrollable overflow before, and there isn't now. |
|
6982 return oldDeltas != mOverflow.mVisualDeltas; |
|
6983 } else { |
|
6984 bool changed = !aOverflowAreas.ScrollableOverflow().IsEqualEdges(nsRect(nsPoint(0, 0), GetSize())) || |
|
6985 !aOverflowAreas.VisualOverflow().IsEqualEdges(GetVisualOverflowFromDeltas()); |
|
6986 |
|
6987 // it's a large overflow area that we need to store as a property |
|
6988 mOverflow.mType = NS_FRAME_OVERFLOW_LARGE; |
|
6989 nsOverflowAreas* overflow = GetOverflowAreasProperty(); |
|
6990 NS_ASSERTION(overflow, "should have created areas"); |
|
6991 *overflow = aOverflowAreas; |
|
6992 return changed; |
|
6993 } |
|
6994 } |
|
6995 |
|
6996 inline bool |
|
6997 IsInlineFrame(nsIFrame *aFrame) |
|
6998 { |
|
6999 nsIAtom *type = aFrame->GetType(); |
|
7000 return type == nsGkAtoms::inlineFrame; |
|
7001 } |
|
7002 |
|
7003 /** |
|
7004 * Compute the union of the border boxes of aFrame and its descendants, |
|
7005 * in aFrame's coordinate space (if aApplyTransform is false) or its |
|
7006 * post-transform coordinate space (if aApplyTransform is true). |
|
7007 */ |
|
7008 static nsRect |
|
7009 UnionBorderBoxes(nsIFrame* aFrame, bool aApplyTransform, |
|
7010 const nsSize* aSizeOverride = nullptr, |
|
7011 const nsOverflowAreas* aOverflowOverride = nullptr) |
|
7012 { |
|
7013 const nsRect bounds(nsPoint(0, 0), |
|
7014 aSizeOverride ? *aSizeOverride : aFrame->GetSize()); |
|
7015 |
|
7016 // Start from our border-box, transformed. See comment below about |
|
7017 // transform of children. |
|
7018 nsRect u; |
|
7019 bool doTransform = aApplyTransform && aFrame->IsTransformed(); |
|
7020 if (doTransform) { |
|
7021 u = nsDisplayTransform::TransformRect(bounds, aFrame, |
|
7022 nsPoint(0, 0), &bounds); |
|
7023 } else { |
|
7024 u = bounds; |
|
7025 } |
|
7026 |
|
7027 // Only iterate through the children if the overflow areas suggest |
|
7028 // that we might need to, and if the frame doesn't clip its overflow |
|
7029 // anyway. |
|
7030 if (aOverflowOverride) { |
|
7031 if (!doTransform && |
|
7032 bounds.IsEqualEdges(aOverflowOverride->VisualOverflow()) && |
|
7033 bounds.IsEqualEdges(aOverflowOverride->ScrollableOverflow())) { |
|
7034 return u; |
|
7035 } |
|
7036 } else { |
|
7037 if (!doTransform && |
|
7038 bounds.IsEqualEdges(aFrame->GetVisualOverflowRect()) && |
|
7039 bounds.IsEqualEdges(aFrame->GetScrollableOverflowRect())) { |
|
7040 return u; |
|
7041 } |
|
7042 } |
|
7043 const nsStyleDisplay* disp = aFrame->StyleDisplay(); |
|
7044 nsIAtom* fType = aFrame->GetType(); |
|
7045 if (nsFrame::ShouldApplyOverflowClipping(aFrame, disp) || |
|
7046 fType == nsGkAtoms::scrollFrame || |
|
7047 fType == nsGkAtoms::svgOuterSVGFrame) { |
|
7048 return u; |
|
7049 } |
|
7050 |
|
7051 nsRect clipPropClipRect; |
|
7052 bool hasClipPropClip = |
|
7053 aFrame->GetClipPropClipRect(disp, &clipPropClipRect, bounds.Size()); |
|
7054 |
|
7055 // Iterate over all children except pop-ups. |
|
7056 const nsIFrame::ChildListIDs skip(nsIFrame::kPopupList | |
|
7057 nsIFrame::kSelectPopupList); |
|
7058 for (nsIFrame::ChildListIterator childLists(aFrame); |
|
7059 !childLists.IsDone(); childLists.Next()) { |
|
7060 if (skip.Contains(childLists.CurrentID())) { |
|
7061 continue; |
|
7062 } |
|
7063 |
|
7064 nsFrameList children = childLists.CurrentList(); |
|
7065 for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) { |
|
7066 nsIFrame* child = e.get(); |
|
7067 // Note that passing |true| for aApplyTransform when |
|
7068 // child->Preserves3D() is incorrect if our aApplyTransform is |
|
7069 // false... but the opposite would be as well. This is because |
|
7070 // elements within a preserve-3d scene are always transformed up |
|
7071 // to the top of the scene. This means we don't have a |
|
7072 // mechanism for getting a transform up to an intermediate point |
|
7073 // within the scene. We choose to over-transform rather than |
|
7074 // under-transform because this is consistent with other |
|
7075 // overflow areas. |
|
7076 nsRect childRect = UnionBorderBoxes(child, true) + |
|
7077 child->GetPosition(); |
|
7078 |
|
7079 if (hasClipPropClip) { |
|
7080 // Intersect with the clip before transforming. |
|
7081 childRect.IntersectRect(childRect, clipPropClipRect); |
|
7082 } |
|
7083 |
|
7084 // Note that we transform each child separately according to |
|
7085 // aFrame's transform, and then union, which gives a different |
|
7086 // (smaller) result from unioning and then transforming the |
|
7087 // union. This doesn't match the way we handle overflow areas |
|
7088 // with 2-D transforms, though it does match the way we handle |
|
7089 // overflow areas in preserve-3d 3-D scenes. |
|
7090 if (doTransform && !child->Preserves3D()) { |
|
7091 childRect = nsDisplayTransform::TransformRect(childRect, aFrame, |
|
7092 nsPoint(0, 0), &bounds); |
|
7093 } |
|
7094 u.UnionRectEdges(u, childRect); |
|
7095 } |
|
7096 } |
|
7097 |
|
7098 return u; |
|
7099 } |
|
7100 |
|
7101 static void |
|
7102 ComputeAndIncludeOutlineArea(nsIFrame* aFrame, nsOverflowAreas& aOverflowAreas, |
|
7103 const nsSize& aNewSize) |
|
7104 { |
|
7105 const nsStyleOutline* outline = aFrame->StyleOutline(); |
|
7106 if (outline->GetOutlineStyle() == NS_STYLE_BORDER_STYLE_NONE) { |
|
7107 return; |
|
7108 } |
|
7109 |
|
7110 nscoord width; |
|
7111 DebugOnly<bool> result = outline->GetOutlineWidth(width); |
|
7112 NS_ASSERTION(result, "GetOutlineWidth had no cached outline width"); |
|
7113 if (width <= 0) { |
|
7114 return; |
|
7115 } |
|
7116 |
|
7117 // When the outline property is set on :-moz-anonymous-block or |
|
7118 // :-moz-anonymous-positioned-block pseudo-elements, it inherited |
|
7119 // that outline from the inline that was broken because it |
|
7120 // contained a block. In that case, we don't want a really wide |
|
7121 // outline if the block inside the inline is narrow, so union the |
|
7122 // actual contents of the anonymous blocks. |
|
7123 nsIFrame *frameForArea = aFrame; |
|
7124 do { |
|
7125 nsIAtom *pseudoType = frameForArea->StyleContext()->GetPseudo(); |
|
7126 if (pseudoType != nsCSSAnonBoxes::mozAnonymousBlock && |
|
7127 pseudoType != nsCSSAnonBoxes::mozAnonymousPositionedBlock) |
|
7128 break; |
|
7129 // If we're done, we really want it and all its later siblings. |
|
7130 frameForArea = frameForArea->GetFirstPrincipalChild(); |
|
7131 NS_ASSERTION(frameForArea, "anonymous block with no children?"); |
|
7132 } while (frameForArea); |
|
7133 |
|
7134 // Find the union of the border boxes of all descendants, or in |
|
7135 // the block-in-inline case, all descendants we care about. |
|
7136 // |
|
7137 // Note that the interesting perspective-related cases are taken |
|
7138 // care of by the code that handles those issues for overflow |
|
7139 // calling FinishAndStoreOverflow again, which in turn calls this |
|
7140 // function again. We still need to deal with preserve-3d a bit. |
|
7141 nsRect innerRect; |
|
7142 if (frameForArea == aFrame) { |
|
7143 innerRect = UnionBorderBoxes(aFrame, false, &aNewSize, &aOverflowAreas); |
|
7144 } else { |
|
7145 for (; frameForArea; frameForArea = frameForArea->GetNextSibling()) { |
|
7146 nsRect r(UnionBorderBoxes(frameForArea, true)); |
|
7147 |
|
7148 // Adjust for offsets transforms up to aFrame's pre-transform |
|
7149 // (i.e., normal) coordinate space; see comments in |
|
7150 // UnionBorderBoxes for some of the subtlety here. |
|
7151 for (nsIFrame *f = frameForArea, *parent = f->GetParent(); |
|
7152 /* see middle of loop */; |
|
7153 f = parent, parent = f->GetParent()) { |
|
7154 r += f->GetPosition(); |
|
7155 if (parent == aFrame) { |
|
7156 break; |
|
7157 } |
|
7158 if (parent->IsTransformed() && !f->Preserves3D()) { |
|
7159 r = nsDisplayTransform::TransformRect(r, parent, nsPoint(0, 0)); |
|
7160 } |
|
7161 } |
|
7162 |
|
7163 innerRect.UnionRect(innerRect, r); |
|
7164 } |
|
7165 } |
|
7166 |
|
7167 aFrame->Properties().Set(nsIFrame::OutlineInnerRectProperty(), |
|
7168 new nsRect(innerRect)); |
|
7169 |
|
7170 nscoord offset = outline->mOutlineOffset; |
|
7171 nscoord inflateBy = std::max(width + offset, 0); |
|
7172 |
|
7173 // Keep this code (and the storing of properties just above) in |
|
7174 // sync with GetOutlineInnerRect in nsCSSRendering.cpp. |
|
7175 nsRect outerRect(innerRect); |
|
7176 outerRect.Inflate(inflateBy, inflateBy); |
|
7177 |
|
7178 nsRect& vo = aOverflowAreas.VisualOverflow(); |
|
7179 vo.UnionRectEdges(vo, outerRect); |
|
7180 } |
|
7181 |
|
7182 bool |
|
7183 nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas, |
|
7184 nsSize aNewSize, nsSize* aOldSize) |
|
7185 { |
|
7186 NS_ASSERTION(FrameMaintainsOverflow(this), |
|
7187 "Don't call - overflow rects not maintained on these SVG frames"); |
|
7188 |
|
7189 nsRect bounds(nsPoint(0, 0), aNewSize); |
|
7190 // Store the passed in overflow area if we are a preserve-3d frame or we have |
|
7191 // a transform, and it's not just the frame bounds. |
|
7192 if (Preserves3D() || HasPerspective() || IsTransformed()) { |
|
7193 if (!aOverflowAreas.VisualOverflow().IsEqualEdges(bounds) || |
|
7194 !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) { |
|
7195 |
|
7196 nsOverflowAreas* initial = |
|
7197 static_cast<nsOverflowAreas*>(Properties().Get(nsIFrame::InitialOverflowProperty())); |
|
7198 if (!initial) { |
|
7199 Properties().Set(nsIFrame::InitialOverflowProperty(), |
|
7200 new nsOverflowAreas(aOverflowAreas)); |
|
7201 } else if (initial != &aOverflowAreas) { |
|
7202 *initial = aOverflowAreas; |
|
7203 } |
|
7204 } |
|
7205 #ifdef DEBUG |
|
7206 Properties().Set(nsIFrame::DebugInitialOverflowPropertyApplied(), nullptr); |
|
7207 #endif |
|
7208 } else { |
|
7209 #ifdef DEBUG |
|
7210 Properties().Delete(nsIFrame::DebugInitialOverflowPropertyApplied()); |
|
7211 #endif |
|
7212 } |
|
7213 |
|
7214 // This is now called FinishAndStoreOverflow() instead of |
|
7215 // StoreOverflow() because frame-generic ways of adding overflow |
|
7216 // can happen here, e.g. CSS2 outline and native theme. |
|
7217 // If the overflow area width or height is nscoord_MAX, then a |
|
7218 // saturating union may have encounted an overflow, so the overflow may not |
|
7219 // contain the frame border-box. Don't warn in that case. |
|
7220 // Don't warn for SVG either, since SVG doesn't need the overflow area |
|
7221 // to contain the frame bounds. |
|
7222 NS_FOR_FRAME_OVERFLOW_TYPES(otype) { |
|
7223 DebugOnly<nsRect*> r = &aOverflowAreas.Overflow(otype); |
|
7224 NS_ASSERTION(aNewSize.width == 0 || aNewSize.height == 0 || |
|
7225 r->width == nscoord_MAX || r->height == nscoord_MAX || |
|
7226 (mState & NS_FRAME_SVG_LAYOUT) || |
|
7227 r->Contains(nsRect(nsPoint(0,0), aNewSize)), |
|
7228 "Computed overflow area must contain frame bounds"); |
|
7229 } |
|
7230 |
|
7231 // If we clip our children, clear accumulated overflow area. The |
|
7232 // children are actually clipped to the padding-box, but since the |
|
7233 // overflow area should include the entire border-box, just set it to |
|
7234 // the border-box here. |
|
7235 const nsStyleDisplay* disp = StyleDisplay(); |
|
7236 NS_ASSERTION((disp->mOverflowY == NS_STYLE_OVERFLOW_CLIP) == |
|
7237 (disp->mOverflowX == NS_STYLE_OVERFLOW_CLIP), |
|
7238 "If one overflow is clip, the other should be too"); |
|
7239 if (nsFrame::ShouldApplyOverflowClipping(this, disp)) { |
|
7240 // The contents are actually clipped to the padding area |
|
7241 aOverflowAreas.SetAllTo(bounds); |
|
7242 } |
|
7243 |
|
7244 // Overflow area must always include the frame's top-left and bottom-right, |
|
7245 // even if the frame rect is empty (so we can scroll to those positions). |
|
7246 // Pending a real fix for bug 426879, don't do this for inline frames |
|
7247 // with zero width. |
|
7248 // Do not do this for SVG either, since it will usually massively increase |
|
7249 // the area unnecessarily. |
|
7250 if ((aNewSize.width != 0 || !IsInlineFrame(this)) && |
|
7251 !(GetStateBits() & NS_FRAME_SVG_LAYOUT)) { |
|
7252 NS_FOR_FRAME_OVERFLOW_TYPES(otype) { |
|
7253 nsRect& o = aOverflowAreas.Overflow(otype); |
|
7254 o.UnionRectEdges(o, bounds); |
|
7255 } |
|
7256 } |
|
7257 |
|
7258 // Note that NS_STYLE_OVERFLOW_CLIP doesn't clip the frame background, |
|
7259 // so we add theme background overflow here so it's not clipped. |
|
7260 if (!IsBoxWrapped() && IsThemed(disp)) { |
|
7261 nsRect r(bounds); |
|
7262 nsPresContext *presContext = PresContext(); |
|
7263 if (presContext->GetTheme()-> |
|
7264 GetWidgetOverflow(presContext->DeviceContext(), this, |
|
7265 disp->mAppearance, &r)) { |
|
7266 nsRect& vo = aOverflowAreas.VisualOverflow(); |
|
7267 vo.UnionRectEdges(vo, r); |
|
7268 } |
|
7269 } |
|
7270 |
|
7271 ComputeAndIncludeOutlineArea(this, aOverflowAreas, aNewSize); |
|
7272 |
|
7273 // Nothing in here should affect scrollable overflow. |
|
7274 aOverflowAreas.VisualOverflow() = |
|
7275 ComputeEffectsRect(this, aOverflowAreas.VisualOverflow(), aNewSize); |
|
7276 |
|
7277 // Absolute position clipping |
|
7278 nsRect clipPropClipRect; |
|
7279 bool hasClipPropClip = GetClipPropClipRect(disp, &clipPropClipRect, aNewSize); |
|
7280 if (hasClipPropClip) { |
|
7281 NS_FOR_FRAME_OVERFLOW_TYPES(otype) { |
|
7282 nsRect& o = aOverflowAreas.Overflow(otype); |
|
7283 o.IntersectRect(o, clipPropClipRect); |
|
7284 } |
|
7285 } |
|
7286 |
|
7287 /* If we're transformed, transform the overflow rect by the current transformation. */ |
|
7288 bool hasTransform = IsTransformed(); |
|
7289 nsSize oldSize = aOldSize ? *aOldSize : mRect.Size(); |
|
7290 bool sizeChanged = (oldSize != aNewSize); |
|
7291 if (hasTransform) { |
|
7292 Properties().Set(nsIFrame::PreTransformOverflowAreasProperty(), |
|
7293 new nsOverflowAreas(aOverflowAreas)); |
|
7294 /* Since our size might not actually have been computed yet, we need to make sure that we use the |
|
7295 * correct dimensions by overriding the stored bounding rectangle with the value the caller has |
|
7296 * ensured us we'll use. |
|
7297 */ |
|
7298 nsRect newBounds(nsPoint(0, 0), aNewSize); |
|
7299 // Transform affects both overflow areas. |
|
7300 NS_FOR_FRAME_OVERFLOW_TYPES(otype) { |
|
7301 nsRect& o = aOverflowAreas.Overflow(otype); |
|
7302 o = nsDisplayTransform::TransformRect(o, this, nsPoint(0, 0), &newBounds); |
|
7303 } |
|
7304 if (Preserves3DChildren()) { |
|
7305 ComputePreserve3DChildrenOverflow(aOverflowAreas, newBounds); |
|
7306 } else if (sizeChanged && ChildrenHavePerspective()) { |
|
7307 RecomputePerspectiveChildrenOverflow(this->StyleContext(), &newBounds); |
|
7308 } |
|
7309 } else { |
|
7310 Properties().Delete(nsIFrame::PreTransformOverflowAreasProperty()); |
|
7311 if (ChildrenHavePerspective() && sizeChanged) { |
|
7312 nsRect newBounds(nsPoint(0, 0), aNewSize); |
|
7313 RecomputePerspectiveChildrenOverflow(this->StyleContext(), &newBounds); |
|
7314 } |
|
7315 } |
|
7316 |
|
7317 bool anyOverflowChanged; |
|
7318 if (aOverflowAreas != nsOverflowAreas(bounds, bounds)) { |
|
7319 anyOverflowChanged = SetOverflowAreas(aOverflowAreas); |
|
7320 } else { |
|
7321 anyOverflowChanged = ClearOverflowRects(); |
|
7322 } |
|
7323 |
|
7324 if (anyOverflowChanged) { |
|
7325 nsSVGEffects::InvalidateDirectRenderingObservers(this); |
|
7326 } |
|
7327 return anyOverflowChanged; |
|
7328 } |
|
7329 |
|
7330 void |
|
7331 nsIFrame::RecomputePerspectiveChildrenOverflow(const nsStyleContext* aStartStyle, const nsRect* aBounds) |
|
7332 { |
|
7333 // Children may check our size when getting our transform, make sure it's valid. |
|
7334 nsSize oldSize = GetSize(); |
|
7335 if (aBounds) { |
|
7336 SetSize(aBounds->Size()); |
|
7337 } |
|
7338 nsIFrame::ChildListIterator lists(this); |
|
7339 for (; !lists.IsDone(); lists.Next()) { |
|
7340 nsFrameList::Enumerator childFrames(lists.CurrentList()); |
|
7341 for (; !childFrames.AtEnd(); childFrames.Next()) { |
|
7342 nsIFrame* child = childFrames.get(); |
|
7343 if (!FrameMaintainsOverflow(child)) { |
|
7344 continue; // frame does not maintain overflow rects |
|
7345 } |
|
7346 if (child->HasPerspective()) { |
|
7347 nsOverflowAreas* overflow = |
|
7348 static_cast<nsOverflowAreas*>(child->Properties().Get(nsIFrame::InitialOverflowProperty())); |
|
7349 nsRect bounds(nsPoint(0, 0), child->GetSize()); |
|
7350 if (overflow) { |
|
7351 nsOverflowAreas overflowCopy = *overflow; |
|
7352 child->FinishAndStoreOverflow(overflowCopy, bounds.Size()); |
|
7353 } else { |
|
7354 nsOverflowAreas boundsOverflow; |
|
7355 boundsOverflow.SetAllTo(bounds); |
|
7356 child->FinishAndStoreOverflow(boundsOverflow, bounds.Size()); |
|
7357 } |
|
7358 } else if (child->StyleContext()->GetParent() == aStartStyle || |
|
7359 child->StyleContext() == aStartStyle) { |
|
7360 // If a frame is using perspective, then the size used to compute |
|
7361 // perspective-origin is the size of the frame belonging to its parent |
|
7362 // style context. We must find any descendant frames using our size |
|
7363 // (by recurse into frames with the same style context, or a direct |
|
7364 // child style context) to update their overflow rects too. |
|
7365 child->RecomputePerspectiveChildrenOverflow(aStartStyle, nullptr); |
|
7366 } |
|
7367 } |
|
7368 } |
|
7369 // Restore our old size just in case something depends on this elesewhere. |
|
7370 SetSize(oldSize); |
|
7371 } |
|
7372 |
|
7373 /* The overflow rects for leaf nodes in a preserve-3d hierarchy depends on |
|
7374 * the mRect value for their parents (since we use their transform, and transform |
|
7375 * depends on this for transform-origin etc). These weren't necessarily correct |
|
7376 * when we reflowed initially, so walk over all preserve-3d children and repeat the |
|
7377 * overflow calculation. |
|
7378 */ |
|
7379 static void |
|
7380 RecomputePreserve3DChildrenOverflow(nsIFrame* aFrame, const nsRect* aBounds) |
|
7381 { |
|
7382 // Children may check our size when getting our transform, make sure it's valid. |
|
7383 nsSize oldSize = aFrame->GetSize(); |
|
7384 if (aBounds) { |
|
7385 aFrame->SetSize(aBounds->Size()); |
|
7386 } |
|
7387 nsIFrame::ChildListIterator lists(aFrame); |
|
7388 for (; !lists.IsDone(); lists.Next()) { |
|
7389 nsFrameList::Enumerator childFrames(lists.CurrentList()); |
|
7390 for (; !childFrames.AtEnd(); childFrames.Next()) { |
|
7391 nsIFrame* child = childFrames.get(); |
|
7392 if (!FrameMaintainsOverflow(child)) { |
|
7393 continue; // frame does not maintain overflow rects |
|
7394 } |
|
7395 if (child->Preserves3DChildren()) { |
|
7396 RecomputePreserve3DChildrenOverflow(child, nullptr); |
|
7397 } else if (child->Preserves3D()) { |
|
7398 nsOverflowAreas* overflow = |
|
7399 static_cast<nsOverflowAreas*>(child->Properties().Get(nsIFrame::InitialOverflowProperty())); |
|
7400 nsRect bounds(nsPoint(0, 0), child->GetSize()); |
|
7401 if (overflow) { |
|
7402 nsOverflowAreas overflowCopy = *overflow; |
|
7403 child->FinishAndStoreOverflow(overflowCopy, bounds.Size()); |
|
7404 } else { |
|
7405 nsOverflowAreas boundsOverflow; |
|
7406 boundsOverflow.SetAllTo(bounds); |
|
7407 child->FinishAndStoreOverflow(boundsOverflow, bounds.Size()); |
|
7408 } |
|
7409 } |
|
7410 } |
|
7411 } |
|
7412 // Restore our old size just in case something depends on this elesewhere. |
|
7413 aFrame->SetSize(oldSize); |
|
7414 |
|
7415 // Only repeat computing our overflow in recursive calls since the initial caller is still |
|
7416 // in the middle of doing this and we don't want an infinite loop. |
|
7417 if (!aBounds) { |
|
7418 nsOverflowAreas* overflow = |
|
7419 static_cast<nsOverflowAreas*>(aFrame->Properties().Get(nsIFrame::InitialOverflowProperty())); |
|
7420 nsRect bounds(nsPoint(0, 0), aFrame->GetSize()); |
|
7421 if (overflow) { |
|
7422 nsOverflowAreas overflowCopy = *overflow; |
|
7423 overflowCopy.UnionAllWith(bounds); |
|
7424 aFrame->FinishAndStoreOverflow(overflowCopy, bounds.Size()); |
|
7425 } else { |
|
7426 nsOverflowAreas boundsOverflow; |
|
7427 boundsOverflow.SetAllTo(bounds); |
|
7428 aFrame->FinishAndStoreOverflow(boundsOverflow, bounds.Size()); |
|
7429 } |
|
7430 } |
|
7431 } |
|
7432 |
|
7433 void |
|
7434 nsIFrame::ComputePreserve3DChildrenOverflow(nsOverflowAreas& aOverflowAreas, const nsRect& aBounds) |
|
7435 { |
|
7436 // When we are preserving 3d we need to iterate over all children separately. |
|
7437 // If the child also preserves 3d then their overflow will already been in our |
|
7438 // coordinate space, otherwise we need to transform. |
|
7439 |
|
7440 // If we're the top frame in a preserve 3d chain then we need to recalculate the overflow |
|
7441 // areas of all our children since they will have used our size/offset which was invalid at |
|
7442 // the time. |
|
7443 if (!Preserves3D()) { |
|
7444 RecomputePreserve3DChildrenOverflow(this, &aBounds); |
|
7445 } |
|
7446 |
|
7447 nsRect childVisual; |
|
7448 nsRect childScrollable; |
|
7449 nsIFrame::ChildListIterator lists(this); |
|
7450 for (; !lists.IsDone(); lists.Next()) { |
|
7451 nsFrameList::Enumerator childFrames(lists.CurrentList()); |
|
7452 for (; !childFrames.AtEnd(); childFrames.Next()) { |
|
7453 nsIFrame* child = childFrames.get(); |
|
7454 nsPoint offset = child->GetPosition(); |
|
7455 nsRect visual = child->GetVisualOverflowRect(); |
|
7456 nsRect scrollable = child->GetScrollableOverflowRect(); |
|
7457 visual.MoveBy(offset); |
|
7458 scrollable.MoveBy(offset); |
|
7459 if (child->Preserves3D()) { |
|
7460 childVisual = childVisual.Union(visual); |
|
7461 childScrollable = childScrollable.Union(scrollable); |
|
7462 } else { |
|
7463 childVisual = |
|
7464 childVisual.Union(nsDisplayTransform::TransformRect(visual, |
|
7465 this, nsPoint(0,0), &aBounds)); |
|
7466 childScrollable = |
|
7467 childScrollable.Union(nsDisplayTransform::TransformRect(scrollable, |
|
7468 this, nsPoint(0,0), &aBounds)); |
|
7469 } |
|
7470 } |
|
7471 } |
|
7472 |
|
7473 aOverflowAreas.Overflow(eVisualOverflow) = aOverflowAreas.Overflow(eVisualOverflow).Union(childVisual); |
|
7474 aOverflowAreas.Overflow(eScrollableOverflow) = aOverflowAreas.Overflow(eScrollableOverflow).Union(childScrollable); |
|
7475 } |
|
7476 |
|
7477 void |
|
7478 nsFrame::ConsiderChildOverflow(nsOverflowAreas& aOverflowAreas, |
|
7479 nsIFrame* aChildFrame) |
|
7480 { |
|
7481 aOverflowAreas.UnionWith(aChildFrame->GetOverflowAreas() + |
|
7482 aChildFrame->GetPosition()); |
|
7483 } |
|
7484 |
|
7485 /** |
|
7486 * This function takes a frame that is part of a block-in-inline split, |
|
7487 * and _if_ that frame is an anonymous block created by an ib split it |
|
7488 * returns the block's preceding inline. This is needed because the |
|
7489 * split inline's style context is the parent of the anonymous block's |
|
7490 * style context. |
|
7491 * |
|
7492 * If aFrame is not an anonymous block, null is returned. |
|
7493 */ |
|
7494 static nsIFrame* |
|
7495 GetIBSplitSiblingForAnonymousBlock(const nsIFrame* aFrame) |
|
7496 { |
|
7497 NS_PRECONDITION(aFrame, "Must have a non-null frame!"); |
|
7498 NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT, |
|
7499 "GetIBSplitSibling should only be called on ib-split frames"); |
|
7500 |
|
7501 nsIAtom* type = aFrame->StyleContext()->GetPseudo(); |
|
7502 if (type != nsCSSAnonBoxes::mozAnonymousBlock && |
|
7503 type != nsCSSAnonBoxes::mozAnonymousPositionedBlock) { |
|
7504 // it's not an anonymous block |
|
7505 return nullptr; |
|
7506 } |
|
7507 |
|
7508 // Find the first continuation of the frame. (Ugh. This ends up |
|
7509 // being O(N^2) when it is called O(N) times.) |
|
7510 aFrame = aFrame->FirstContinuation(); |
|
7511 |
|
7512 /* |
|
7513 * Now look up the nsGkAtoms::IBSplitPrevSibling |
|
7514 * property. |
|
7515 */ |
|
7516 nsIFrame *ibSplitSibling = static_cast<nsIFrame*> |
|
7517 (aFrame->Properties().Get(nsIFrame::IBSplitPrevSibling())); |
|
7518 NS_ASSERTION(ibSplitSibling, "Broken frame tree?"); |
|
7519 return ibSplitSibling; |
|
7520 } |
|
7521 |
|
7522 /** |
|
7523 * Get the parent, corrected for the mangled frame tree resulting from |
|
7524 * having a block within an inline. The result only differs from the |
|
7525 * result of |GetParent| when |GetParent| returns an anonymous block |
|
7526 * that was created for an element that was 'display: inline' because |
|
7527 * that element contained a block. |
|
7528 * |
|
7529 * Also skip anonymous scrolled-content parents; inherit directly from the |
|
7530 * outer scroll frame. |
|
7531 */ |
|
7532 static nsIFrame* |
|
7533 GetCorrectedParent(const nsIFrame* aFrame) |
|
7534 { |
|
7535 nsIFrame *parent = aFrame->GetParent(); |
|
7536 if (!parent) { |
|
7537 return nullptr; |
|
7538 } |
|
7539 |
|
7540 // Outer tables are always anon boxes; if we're in here for an outer |
|
7541 // table, that actually means its the _inner_ table that wants to |
|
7542 // know its parent. So get the pseudo of the inner in that case. |
|
7543 nsIAtom* pseudo = aFrame->StyleContext()->GetPseudo(); |
|
7544 if (pseudo == nsCSSAnonBoxes::tableOuter) { |
|
7545 pseudo = aFrame->GetFirstPrincipalChild()->StyleContext()->GetPseudo(); |
|
7546 } |
|
7547 return nsFrame::CorrectStyleParentFrame(parent, pseudo); |
|
7548 } |
|
7549 |
|
7550 /* static */ |
|
7551 nsIFrame* |
|
7552 nsFrame::CorrectStyleParentFrame(nsIFrame* aProspectiveParent, |
|
7553 nsIAtom* aChildPseudo) |
|
7554 { |
|
7555 NS_PRECONDITION(aProspectiveParent, "Must have a prospective parent"); |
|
7556 |
|
7557 // Anon boxes are parented to their actual parent already, except |
|
7558 // for non-elements. Those should not be treated as an anon box. |
|
7559 if (aChildPseudo && aChildPseudo != nsCSSAnonBoxes::mozNonElement && |
|
7560 nsCSSAnonBoxes::IsAnonBox(aChildPseudo)) { |
|
7561 NS_ASSERTION(aChildPseudo != nsCSSAnonBoxes::mozAnonymousBlock && |
|
7562 aChildPseudo != nsCSSAnonBoxes::mozAnonymousPositionedBlock, |
|
7563 "Should have dealt with kids that have " |
|
7564 "NS_FRAME_PART_OF_IBSPLIT elsewhere"); |
|
7565 return aProspectiveParent; |
|
7566 } |
|
7567 |
|
7568 // Otherwise, walk up out of all anon boxes. For placeholder frames, walk out |
|
7569 // of all pseudo-elements as well. Otherwise ReparentStyleContext could cause |
|
7570 // style data to be out of sync with the frame tree. |
|
7571 nsIFrame* parent = aProspectiveParent; |
|
7572 do { |
|
7573 if (parent->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) { |
|
7574 nsIFrame* sibling = GetIBSplitSiblingForAnonymousBlock(parent); |
|
7575 |
|
7576 if (sibling) { |
|
7577 // |parent| was a block in an {ib} split; use the inline as |
|
7578 // |the style parent. |
|
7579 parent = sibling; |
|
7580 } |
|
7581 } |
|
7582 |
|
7583 nsIAtom* parentPseudo = parent->StyleContext()->GetPseudo(); |
|
7584 if (!parentPseudo || |
|
7585 (!nsCSSAnonBoxes::IsAnonBox(parentPseudo) && |
|
7586 // nsPlaceholderFrame pases in nsGkAtoms::placeholderFrame for |
|
7587 // aChildPseudo (even though that's not a valid pseudo-type) just to |
|
7588 // trigger this behavior of walking up to the nearest non-pseudo |
|
7589 // ancestor. |
|
7590 aChildPseudo != nsGkAtoms::placeholderFrame)) { |
|
7591 return parent; |
|
7592 } |
|
7593 |
|
7594 parent = parent->GetParent(); |
|
7595 } while (parent); |
|
7596 |
|
7597 if (aProspectiveParent->StyleContext()->GetPseudo() == |
|
7598 nsCSSAnonBoxes::viewportScroll) { |
|
7599 // aProspectiveParent is the scrollframe for a viewport |
|
7600 // and the kids are the anonymous scrollbars |
|
7601 return aProspectiveParent; |
|
7602 } |
|
7603 |
|
7604 // We can get here if the root element is absolutely positioned. |
|
7605 // We can't test for this very accurately, but it can only happen |
|
7606 // when the prospective parent is a canvas frame. |
|
7607 NS_ASSERTION(aProspectiveParent->GetType() == nsGkAtoms::canvasFrame, |
|
7608 "Should have found a parent before this"); |
|
7609 return nullptr; |
|
7610 } |
|
7611 |
|
7612 nsIFrame* |
|
7613 nsFrame::DoGetParentStyleContextFrame() const |
|
7614 { |
|
7615 if (mContent && !mContent->GetParent() && |
|
7616 !StyleContext()->GetPseudo()) { |
|
7617 // we're a frame for the root. We have no style context parent. |
|
7618 return nullptr; |
|
7619 } |
|
7620 |
|
7621 if (!(mState & NS_FRAME_OUT_OF_FLOW)) { |
|
7622 /* |
|
7623 * If this frame is an anonymous block created when an inline with a block |
|
7624 * inside it got split, then the parent style context is on its preceding |
|
7625 * inline. We can get to it using GetIBSplitSiblingForAnonymousBlock. |
|
7626 */ |
|
7627 if (mState & NS_FRAME_PART_OF_IBSPLIT) { |
|
7628 nsIFrame* ibSplitSibling = GetIBSplitSiblingForAnonymousBlock(this); |
|
7629 if (ibSplitSibling) { |
|
7630 return ibSplitSibling; |
|
7631 } |
|
7632 } |
|
7633 |
|
7634 // If this frame is one of the blocks that split an inline, we must |
|
7635 // return the "special" inline parent, i.e., the parent that this |
|
7636 // frame would have if we didn't mangle the frame structure. |
|
7637 return GetCorrectedParent(this); |
|
7638 } |
|
7639 |
|
7640 // We're an out-of-flow frame. For out-of-flow frames, we must |
|
7641 // resolve underneath the placeholder's parent. The placeholder is |
|
7642 // reached from the first-in-flow. |
|
7643 nsIFrame* placeholder = PresContext()->FrameManager()-> |
|
7644 GetPlaceholderFrameFor(FirstInFlow()); |
|
7645 if (!placeholder) { |
|
7646 NS_NOTREACHED("no placeholder frame for out-of-flow frame"); |
|
7647 return GetCorrectedParent(this); |
|
7648 } |
|
7649 return placeholder->GetParentStyleContextFrame(); |
|
7650 } |
|
7651 |
|
7652 void |
|
7653 nsFrame::GetLastLeaf(nsPresContext* aPresContext, nsIFrame **aFrame) |
|
7654 { |
|
7655 if (!aFrame || !*aFrame) |
|
7656 return; |
|
7657 nsIFrame *child = *aFrame; |
|
7658 //if we are a block frame then go for the last line of 'this' |
|
7659 while (1){ |
|
7660 child = child->GetFirstPrincipalChild(); |
|
7661 if (!child) |
|
7662 return;//nothing to do |
|
7663 nsIFrame* siblingFrame; |
|
7664 nsIContent* content; |
|
7665 //ignore anonymous elements, e.g. mozTableAdd* mozTableRemove* |
|
7666 //see bug 278197 comment #12 #13 for details |
|
7667 while ((siblingFrame = child->GetNextSibling()) && |
|
7668 (content = siblingFrame->GetContent()) && |
|
7669 !content->IsRootOfNativeAnonymousSubtree()) |
|
7670 child = siblingFrame; |
|
7671 *aFrame = child; |
|
7672 } |
|
7673 } |
|
7674 |
|
7675 void |
|
7676 nsFrame::GetFirstLeaf(nsPresContext* aPresContext, nsIFrame **aFrame) |
|
7677 { |
|
7678 if (!aFrame || !*aFrame) |
|
7679 return; |
|
7680 nsIFrame *child = *aFrame; |
|
7681 while (1){ |
|
7682 child = child->GetFirstPrincipalChild(); |
|
7683 if (!child) |
|
7684 return;//nothing to do |
|
7685 *aFrame = child; |
|
7686 } |
|
7687 } |
|
7688 |
|
7689 /* virtual */ bool |
|
7690 nsIFrame::IsFocusable(int32_t *aTabIndex, bool aWithMouse) |
|
7691 { |
|
7692 int32_t tabIndex = -1; |
|
7693 if (aTabIndex) { |
|
7694 *aTabIndex = -1; // Default for early return is not focusable |
|
7695 } |
|
7696 bool isFocusable = false; |
|
7697 |
|
7698 if (mContent && mContent->IsElement() && IsVisibleConsideringAncestors()) { |
|
7699 const nsStyleUserInterface* ui = StyleUserInterface(); |
|
7700 if (ui->mUserFocus != NS_STYLE_USER_FOCUS_IGNORE && |
|
7701 ui->mUserFocus != NS_STYLE_USER_FOCUS_NONE) { |
|
7702 // Pass in default tabindex of -1 for nonfocusable and 0 for focusable |
|
7703 tabIndex = 0; |
|
7704 } |
|
7705 isFocusable = mContent->IsFocusable(&tabIndex, aWithMouse); |
|
7706 if (!isFocusable && !aWithMouse && |
|
7707 GetType() == nsGkAtoms::scrollFrame && |
|
7708 mContent->IsHTML() && |
|
7709 !mContent->IsRootOfNativeAnonymousSubtree() && |
|
7710 mContent->GetParent() && |
|
7711 !mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) { |
|
7712 // Elements with scrollable view are focusable with script & tabbable |
|
7713 // Otherwise you couldn't scroll them with keyboard, which is |
|
7714 // an accessibility issue (e.g. Section 508 rules) |
|
7715 // However, we don't make them to be focusable with the mouse, |
|
7716 // because the extra focus outlines are considered unnecessarily ugly. |
|
7717 // When clicked on, the selection position within the element |
|
7718 // will be enough to make them keyboard scrollable. |
|
7719 nsIScrollableFrame *scrollFrame = do_QueryFrame(this); |
|
7720 if (scrollFrame && |
|
7721 scrollFrame->GetScrollbarStyles() != ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, NS_STYLE_OVERFLOW_HIDDEN) && |
|
7722 !scrollFrame->GetScrollRange().IsEqualEdges(nsRect(0, 0, 0, 0))) { |
|
7723 // Scroll bars will be used for overflow |
|
7724 isFocusable = true; |
|
7725 tabIndex = 0; |
|
7726 } |
|
7727 } |
|
7728 } |
|
7729 |
|
7730 if (aTabIndex) { |
|
7731 *aTabIndex = tabIndex; |
|
7732 } |
|
7733 return isFocusable; |
|
7734 } |
|
7735 |
|
7736 /** |
|
7737 * @return true if this text frame ends with a newline character which is |
|
7738 * treated as preformatted. It should return false if this is not a text frame. |
|
7739 */ |
|
7740 bool |
|
7741 nsIFrame::HasSignificantTerminalNewline() const |
|
7742 { |
|
7743 return false; |
|
7744 } |
|
7745 |
|
7746 static uint8_t |
|
7747 ConvertSVGDominantBaselineToVerticalAlign(uint8_t aDominantBaseline) |
|
7748 { |
|
7749 // Most of these are approximate mappings. |
|
7750 switch (aDominantBaseline) { |
|
7751 case NS_STYLE_DOMINANT_BASELINE_HANGING: |
|
7752 case NS_STYLE_DOMINANT_BASELINE_TEXT_BEFORE_EDGE: |
|
7753 return NS_STYLE_VERTICAL_ALIGN_TEXT_TOP; |
|
7754 case NS_STYLE_DOMINANT_BASELINE_TEXT_AFTER_EDGE: |
|
7755 case NS_STYLE_DOMINANT_BASELINE_IDEOGRAPHIC: |
|
7756 return NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM; |
|
7757 case NS_STYLE_DOMINANT_BASELINE_CENTRAL: |
|
7758 case NS_STYLE_DOMINANT_BASELINE_MIDDLE: |
|
7759 case NS_STYLE_DOMINANT_BASELINE_MATHEMATICAL: |
|
7760 return NS_STYLE_VERTICAL_ALIGN_MIDDLE; |
|
7761 case NS_STYLE_DOMINANT_BASELINE_AUTO: |
|
7762 case NS_STYLE_DOMINANT_BASELINE_ALPHABETIC: |
|
7763 return NS_STYLE_VERTICAL_ALIGN_BASELINE; |
|
7764 case NS_STYLE_DOMINANT_BASELINE_USE_SCRIPT: |
|
7765 case NS_STYLE_DOMINANT_BASELINE_NO_CHANGE: |
|
7766 case NS_STYLE_DOMINANT_BASELINE_RESET_SIZE: |
|
7767 // These three should not simply map to 'baseline', but we don't |
|
7768 // support the complex baseline model that SVG 1.1 has and which |
|
7769 // css3-linebox now defines. |
|
7770 return NS_STYLE_VERTICAL_ALIGN_BASELINE; |
|
7771 default: |
|
7772 NS_NOTREACHED("unexpected aDominantBaseline value"); |
|
7773 return NS_STYLE_VERTICAL_ALIGN_BASELINE; |
|
7774 } |
|
7775 } |
|
7776 |
|
7777 uint8_t |
|
7778 nsIFrame::VerticalAlignEnum() const |
|
7779 { |
|
7780 if (IsSVGText()) { |
|
7781 uint8_t dominantBaseline; |
|
7782 for (const nsIFrame* frame = this; frame; frame = frame->GetParent()) { |
|
7783 dominantBaseline = frame->StyleSVGReset()->mDominantBaseline; |
|
7784 if (dominantBaseline != NS_STYLE_DOMINANT_BASELINE_AUTO || |
|
7785 frame->GetType() == nsGkAtoms::svgTextFrame) { |
|
7786 break; |
|
7787 } |
|
7788 } |
|
7789 return ConvertSVGDominantBaselineToVerticalAlign(dominantBaseline); |
|
7790 } |
|
7791 |
|
7792 const nsStyleCoord& verticalAlign = StyleTextReset()->mVerticalAlign; |
|
7793 if (verticalAlign.GetUnit() == eStyleUnit_Enumerated) { |
|
7794 return verticalAlign.GetIntValue(); |
|
7795 } |
|
7796 |
|
7797 return eInvalidVerticalAlign; |
|
7798 } |
|
7799 |
|
7800 /* static */ |
|
7801 void nsFrame::FillCursorInformationFromStyle(const nsStyleUserInterface* ui, |
|
7802 nsIFrame::Cursor& aCursor) |
|
7803 { |
|
7804 aCursor.mCursor = ui->mCursor; |
|
7805 aCursor.mHaveHotspot = false; |
|
7806 aCursor.mHotspotX = aCursor.mHotspotY = 0.0f; |
|
7807 |
|
7808 for (nsCursorImage *item = ui->mCursorArray, |
|
7809 *item_end = ui->mCursorArray + ui->mCursorArrayLength; |
|
7810 item < item_end; ++item) { |
|
7811 uint32_t status; |
|
7812 nsresult rv = item->GetImage()->GetImageStatus(&status); |
|
7813 if (NS_SUCCEEDED(rv) && (status & imgIRequest::STATUS_LOAD_COMPLETE)) { |
|
7814 // This is the one we want |
|
7815 item->GetImage()->GetImage(getter_AddRefs(aCursor.mContainer)); |
|
7816 aCursor.mHaveHotspot = item->mHaveHotspot; |
|
7817 aCursor.mHotspotX = item->mHotspotX; |
|
7818 aCursor.mHotspotY = item->mHotspotY; |
|
7819 break; |
|
7820 } |
|
7821 } |
|
7822 } |
|
7823 |
|
7824 NS_IMETHODIMP |
|
7825 nsFrame::RefreshSizeCache(nsBoxLayoutState& aState) |
|
7826 { |
|
7827 // XXXbz this comment needs some rewriting to make sense in the |
|
7828 // post-reflow-branch world. |
|
7829 |
|
7830 // Ok we need to compute our minimum, preferred, and maximum sizes. |
|
7831 // 1) Maximum size. This is easy. Its infinite unless it is overloaded by CSS. |
|
7832 // 2) Preferred size. This is a little harder. This is the size the block would be |
|
7833 // if it were laid out on an infinite canvas. So we can get this by reflowing |
|
7834 // the block with and INTRINSIC width and height. We can also do a nice optimization |
|
7835 // for incremental reflow. If the reflow is incremental then we can pass a flag to |
|
7836 // have the block compute the preferred width for us! Preferred height can just be |
|
7837 // the minimum height; |
|
7838 // 3) Minimum size. This is a toughy. We can pass the block a flag asking for the max element |
|
7839 // size. That would give us the width. Unfortunately you can only ask for a maxElementSize |
|
7840 // during an incremental reflow. So on other reflows we will just have to use 0. |
|
7841 // The min height on the other hand is fairly easy we need to get the largest |
|
7842 // line height. This can be done with the line iterator. |
|
7843 |
|
7844 // if we do have a rendering context |
|
7845 nsresult rv = NS_OK; |
|
7846 nsRenderingContext* rendContext = aState.GetRenderingContext(); |
|
7847 if (rendContext) { |
|
7848 nsPresContext* presContext = aState.PresContext(); |
|
7849 |
|
7850 // If we don't have any HTML constraints and it's a resize, then nothing in the block |
|
7851 // could have changed, so no refresh is necessary. |
|
7852 nsBoxLayoutMetrics* metrics = BoxMetrics(); |
|
7853 if (!DoesNeedRecalc(metrics->mBlockPrefSize)) |
|
7854 return NS_OK; |
|
7855 |
|
7856 // the rect we plan to size to. |
|
7857 nsRect rect = GetRect(); |
|
7858 |
|
7859 nsMargin bp(0,0,0,0); |
|
7860 GetBorderAndPadding(bp); |
|
7861 |
|
7862 { |
|
7863 // If we're a container for font size inflation, then shrink |
|
7864 // wrapping inside of us should not apply font size inflation. |
|
7865 AutoMaybeDisableFontInflation an(this); |
|
7866 |
|
7867 metrics->mBlockPrefSize.width = |
|
7868 GetPrefWidth(rendContext) + bp.LeftRight(); |
|
7869 metrics->mBlockMinSize.width = |
|
7870 GetMinWidth(rendContext) + bp.LeftRight(); |
|
7871 } |
|
7872 |
|
7873 // do the nasty. |
|
7874 const WritingMode wm = aState.OuterReflowState() ? |
|
7875 aState.OuterReflowState()->GetWritingMode() : GetWritingMode(); |
|
7876 nsHTMLReflowMetrics desiredSize(wm); |
|
7877 rv = BoxReflow(aState, presContext, desiredSize, rendContext, |
|
7878 rect.x, rect.y, |
|
7879 metrics->mBlockPrefSize.width, NS_UNCONSTRAINEDSIZE); |
|
7880 |
|
7881 metrics->mBlockMinSize.height = 0; |
|
7882 // ok we need the max ascent of the items on the line. So to do this |
|
7883 // ask the block for its line iterator. Get the max ascent. |
|
7884 nsAutoLineIterator lines = GetLineIterator(); |
|
7885 if (lines) |
|
7886 { |
|
7887 metrics->mBlockMinSize.height = 0; |
|
7888 int count = 0; |
|
7889 nsIFrame* firstFrame = nullptr; |
|
7890 int32_t framesOnLine; |
|
7891 nsRect lineBounds; |
|
7892 uint32_t lineFlags; |
|
7893 |
|
7894 do { |
|
7895 lines->GetLine(count, &firstFrame, &framesOnLine, lineBounds, &lineFlags); |
|
7896 |
|
7897 if (lineBounds.height > metrics->mBlockMinSize.height) |
|
7898 metrics->mBlockMinSize.height = lineBounds.height; |
|
7899 |
|
7900 count++; |
|
7901 } while(firstFrame); |
|
7902 } else { |
|
7903 metrics->mBlockMinSize.height = desiredSize.Height(); |
|
7904 } |
|
7905 |
|
7906 metrics->mBlockPrefSize.height = metrics->mBlockMinSize.height; |
|
7907 |
|
7908 if (desiredSize.TopAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) { |
|
7909 if (!nsLayoutUtils::GetFirstLineBaseline(this, &metrics->mBlockAscent)) |
|
7910 metrics->mBlockAscent = GetBaseline(); |
|
7911 } else { |
|
7912 metrics->mBlockAscent = desiredSize.TopAscent(); |
|
7913 } |
|
7914 |
|
7915 #ifdef DEBUG_adaptor |
|
7916 printf("min=(%d,%d), pref=(%d,%d), ascent=%d\n", metrics->mBlockMinSize.width, |
|
7917 metrics->mBlockMinSize.height, |
|
7918 metrics->mBlockPrefSize.width, |
|
7919 metrics->mBlockPrefSize.height, |
|
7920 metrics->mBlockAscent); |
|
7921 #endif |
|
7922 } |
|
7923 |
|
7924 return rv; |
|
7925 } |
|
7926 |
|
7927 /* virtual */ nsILineIterator* |
|
7928 nsFrame::GetLineIterator() |
|
7929 { |
|
7930 return nullptr; |
|
7931 } |
|
7932 |
|
7933 nsSize |
|
7934 nsFrame::GetPrefSize(nsBoxLayoutState& aState) |
|
7935 { |
|
7936 nsSize size(0,0); |
|
7937 DISPLAY_PREF_SIZE(this, size); |
|
7938 // If the size is cached, and there are no HTML constraints that we might |
|
7939 // be depending on, then we just return the cached size. |
|
7940 nsBoxLayoutMetrics *metrics = BoxMetrics(); |
|
7941 if (!DoesNeedRecalc(metrics->mPrefSize)) { |
|
7942 return metrics->mPrefSize; |
|
7943 } |
|
7944 |
|
7945 if (IsCollapsed()) |
|
7946 return size; |
|
7947 |
|
7948 // get our size in CSS. |
|
7949 bool widthSet, heightSet; |
|
7950 bool completelyRedefined = nsIFrame::AddCSSPrefSize(this, size, widthSet, heightSet); |
|
7951 |
|
7952 // Refresh our caches with new sizes. |
|
7953 if (!completelyRedefined) { |
|
7954 RefreshSizeCache(aState); |
|
7955 nsSize blockSize = metrics->mBlockPrefSize; |
|
7956 |
|
7957 // notice we don't need to add our borders or padding |
|
7958 // in. That's because the block did it for us. |
|
7959 if (!widthSet) |
|
7960 size.width = blockSize.width; |
|
7961 if (!heightSet) |
|
7962 size.height = blockSize.height; |
|
7963 } |
|
7964 |
|
7965 metrics->mPrefSize = size; |
|
7966 return size; |
|
7967 } |
|
7968 |
|
7969 nsSize |
|
7970 nsFrame::GetMinSize(nsBoxLayoutState& aState) |
|
7971 { |
|
7972 nsSize size(0,0); |
|
7973 DISPLAY_MIN_SIZE(this, size); |
|
7974 // Don't use the cache if we have HTMLReflowState constraints --- they might have changed |
|
7975 nsBoxLayoutMetrics *metrics = BoxMetrics(); |
|
7976 if (!DoesNeedRecalc(metrics->mMinSize)) { |
|
7977 size = metrics->mMinSize; |
|
7978 return size; |
|
7979 } |
|
7980 |
|
7981 if (IsCollapsed()) |
|
7982 return size; |
|
7983 |
|
7984 // get our size in CSS. |
|
7985 bool widthSet, heightSet; |
|
7986 bool completelyRedefined = |
|
7987 nsIFrame::AddCSSMinSize(aState, this, size, widthSet, heightSet); |
|
7988 |
|
7989 // Refresh our caches with new sizes. |
|
7990 if (!completelyRedefined) { |
|
7991 RefreshSizeCache(aState); |
|
7992 nsSize blockSize = metrics->mBlockMinSize; |
|
7993 |
|
7994 if (!widthSet) |
|
7995 size.width = blockSize.width; |
|
7996 if (!heightSet) |
|
7997 size.height = blockSize.height; |
|
7998 } |
|
7999 |
|
8000 metrics->mMinSize = size; |
|
8001 return size; |
|
8002 } |
|
8003 |
|
8004 nsSize |
|
8005 nsFrame::GetMaxSize(nsBoxLayoutState& aState) |
|
8006 { |
|
8007 nsSize size(NS_INTRINSICSIZE, NS_INTRINSICSIZE); |
|
8008 DISPLAY_MAX_SIZE(this, size); |
|
8009 // Don't use the cache if we have HTMLReflowState constraints --- they might have changed |
|
8010 nsBoxLayoutMetrics *metrics = BoxMetrics(); |
|
8011 if (!DoesNeedRecalc(metrics->mMaxSize)) { |
|
8012 size = metrics->mMaxSize; |
|
8013 return size; |
|
8014 } |
|
8015 |
|
8016 if (IsCollapsed()) |
|
8017 return size; |
|
8018 |
|
8019 size = nsBox::GetMaxSize(aState); |
|
8020 metrics->mMaxSize = size; |
|
8021 |
|
8022 return size; |
|
8023 } |
|
8024 |
|
8025 nscoord |
|
8026 nsFrame::GetFlex(nsBoxLayoutState& aState) |
|
8027 { |
|
8028 nsBoxLayoutMetrics *metrics = BoxMetrics(); |
|
8029 if (!DoesNeedRecalc(metrics->mFlex)) |
|
8030 return metrics->mFlex; |
|
8031 |
|
8032 metrics->mFlex = nsBox::GetFlex(aState); |
|
8033 |
|
8034 return metrics->mFlex; |
|
8035 } |
|
8036 |
|
8037 nscoord |
|
8038 nsFrame::GetBoxAscent(nsBoxLayoutState& aState) |
|
8039 { |
|
8040 nsBoxLayoutMetrics *metrics = BoxMetrics(); |
|
8041 if (!DoesNeedRecalc(metrics->mAscent)) |
|
8042 return metrics->mAscent; |
|
8043 |
|
8044 if (IsCollapsed()) { |
|
8045 metrics->mAscent = 0; |
|
8046 } else { |
|
8047 // Refresh our caches with new sizes. |
|
8048 RefreshSizeCache(aState); |
|
8049 metrics->mAscent = metrics->mBlockAscent; |
|
8050 } |
|
8051 |
|
8052 return metrics->mAscent; |
|
8053 } |
|
8054 |
|
8055 nsresult |
|
8056 nsFrame::DoLayout(nsBoxLayoutState& aState) |
|
8057 { |
|
8058 nsRect ourRect(mRect); |
|
8059 |
|
8060 nsRenderingContext* rendContext = aState.GetRenderingContext(); |
|
8061 nsPresContext* presContext = aState.PresContext(); |
|
8062 const WritingMode wm = aState.OuterReflowState() ? |
|
8063 aState.OuterReflowState()->GetWritingMode() : GetWritingMode(); |
|
8064 nsHTMLReflowMetrics desiredSize(wm); |
|
8065 nsresult rv = NS_OK; |
|
8066 |
|
8067 if (rendContext) { |
|
8068 |
|
8069 rv = BoxReflow(aState, presContext, desiredSize, rendContext, |
|
8070 ourRect.x, ourRect.y, ourRect.width, ourRect.height); |
|
8071 |
|
8072 if (IsCollapsed()) { |
|
8073 SetSize(nsSize(0, 0)); |
|
8074 } else { |
|
8075 |
|
8076 // if our child needs to be bigger. This might happend with |
|
8077 // wrapping text. There is no way to predict its height until we |
|
8078 // reflow it. Now that we know the height reshuffle upward. |
|
8079 if (desiredSize.Width() > ourRect.width || |
|
8080 desiredSize.Height() > ourRect.height) { |
|
8081 |
|
8082 #ifdef DEBUG_GROW |
|
8083 DumpBox(stdout); |
|
8084 printf(" GREW from (%d,%d) -> (%d,%d)\n", |
|
8085 ourRect.width, ourRect.height, |
|
8086 desiredSize.Width(), desiredSize.Height()); |
|
8087 #endif |
|
8088 |
|
8089 if (desiredSize.Width() > ourRect.width) |
|
8090 ourRect.width = desiredSize.Width(); |
|
8091 |
|
8092 if (desiredSize.Height() > ourRect.height) |
|
8093 ourRect.height = desiredSize.Height(); |
|
8094 } |
|
8095 |
|
8096 // ensure our size is what we think is should be. Someone could have |
|
8097 // reset the frame to be smaller or something dumb like that. |
|
8098 SetSize(ourRect.Size()); |
|
8099 } |
|
8100 } |
|
8101 |
|
8102 // Should we do this if IsCollapsed() is true? |
|
8103 nsSize size(GetSize()); |
|
8104 desiredSize.Width() = size.width; |
|
8105 desiredSize.Height() = size.height; |
|
8106 desiredSize.UnionOverflowAreasWithDesiredBounds(); |
|
8107 |
|
8108 if (HasAbsolutelyPositionedChildren()) { |
|
8109 // Set up a |reflowState| to pass into ReflowAbsoluteFrames |
|
8110 nsHTMLReflowState reflowState(aState.PresContext(), this, |
|
8111 aState.GetRenderingContext(), |
|
8112 nsSize(size.width, NS_UNCONSTRAINEDSIZE), |
|
8113 nsHTMLReflowState::DUMMY_PARENT_REFLOW_STATE); |
|
8114 |
|
8115 AddStateBits(NS_FRAME_IN_REFLOW); |
|
8116 // Set up a |reflowStatus| to pass into ReflowAbsoluteFrames |
|
8117 // (just a dummy value; hopefully that's OK) |
|
8118 nsReflowStatus reflowStatus = NS_FRAME_COMPLETE; |
|
8119 ReflowAbsoluteFrames(aState.PresContext(), desiredSize, |
|
8120 reflowState, reflowStatus); |
|
8121 RemoveStateBits(NS_FRAME_IN_REFLOW); |
|
8122 } |
|
8123 |
|
8124 nsSize oldSize(ourRect.Size()); |
|
8125 FinishAndStoreOverflow(desiredSize.mOverflowAreas, size, &oldSize); |
|
8126 |
|
8127 SyncLayout(aState); |
|
8128 |
|
8129 return rv; |
|
8130 } |
|
8131 |
|
8132 nsresult |
|
8133 nsFrame::BoxReflow(nsBoxLayoutState& aState, |
|
8134 nsPresContext* aPresContext, |
|
8135 nsHTMLReflowMetrics& aDesiredSize, |
|
8136 nsRenderingContext* aRenderingContext, |
|
8137 nscoord aX, |
|
8138 nscoord aY, |
|
8139 nscoord aWidth, |
|
8140 nscoord aHeight, |
|
8141 bool aMoveFrame) |
|
8142 { |
|
8143 DO_GLOBAL_REFLOW_COUNT("nsBoxToBlockAdaptor"); |
|
8144 |
|
8145 #ifdef DEBUG_REFLOW |
|
8146 nsAdaptorAddIndents(); |
|
8147 printf("Reflowing: "); |
|
8148 nsFrame::ListTag(stdout, mFrame); |
|
8149 printf("\n"); |
|
8150 gIndent2++; |
|
8151 #endif |
|
8152 |
|
8153 nsBoxLayoutMetrics *metrics = BoxMetrics(); |
|
8154 nsReflowStatus status = NS_FRAME_COMPLETE; |
|
8155 |
|
8156 bool needsReflow = NS_SUBTREE_DIRTY(this); |
|
8157 |
|
8158 // if we don't need a reflow then |
|
8159 // lets see if we are already that size. Yes? then don't even reflow. We are done. |
|
8160 if (!needsReflow) { |
|
8161 |
|
8162 if (aWidth != NS_INTRINSICSIZE && aHeight != NS_INTRINSICSIZE) { |
|
8163 |
|
8164 // if the new calculated size has a 0 width or a 0 height |
|
8165 if ((metrics->mLastSize.width == 0 || metrics->mLastSize.height == 0) && (aWidth == 0 || aHeight == 0)) { |
|
8166 needsReflow = false; |
|
8167 aDesiredSize.Width() = aWidth; |
|
8168 aDesiredSize.Height() = aHeight; |
|
8169 SetSize(nsSize(aDesiredSize.Width(), aDesiredSize.Height())); |
|
8170 } else { |
|
8171 aDesiredSize.Width() = metrics->mLastSize.width; |
|
8172 aDesiredSize.Height() = metrics->mLastSize.height; |
|
8173 |
|
8174 // remove the margin. The rect of our child does not include it but our calculated size does. |
|
8175 // don't reflow if we are already the right size |
|
8176 if (metrics->mLastSize.width == aWidth && metrics->mLastSize.height == aHeight) |
|
8177 needsReflow = false; |
|
8178 else |
|
8179 needsReflow = true; |
|
8180 |
|
8181 } |
|
8182 } else { |
|
8183 // if the width or height are intrinsic alway reflow because |
|
8184 // we don't know what it should be. |
|
8185 needsReflow = true; |
|
8186 } |
|
8187 } |
|
8188 |
|
8189 // ok now reflow the child into the spacers calculated space |
|
8190 if (needsReflow) { |
|
8191 |
|
8192 aDesiredSize.Width() = 0; |
|
8193 aDesiredSize.Height() = 0; |
|
8194 |
|
8195 // create a reflow state to tell our child to flow at the given size. |
|
8196 |
|
8197 // Construct a bogus parent reflow state so that there's a usable |
|
8198 // containing block reflow state. |
|
8199 nsMargin margin(0,0,0,0); |
|
8200 GetMargin(margin); |
|
8201 |
|
8202 nsSize parentSize(aWidth, aHeight); |
|
8203 if (parentSize.height != NS_INTRINSICSIZE) |
|
8204 parentSize.height += margin.TopBottom(); |
|
8205 if (parentSize.width != NS_INTRINSICSIZE) |
|
8206 parentSize.width += margin.LeftRight(); |
|
8207 |
|
8208 nsIFrame *parentFrame = GetParent(); |
|
8209 nsFrameState savedState = parentFrame->GetStateBits(); |
|
8210 nsHTMLReflowState parentReflowState(aPresContext, parentFrame, |
|
8211 aRenderingContext, |
|
8212 parentSize, |
|
8213 nsHTMLReflowState::DUMMY_PARENT_REFLOW_STATE); |
|
8214 parentFrame->RemoveStateBits(~nsFrameState(0)); |
|
8215 parentFrame->AddStateBits(savedState); |
|
8216 |
|
8217 // This may not do very much useful, but it's probably worth trying. |
|
8218 if (parentSize.width != NS_INTRINSICSIZE) |
|
8219 parentReflowState.SetComputedWidth(std::max(parentSize.width, 0)); |
|
8220 if (parentSize.height != NS_INTRINSICSIZE) |
|
8221 parentReflowState.SetComputedHeight(std::max(parentSize.height, 0)); |
|
8222 parentReflowState.ComputedPhysicalMargin().SizeTo(0, 0, 0, 0); |
|
8223 // XXX use box methods |
|
8224 parentFrame->GetPadding(parentReflowState.ComputedPhysicalPadding()); |
|
8225 parentFrame->GetBorder(parentReflowState.ComputedPhysicalBorderPadding()); |
|
8226 parentReflowState.ComputedPhysicalBorderPadding() += |
|
8227 parentReflowState.ComputedPhysicalPadding(); |
|
8228 |
|
8229 // Construct the parent chain manually since constructing it normally |
|
8230 // messes up dimensions. |
|
8231 const nsHTMLReflowState *outerReflowState = aState.OuterReflowState(); |
|
8232 NS_ASSERTION(!outerReflowState || outerReflowState->frame != this, |
|
8233 "in and out of XUL on a single frame?"); |
|
8234 const nsHTMLReflowState* parentRS; |
|
8235 if (outerReflowState && outerReflowState->frame == parentFrame) { |
|
8236 // We're a frame (such as a text control frame) that jumps into |
|
8237 // box reflow and then straight out of it on the child frame. |
|
8238 // This means we actually have a real parent reflow state. |
|
8239 // nsLayoutUtils::InflationMinFontSizeFor used to need this to be |
|
8240 // linked up correctly for text control frames, so do so here). |
|
8241 parentRS = outerReflowState; |
|
8242 } else { |
|
8243 parentRS = &parentReflowState; |
|
8244 } |
|
8245 |
|
8246 // XXX Is it OK that this reflow state has only one ancestor? |
|
8247 // (It used to have a bogus parent, skipping all the boxes). |
|
8248 nsSize availSize(aWidth, NS_INTRINSICSIZE); |
|
8249 nsHTMLReflowState reflowState(aPresContext, *parentRS, this, |
|
8250 availSize, -1, -1, |
|
8251 nsHTMLReflowState::DUMMY_PARENT_REFLOW_STATE); |
|
8252 |
|
8253 // XXX_jwir3: This is somewhat fishy. If this is actually changing the value |
|
8254 // here (which it might be), then we should make sure that it's |
|
8255 // correct the first time around, rather than changing it later. |
|
8256 reflowState.mCBReflowState = parentRS; |
|
8257 |
|
8258 reflowState.mReflowDepth = aState.GetReflowDepth(); |
|
8259 |
|
8260 // mComputedWidth and mComputedHeight are content-box, not |
|
8261 // border-box |
|
8262 if (aWidth != NS_INTRINSICSIZE) { |
|
8263 nscoord computedWidth = |
|
8264 aWidth - reflowState.ComputedPhysicalBorderPadding().LeftRight(); |
|
8265 computedWidth = std::max(computedWidth, 0); |
|
8266 reflowState.SetComputedWidth(computedWidth); |
|
8267 } |
|
8268 |
|
8269 // Most child frames of box frames (e.g. subdocument or scroll frames) |
|
8270 // need to be constrained to the provided size and overflow as necessary. |
|
8271 // The one exception are block frames, because we need to know their |
|
8272 // natural height excluding any overflow area which may be caused by |
|
8273 // various CSS effects such as shadow or outline. |
|
8274 if (!IsFrameOfType(eBlockFrame)) { |
|
8275 if (aHeight != NS_INTRINSICSIZE) { |
|
8276 nscoord computedHeight = |
|
8277 aHeight - reflowState.ComputedPhysicalBorderPadding().TopBottom(); |
|
8278 computedHeight = std::max(computedHeight, 0); |
|
8279 reflowState.SetComputedHeight(computedHeight); |
|
8280 } else { |
|
8281 reflowState.SetComputedHeight( |
|
8282 ComputeSize(aRenderingContext, availSize, availSize.width, |
|
8283 nsSize(reflowState.ComputedPhysicalMargin().LeftRight(), |
|
8284 reflowState.ComputedPhysicalMargin().TopBottom()), |
|
8285 nsSize(reflowState.ComputedPhysicalBorderPadding().LeftRight() - |
|
8286 reflowState.ComputedPhysicalPadding().LeftRight(), |
|
8287 reflowState.ComputedPhysicalBorderPadding().TopBottom() - |
|
8288 reflowState.ComputedPhysicalPadding().TopBottom()), |
|
8289 nsSize(reflowState.ComputedPhysicalPadding().LeftRight(), |
|
8290 reflowState.ComputedPhysicalPadding().TopBottom()), |
|
8291 false).height |
|
8292 ); |
|
8293 } |
|
8294 } |
|
8295 |
|
8296 // Box layout calls SetRect before Layout, whereas non-box layout |
|
8297 // calls SetRect after Reflow. |
|
8298 // XXX Perhaps we should be doing this by twiddling the rect back to |
|
8299 // mLastSize before calling Reflow and then switching it back, but |
|
8300 // However, mLastSize can also be the size passed to BoxReflow by |
|
8301 // RefreshSizeCache, so that doesn't really make sense. |
|
8302 if (metrics->mLastSize.width != aWidth) { |
|
8303 reflowState.mFlags.mHResize = true; |
|
8304 |
|
8305 // When font size inflation is enabled, a horizontal resize |
|
8306 // requires a full reflow. See nsHTMLReflowState::InitResizeFlags |
|
8307 // for more details. |
|
8308 if (nsLayoutUtils::FontSizeInflationEnabled(aPresContext)) { |
|
8309 AddStateBits(NS_FRAME_IS_DIRTY); |
|
8310 } |
|
8311 } |
|
8312 if (metrics->mLastSize.height != aHeight) |
|
8313 reflowState.mFlags.mVResize = true; |
|
8314 |
|
8315 #ifdef DEBUG_REFLOW |
|
8316 nsAdaptorAddIndents(); |
|
8317 printf("Size=(%d,%d)\n",reflowState.ComputedWidth(), |
|
8318 reflowState.ComputedHeight()); |
|
8319 nsAdaptorAddIndents(); |
|
8320 nsAdaptorPrintReason(reflowState); |
|
8321 printf("\n"); |
|
8322 #endif |
|
8323 |
|
8324 // place the child and reflow |
|
8325 WillReflow(aPresContext); |
|
8326 |
|
8327 Reflow(aPresContext, aDesiredSize, reflowState, status); |
|
8328 |
|
8329 NS_ASSERTION(NS_FRAME_IS_COMPLETE(status), "bad status"); |
|
8330 |
|
8331 uint32_t layoutFlags = aState.LayoutFlags(); |
|
8332 nsContainerFrame::FinishReflowChild(this, aPresContext, aDesiredSize, |
|
8333 &reflowState, aX, aY, layoutFlags | NS_FRAME_NO_MOVE_FRAME); |
|
8334 |
|
8335 // Save the ascent. (bug 103925) |
|
8336 if (IsCollapsed()) { |
|
8337 metrics->mAscent = 0; |
|
8338 } else { |
|
8339 if (aDesiredSize.TopAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) { |
|
8340 if (!nsLayoutUtils::GetFirstLineBaseline(this, &metrics->mAscent)) |
|
8341 metrics->mAscent = GetBaseline(); |
|
8342 } else |
|
8343 metrics->mAscent = aDesiredSize.TopAscent(); |
|
8344 } |
|
8345 |
|
8346 } else { |
|
8347 aDesiredSize.SetTopAscent(metrics->mBlockAscent); |
|
8348 } |
|
8349 |
|
8350 #ifdef DEBUG_REFLOW |
|
8351 if (aHeight != NS_INTRINSICSIZE && aDesiredSize.Height() != aHeight) |
|
8352 { |
|
8353 nsAdaptorAddIndents(); |
|
8354 printf("*****got taller!*****\n"); |
|
8355 |
|
8356 } |
|
8357 if (aWidth != NS_INTRINSICSIZE && aDesiredSize.Width() != aWidth) |
|
8358 { |
|
8359 nsAdaptorAddIndents(); |
|
8360 printf("*****got wider!******\n"); |
|
8361 |
|
8362 } |
|
8363 #endif |
|
8364 |
|
8365 if (aWidth == NS_INTRINSICSIZE) |
|
8366 aWidth = aDesiredSize.Width(); |
|
8367 |
|
8368 if (aHeight == NS_INTRINSICSIZE) |
|
8369 aHeight = aDesiredSize.Height(); |
|
8370 |
|
8371 metrics->mLastSize.width = aDesiredSize.Width(); |
|
8372 metrics->mLastSize.height = aDesiredSize.Height(); |
|
8373 |
|
8374 #ifdef DEBUG_REFLOW |
|
8375 gIndent2--; |
|
8376 #endif |
|
8377 |
|
8378 return NS_OK; |
|
8379 } |
|
8380 |
|
8381 static void |
|
8382 DestroyBoxMetrics(void* aPropertyValue) |
|
8383 { |
|
8384 delete static_cast<nsBoxLayoutMetrics*>(aPropertyValue); |
|
8385 } |
|
8386 |
|
8387 NS_DECLARE_FRAME_PROPERTY(BoxMetricsProperty, DestroyBoxMetrics) |
|
8388 |
|
8389 nsBoxLayoutMetrics* |
|
8390 nsFrame::BoxMetrics() const |
|
8391 { |
|
8392 nsBoxLayoutMetrics* metrics = |
|
8393 static_cast<nsBoxLayoutMetrics*>(Properties().Get(BoxMetricsProperty())); |
|
8394 NS_ASSERTION(metrics, "A box layout method was called but InitBoxMetrics was never called"); |
|
8395 return metrics; |
|
8396 } |
|
8397 |
|
8398 /* static */ void |
|
8399 nsIFrame::AddInPopupStateBitToDescendants(nsIFrame* aFrame) |
|
8400 { |
|
8401 aFrame->AddStateBits(NS_FRAME_IN_POPUP); |
|
8402 |
|
8403 nsAutoTArray<nsIFrame::ChildList,4> childListArray; |
|
8404 aFrame->GetCrossDocChildLists(&childListArray); |
|
8405 |
|
8406 nsIFrame::ChildListArrayIterator lists(childListArray); |
|
8407 for (; !lists.IsDone(); lists.Next()) { |
|
8408 nsFrameList::Enumerator childFrames(lists.CurrentList()); |
|
8409 for (; !childFrames.AtEnd(); childFrames.Next()) { |
|
8410 AddInPopupStateBitToDescendants(childFrames.get()); |
|
8411 } |
|
8412 } |
|
8413 } |
|
8414 |
|
8415 /* static */ void |
|
8416 nsIFrame::RemoveInPopupStateBitFromDescendants(nsIFrame* aFrame) |
|
8417 { |
|
8418 if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) || |
|
8419 nsLayoutUtils::IsPopup(aFrame)) { |
|
8420 return; |
|
8421 } |
|
8422 |
|
8423 aFrame->RemoveStateBits(NS_FRAME_IN_POPUP); |
|
8424 |
|
8425 nsAutoTArray<nsIFrame::ChildList,4> childListArray; |
|
8426 aFrame->GetCrossDocChildLists(&childListArray); |
|
8427 |
|
8428 nsIFrame::ChildListArrayIterator lists(childListArray); |
|
8429 for (; !lists.IsDone(); lists.Next()) { |
|
8430 nsFrameList::Enumerator childFrames(lists.CurrentList()); |
|
8431 for (; !childFrames.AtEnd(); childFrames.Next()) { |
|
8432 RemoveInPopupStateBitFromDescendants(childFrames.get()); |
|
8433 } |
|
8434 } |
|
8435 } |
|
8436 |
|
8437 void |
|
8438 nsFrame::SetParent(nsIFrame* aParent) |
|
8439 { |
|
8440 bool wasBoxWrapped = IsBoxWrapped(); |
|
8441 mParent = aParent; |
|
8442 if (!wasBoxWrapped && IsBoxWrapped()) { |
|
8443 InitBoxMetrics(true); |
|
8444 } else if (wasBoxWrapped && !IsBoxWrapped()) { |
|
8445 Properties().Delete(BoxMetricsProperty()); |
|
8446 } |
|
8447 |
|
8448 if (GetStateBits() & (NS_FRAME_HAS_VIEW | NS_FRAME_HAS_CHILD_WITH_VIEW)) { |
|
8449 for (nsIFrame* f = aParent; |
|
8450 f && !(f->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW); |
|
8451 f = f->GetParent()) { |
|
8452 f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW); |
|
8453 } |
|
8454 } |
|
8455 |
|
8456 if (HasInvalidFrameInSubtree()) { |
|
8457 for (nsIFrame* f = aParent; |
|
8458 f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT); |
|
8459 f = nsLayoutUtils::GetCrossDocParentFrame(f)) { |
|
8460 f->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT); |
|
8461 } |
|
8462 } |
|
8463 |
|
8464 if (aParent->HasAnyStateBits(NS_FRAME_IN_POPUP)) { |
|
8465 AddInPopupStateBitToDescendants(this); |
|
8466 } else { |
|
8467 RemoveInPopupStateBitFromDescendants(this); |
|
8468 } |
|
8469 |
|
8470 // If our new parent only has invalid children, then we just invalidate |
|
8471 // ourselves too. This is probably faster than clearing the flag all |
|
8472 // the way up the frame tree. |
|
8473 if (aParent->HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT)) { |
|
8474 InvalidateFrame(); |
|
8475 } |
|
8476 } |
|
8477 |
|
8478 void |
|
8479 nsFrame::InitBoxMetrics(bool aClear) |
|
8480 { |
|
8481 FrameProperties props = Properties(); |
|
8482 if (aClear) { |
|
8483 props.Delete(BoxMetricsProperty()); |
|
8484 } |
|
8485 |
|
8486 nsBoxLayoutMetrics *metrics = new nsBoxLayoutMetrics(); |
|
8487 props.Set(BoxMetricsProperty(), metrics); |
|
8488 |
|
8489 nsFrame::MarkIntrinsicWidthsDirty(); |
|
8490 metrics->mBlockAscent = 0; |
|
8491 metrics->mLastSize.SizeTo(0, 0); |
|
8492 } |
|
8493 |
|
8494 void |
|
8495 nsIFrame::CreateOwnLayerIfNeeded(nsDisplayListBuilder* aBuilder, |
|
8496 nsDisplayList* aList) |
|
8497 { |
|
8498 if (GetContent() && |
|
8499 GetContent()->IsXUL() && |
|
8500 GetContent()->HasAttr(kNameSpaceID_None, nsGkAtoms::layer)) { |
|
8501 aList->AppendNewToTop(new (aBuilder) |
|
8502 nsDisplayOwnLayer(aBuilder, this, aList)); |
|
8503 } |
|
8504 } |
|
8505 |
|
8506 bool |
|
8507 nsIFrame::IsSelected() const |
|
8508 { |
|
8509 return (GetContent() && GetContent()->IsSelectionDescendant()) ? |
|
8510 IsFrameSelected() : false; |
|
8511 } |
|
8512 |
|
8513 void |
|
8514 nsIFrame::DestroySurface(void* aPropertyValue) |
|
8515 { |
|
8516 static_cast<gfxASurface*>(aPropertyValue)->Release(); |
|
8517 } |
|
8518 |
|
8519 void |
|
8520 nsIFrame::DestroyDT(void* aPropertyValue) |
|
8521 { |
|
8522 static_cast<mozilla::gfx::DrawTarget*>(aPropertyValue)->Release(); |
|
8523 } |
|
8524 |
|
8525 void |
|
8526 nsIFrame::DestroyRegion(void* aPropertyValue) |
|
8527 { |
|
8528 delete static_cast<nsRegion*>(aPropertyValue); |
|
8529 } |
|
8530 |
|
8531 bool |
|
8532 nsIFrame::IsPseudoStackingContextFromStyle() { |
|
8533 const nsStyleDisplay* disp = StyleDisplay(); |
|
8534 // If you change this, also change the computation of pseudoStackingContext |
|
8535 // in BuildDisplayListForChild() |
|
8536 return disp->mOpacity != 1.0f || |
|
8537 disp->IsPositioned(this) || |
|
8538 disp->IsFloating(this) || |
|
8539 (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT); |
|
8540 } |
|
8541 |
|
8542 Element* |
|
8543 nsIFrame::GetPseudoElement(nsCSSPseudoElements::Type aType) |
|
8544 { |
|
8545 nsIFrame* frame = nullptr; |
|
8546 |
|
8547 if (aType == nsCSSPseudoElements::ePseudo_before) { |
|
8548 frame = nsLayoutUtils::GetBeforeFrame(this); |
|
8549 } else if (aType == nsCSSPseudoElements::ePseudo_after) { |
|
8550 frame = nsLayoutUtils::GetAfterFrame(this); |
|
8551 } |
|
8552 |
|
8553 if (frame) { |
|
8554 nsIContent* content = frame->GetContent(); |
|
8555 if (content->IsElement()) { |
|
8556 return content->AsElement(); |
|
8557 } |
|
8558 } |
|
8559 |
|
8560 return nullptr; |
|
8561 } |
|
8562 |
|
8563 nsIFrame::ContentOffsets::ContentOffsets() |
|
8564 { |
|
8565 } |
|
8566 |
|
8567 nsIFrame::ContentOffsets::ContentOffsets(const ContentOffsets& rhs) |
|
8568 : content(rhs.content), |
|
8569 offset(rhs.offset), |
|
8570 secondaryOffset(rhs.secondaryOffset), |
|
8571 associateWithNext(rhs.associateWithNext) |
|
8572 { |
|
8573 } |
|
8574 |
|
8575 nsIFrame::ContentOffsets::~ContentOffsets() |
|
8576 { |
|
8577 } |
|
8578 |
|
8579 nsIFrame::CaretPosition::CaretPosition() |
|
8580 : mContentOffset(0) |
|
8581 { |
|
8582 } |
|
8583 |
|
8584 nsIFrame::CaretPosition::~CaretPosition() |
|
8585 { |
|
8586 } |
|
8587 |
|
8588 // Box layout debugging |
|
8589 #ifdef DEBUG_REFLOW |
|
8590 int32_t gIndent2 = 0; |
|
8591 |
|
8592 void |
|
8593 nsAdaptorAddIndents() |
|
8594 { |
|
8595 for(int32_t i=0; i < gIndent2; i++) |
|
8596 { |
|
8597 printf(" "); |
|
8598 } |
|
8599 } |
|
8600 |
|
8601 void |
|
8602 nsAdaptorPrintReason(nsHTMLReflowState& aReflowState) |
|
8603 { |
|
8604 char* reflowReasonString; |
|
8605 |
|
8606 switch(aReflowState.reason) |
|
8607 { |
|
8608 case eReflowReason_Initial: |
|
8609 reflowReasonString = "initial"; |
|
8610 break; |
|
8611 |
|
8612 case eReflowReason_Resize: |
|
8613 reflowReasonString = "resize"; |
|
8614 break; |
|
8615 case eReflowReason_Dirty: |
|
8616 reflowReasonString = "dirty"; |
|
8617 break; |
|
8618 case eReflowReason_StyleChange: |
|
8619 reflowReasonString = "stylechange"; |
|
8620 break; |
|
8621 case eReflowReason_Incremental: |
|
8622 { |
|
8623 switch (aReflowState.reflowCommand->Type()) { |
|
8624 case eReflowType_StyleChanged: |
|
8625 reflowReasonString = "incremental (StyleChanged)"; |
|
8626 break; |
|
8627 case eReflowType_ReflowDirty: |
|
8628 reflowReasonString = "incremental (ReflowDirty)"; |
|
8629 break; |
|
8630 default: |
|
8631 reflowReasonString = "incremental (Unknown)"; |
|
8632 } |
|
8633 } |
|
8634 break; |
|
8635 default: |
|
8636 reflowReasonString = "unknown"; |
|
8637 break; |
|
8638 } |
|
8639 |
|
8640 printf("%s",reflowReasonString); |
|
8641 } |
|
8642 |
|
8643 #endif |
|
8644 #ifdef DEBUG_LAYOUT |
|
8645 void |
|
8646 nsFrame::GetBoxName(nsAutoString& aName) |
|
8647 { |
|
8648 GetFrameName(aName); |
|
8649 } |
|
8650 #endif |
|
8651 |
|
8652 #ifdef DEBUG |
|
8653 static void |
|
8654 GetTagName(nsFrame* aFrame, nsIContent* aContent, int aResultSize, |
|
8655 char* aResult) |
|
8656 { |
|
8657 if (aContent) { |
|
8658 PR_snprintf(aResult, aResultSize, "%s@%p", |
|
8659 nsAtomCString(aContent->Tag()).get(), aFrame); |
|
8660 } |
|
8661 else { |
|
8662 PR_snprintf(aResult, aResultSize, "@%p", aFrame); |
|
8663 } |
|
8664 } |
|
8665 |
|
8666 void |
|
8667 nsFrame::Trace(const char* aMethod, bool aEnter) |
|
8668 { |
|
8669 if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) { |
|
8670 char tagbuf[40]; |
|
8671 GetTagName(this, mContent, sizeof(tagbuf), tagbuf); |
|
8672 PR_LogPrint("%s: %s %s", tagbuf, aEnter ? "enter" : "exit", aMethod); |
|
8673 } |
|
8674 } |
|
8675 |
|
8676 void |
|
8677 nsFrame::Trace(const char* aMethod, bool aEnter, nsReflowStatus aStatus) |
|
8678 { |
|
8679 if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) { |
|
8680 char tagbuf[40]; |
|
8681 GetTagName(this, mContent, sizeof(tagbuf), tagbuf); |
|
8682 PR_LogPrint("%s: %s %s, status=%scomplete%s", |
|
8683 tagbuf, aEnter ? "enter" : "exit", aMethod, |
|
8684 NS_FRAME_IS_NOT_COMPLETE(aStatus) ? "not" : "", |
|
8685 (NS_FRAME_REFLOW_NEXTINFLOW & aStatus) ? "+reflow" : ""); |
|
8686 } |
|
8687 } |
|
8688 |
|
8689 void |
|
8690 nsFrame::TraceMsg(const char* aFormatString, ...) |
|
8691 { |
|
8692 if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) { |
|
8693 // Format arguments into a buffer |
|
8694 char argbuf[200]; |
|
8695 va_list ap; |
|
8696 va_start(ap, aFormatString); |
|
8697 PR_vsnprintf(argbuf, sizeof(argbuf), aFormatString, ap); |
|
8698 va_end(ap); |
|
8699 |
|
8700 char tagbuf[40]; |
|
8701 GetTagName(this, mContent, sizeof(tagbuf), tagbuf); |
|
8702 PR_LogPrint("%s: %s", tagbuf, argbuf); |
|
8703 } |
|
8704 } |
|
8705 |
|
8706 void |
|
8707 nsFrame::VerifyDirtyBitSet(const nsFrameList& aFrameList) |
|
8708 { |
|
8709 for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) { |
|
8710 NS_ASSERTION(e.get()->GetStateBits() & NS_FRAME_IS_DIRTY, |
|
8711 "dirty bit not set"); |
|
8712 } |
|
8713 } |
|
8714 |
|
8715 // Start Display Reflow |
|
8716 #ifdef DEBUG |
|
8717 |
|
8718 DR_cookie::DR_cookie(nsPresContext* aPresContext, |
|
8719 nsIFrame* aFrame, |
|
8720 const nsHTMLReflowState& aReflowState, |
|
8721 nsHTMLReflowMetrics& aMetrics, |
|
8722 nsReflowStatus& aStatus) |
|
8723 :mPresContext(aPresContext), mFrame(aFrame), mReflowState(aReflowState), mMetrics(aMetrics), mStatus(aStatus) |
|
8724 { |
|
8725 MOZ_COUNT_CTOR(DR_cookie); |
|
8726 mValue = nsFrame::DisplayReflowEnter(aPresContext, mFrame, mReflowState); |
|
8727 } |
|
8728 |
|
8729 DR_cookie::~DR_cookie() |
|
8730 { |
|
8731 MOZ_COUNT_DTOR(DR_cookie); |
|
8732 nsFrame::DisplayReflowExit(mPresContext, mFrame, mMetrics, mStatus, mValue); |
|
8733 } |
|
8734 |
|
8735 DR_layout_cookie::DR_layout_cookie(nsIFrame* aFrame) |
|
8736 : mFrame(aFrame) |
|
8737 { |
|
8738 MOZ_COUNT_CTOR(DR_layout_cookie); |
|
8739 mValue = nsFrame::DisplayLayoutEnter(mFrame); |
|
8740 } |
|
8741 |
|
8742 DR_layout_cookie::~DR_layout_cookie() |
|
8743 { |
|
8744 MOZ_COUNT_DTOR(DR_layout_cookie); |
|
8745 nsFrame::DisplayLayoutExit(mFrame, mValue); |
|
8746 } |
|
8747 |
|
8748 DR_intrinsic_width_cookie::DR_intrinsic_width_cookie( |
|
8749 nsIFrame* aFrame, |
|
8750 const char* aType, |
|
8751 nscoord& aResult) |
|
8752 : mFrame(aFrame) |
|
8753 , mType(aType) |
|
8754 , mResult(aResult) |
|
8755 { |
|
8756 MOZ_COUNT_CTOR(DR_intrinsic_width_cookie); |
|
8757 mValue = nsFrame::DisplayIntrinsicWidthEnter(mFrame, mType); |
|
8758 } |
|
8759 |
|
8760 DR_intrinsic_width_cookie::~DR_intrinsic_width_cookie() |
|
8761 { |
|
8762 MOZ_COUNT_DTOR(DR_intrinsic_width_cookie); |
|
8763 nsFrame::DisplayIntrinsicWidthExit(mFrame, mType, mResult, mValue); |
|
8764 } |
|
8765 |
|
8766 DR_intrinsic_size_cookie::DR_intrinsic_size_cookie( |
|
8767 nsIFrame* aFrame, |
|
8768 const char* aType, |
|
8769 nsSize& aResult) |
|
8770 : mFrame(aFrame) |
|
8771 , mType(aType) |
|
8772 , mResult(aResult) |
|
8773 { |
|
8774 MOZ_COUNT_CTOR(DR_intrinsic_size_cookie); |
|
8775 mValue = nsFrame::DisplayIntrinsicSizeEnter(mFrame, mType); |
|
8776 } |
|
8777 |
|
8778 DR_intrinsic_size_cookie::~DR_intrinsic_size_cookie() |
|
8779 { |
|
8780 MOZ_COUNT_DTOR(DR_intrinsic_size_cookie); |
|
8781 nsFrame::DisplayIntrinsicSizeExit(mFrame, mType, mResult, mValue); |
|
8782 } |
|
8783 |
|
8784 DR_init_constraints_cookie::DR_init_constraints_cookie( |
|
8785 nsIFrame* aFrame, |
|
8786 nsHTMLReflowState* aState, |
|
8787 nscoord aCBWidth, |
|
8788 nscoord aCBHeight, |
|
8789 const nsMargin* aMargin, |
|
8790 const nsMargin* aPadding) |
|
8791 : mFrame(aFrame) |
|
8792 , mState(aState) |
|
8793 { |
|
8794 MOZ_COUNT_CTOR(DR_init_constraints_cookie); |
|
8795 mValue = nsHTMLReflowState::DisplayInitConstraintsEnter(mFrame, mState, |
|
8796 aCBWidth, aCBHeight, |
|
8797 aMargin, aPadding); |
|
8798 } |
|
8799 |
|
8800 DR_init_constraints_cookie::~DR_init_constraints_cookie() |
|
8801 { |
|
8802 MOZ_COUNT_DTOR(DR_init_constraints_cookie); |
|
8803 nsHTMLReflowState::DisplayInitConstraintsExit(mFrame, mState, mValue); |
|
8804 } |
|
8805 |
|
8806 DR_init_offsets_cookie::DR_init_offsets_cookie( |
|
8807 nsIFrame* aFrame, |
|
8808 nsCSSOffsetState* aState, |
|
8809 nscoord aHorizontalPercentBasis, |
|
8810 nscoord aVerticalPercentBasis, |
|
8811 const nsMargin* aMargin, |
|
8812 const nsMargin* aPadding) |
|
8813 : mFrame(aFrame) |
|
8814 , mState(aState) |
|
8815 { |
|
8816 MOZ_COUNT_CTOR(DR_init_offsets_cookie); |
|
8817 mValue = nsCSSOffsetState::DisplayInitOffsetsEnter(mFrame, mState, |
|
8818 aHorizontalPercentBasis, |
|
8819 aVerticalPercentBasis, |
|
8820 aMargin, aPadding); |
|
8821 } |
|
8822 |
|
8823 DR_init_offsets_cookie::~DR_init_offsets_cookie() |
|
8824 { |
|
8825 MOZ_COUNT_DTOR(DR_init_offsets_cookie); |
|
8826 nsCSSOffsetState::DisplayInitOffsetsExit(mFrame, mState, mValue); |
|
8827 } |
|
8828 |
|
8829 DR_init_type_cookie::DR_init_type_cookie( |
|
8830 nsIFrame* aFrame, |
|
8831 nsHTMLReflowState* aState) |
|
8832 : mFrame(aFrame) |
|
8833 , mState(aState) |
|
8834 { |
|
8835 MOZ_COUNT_CTOR(DR_init_type_cookie); |
|
8836 mValue = nsHTMLReflowState::DisplayInitFrameTypeEnter(mFrame, mState); |
|
8837 } |
|
8838 |
|
8839 DR_init_type_cookie::~DR_init_type_cookie() |
|
8840 { |
|
8841 MOZ_COUNT_DTOR(DR_init_type_cookie); |
|
8842 nsHTMLReflowState::DisplayInitFrameTypeExit(mFrame, mState, mValue); |
|
8843 } |
|
8844 |
|
8845 struct DR_FrameTypeInfo; |
|
8846 struct DR_FrameTreeNode; |
|
8847 struct DR_Rule; |
|
8848 |
|
8849 struct DR_State |
|
8850 { |
|
8851 DR_State(); |
|
8852 ~DR_State(); |
|
8853 void Init(); |
|
8854 void AddFrameTypeInfo(nsIAtom* aFrameType, |
|
8855 const char* aFrameNameAbbrev, |
|
8856 const char* aFrameName); |
|
8857 DR_FrameTypeInfo* GetFrameTypeInfo(nsIAtom* aFrameType); |
|
8858 DR_FrameTypeInfo* GetFrameTypeInfo(char* aFrameName); |
|
8859 void InitFrameTypeTable(); |
|
8860 DR_FrameTreeNode* CreateTreeNode(nsIFrame* aFrame, |
|
8861 const nsHTMLReflowState* aReflowState); |
|
8862 void FindMatchingRule(DR_FrameTreeNode& aNode); |
|
8863 bool RuleMatches(DR_Rule& aRule, |
|
8864 DR_FrameTreeNode& aNode); |
|
8865 bool GetToken(FILE* aFile, |
|
8866 char* aBuf, |
|
8867 size_t aBufSize); |
|
8868 DR_Rule* ParseRule(FILE* aFile); |
|
8869 void ParseRulesFile(); |
|
8870 void AddRule(nsTArray<DR_Rule*>& aRules, |
|
8871 DR_Rule& aRule); |
|
8872 bool IsWhiteSpace(int c); |
|
8873 bool GetNumber(char* aBuf, |
|
8874 int32_t& aNumber); |
|
8875 void PrettyUC(nscoord aSize, |
|
8876 char* aBuf); |
|
8877 void PrintMargin(const char* tag, const nsMargin* aMargin); |
|
8878 void DisplayFrameTypeInfo(nsIFrame* aFrame, |
|
8879 int32_t aIndent); |
|
8880 void DeleteTreeNode(DR_FrameTreeNode& aNode); |
|
8881 |
|
8882 bool mInited; |
|
8883 bool mActive; |
|
8884 int32_t mCount; |
|
8885 int32_t mAssert; |
|
8886 int32_t mIndent; |
|
8887 bool mIndentUndisplayedFrames; |
|
8888 bool mDisplayPixelErrors; |
|
8889 nsTArray<DR_Rule*> mWildRules; |
|
8890 nsTArray<DR_FrameTypeInfo> mFrameTypeTable; |
|
8891 // reflow specific state |
|
8892 nsTArray<DR_FrameTreeNode*> mFrameTreeLeaves; |
|
8893 }; |
|
8894 |
|
8895 static DR_State *DR_state; // the one and only DR_State |
|
8896 |
|
8897 struct DR_RulePart |
|
8898 { |
|
8899 DR_RulePart(nsIAtom* aFrameType) : mFrameType(aFrameType), mNext(0) {} |
|
8900 void Destroy(); |
|
8901 |
|
8902 nsIAtom* mFrameType; |
|
8903 DR_RulePart* mNext; |
|
8904 }; |
|
8905 |
|
8906 void DR_RulePart::Destroy() |
|
8907 { |
|
8908 if (mNext) { |
|
8909 mNext->Destroy(); |
|
8910 } |
|
8911 delete this; |
|
8912 } |
|
8913 |
|
8914 struct DR_Rule |
|
8915 { |
|
8916 DR_Rule() : mLength(0), mTarget(nullptr), mDisplay(false) { |
|
8917 MOZ_COUNT_CTOR(DR_Rule); |
|
8918 } |
|
8919 ~DR_Rule() { |
|
8920 if (mTarget) mTarget->Destroy(); |
|
8921 MOZ_COUNT_DTOR(DR_Rule); |
|
8922 } |
|
8923 void AddPart(nsIAtom* aFrameType); |
|
8924 |
|
8925 uint32_t mLength; |
|
8926 DR_RulePart* mTarget; |
|
8927 bool mDisplay; |
|
8928 }; |
|
8929 |
|
8930 void DR_Rule::AddPart(nsIAtom* aFrameType) |
|
8931 { |
|
8932 DR_RulePart* newPart = new DR_RulePart(aFrameType); |
|
8933 newPart->mNext = mTarget; |
|
8934 mTarget = newPart; |
|
8935 mLength++; |
|
8936 } |
|
8937 |
|
8938 struct DR_FrameTypeInfo |
|
8939 { |
|
8940 DR_FrameTypeInfo(nsIAtom* aFrmeType, const char* aFrameNameAbbrev, const char* aFrameName); |
|
8941 ~DR_FrameTypeInfo() { |
|
8942 int32_t numElements; |
|
8943 numElements = mRules.Length(); |
|
8944 for (int32_t i = numElements - 1; i >= 0; i--) { |
|
8945 delete mRules.ElementAt(i); |
|
8946 } |
|
8947 } |
|
8948 |
|
8949 nsIAtom* mType; |
|
8950 char mNameAbbrev[16]; |
|
8951 char mName[32]; |
|
8952 nsTArray<DR_Rule*> mRules; |
|
8953 private: |
|
8954 DR_FrameTypeInfo& operator=(const DR_FrameTypeInfo&) MOZ_DELETE; |
|
8955 }; |
|
8956 |
|
8957 DR_FrameTypeInfo::DR_FrameTypeInfo(nsIAtom* aFrameType, |
|
8958 const char* aFrameNameAbbrev, |
|
8959 const char* aFrameName) |
|
8960 { |
|
8961 mType = aFrameType; |
|
8962 PL_strncpyz(mNameAbbrev, aFrameNameAbbrev, sizeof(mNameAbbrev)); |
|
8963 PL_strncpyz(mName, aFrameName, sizeof(mName)); |
|
8964 } |
|
8965 |
|
8966 struct DR_FrameTreeNode |
|
8967 { |
|
8968 DR_FrameTreeNode(nsIFrame* aFrame, DR_FrameTreeNode* aParent) : mFrame(aFrame), mParent(aParent), mDisplay(0), mIndent(0) |
|
8969 { |
|
8970 MOZ_COUNT_CTOR(DR_FrameTreeNode); |
|
8971 } |
|
8972 |
|
8973 ~DR_FrameTreeNode() |
|
8974 { |
|
8975 MOZ_COUNT_DTOR(DR_FrameTreeNode); |
|
8976 } |
|
8977 |
|
8978 nsIFrame* mFrame; |
|
8979 DR_FrameTreeNode* mParent; |
|
8980 bool mDisplay; |
|
8981 uint32_t mIndent; |
|
8982 }; |
|
8983 |
|
8984 // DR_State implementation |
|
8985 |
|
8986 DR_State::DR_State() |
|
8987 : mInited(false), mActive(false), mCount(0), mAssert(-1), mIndent(0), |
|
8988 mIndentUndisplayedFrames(false), mDisplayPixelErrors(false) |
|
8989 { |
|
8990 MOZ_COUNT_CTOR(DR_State); |
|
8991 } |
|
8992 |
|
8993 void DR_State::Init() |
|
8994 { |
|
8995 char* env = PR_GetEnv("GECKO_DISPLAY_REFLOW_ASSERT"); |
|
8996 int32_t num; |
|
8997 if (env) { |
|
8998 if (GetNumber(env, num)) |
|
8999 mAssert = num; |
|
9000 else |
|
9001 printf("GECKO_DISPLAY_REFLOW_ASSERT - invalid value = %s", env); |
|
9002 } |
|
9003 |
|
9004 env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_START"); |
|
9005 if (env) { |
|
9006 if (GetNumber(env, num)) |
|
9007 mIndent = num; |
|
9008 else |
|
9009 printf("GECKO_DISPLAY_REFLOW_INDENT_START - invalid value = %s", env); |
|
9010 } |
|
9011 |
|
9012 env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES"); |
|
9013 if (env) { |
|
9014 if (GetNumber(env, num)) |
|
9015 mIndentUndisplayedFrames = num; |
|
9016 else |
|
9017 printf("GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES - invalid value = %s", env); |
|
9018 } |
|
9019 |
|
9020 env = PR_GetEnv("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS"); |
|
9021 if (env) { |
|
9022 if (GetNumber(env, num)) |
|
9023 mDisplayPixelErrors = num; |
|
9024 else |
|
9025 printf("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS - invalid value = %s", env); |
|
9026 } |
|
9027 |
|
9028 InitFrameTypeTable(); |
|
9029 ParseRulesFile(); |
|
9030 mInited = true; |
|
9031 } |
|
9032 |
|
9033 DR_State::~DR_State() |
|
9034 { |
|
9035 MOZ_COUNT_DTOR(DR_State); |
|
9036 int32_t numElements, i; |
|
9037 numElements = mWildRules.Length(); |
|
9038 for (i = numElements - 1; i >= 0; i--) { |
|
9039 delete mWildRules.ElementAt(i); |
|
9040 } |
|
9041 numElements = mFrameTreeLeaves.Length(); |
|
9042 for (i = numElements - 1; i >= 0; i--) { |
|
9043 delete mFrameTreeLeaves.ElementAt(i); |
|
9044 } |
|
9045 } |
|
9046 |
|
9047 bool DR_State::GetNumber(char* aBuf, |
|
9048 int32_t& aNumber) |
|
9049 { |
|
9050 if (sscanf(aBuf, "%d", &aNumber) > 0) |
|
9051 return true; |
|
9052 else |
|
9053 return false; |
|
9054 } |
|
9055 |
|
9056 bool DR_State::IsWhiteSpace(int c) { |
|
9057 return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r'); |
|
9058 } |
|
9059 |
|
9060 bool DR_State::GetToken(FILE* aFile, |
|
9061 char* aBuf, |
|
9062 size_t aBufSize) |
|
9063 { |
|
9064 bool haveToken = false; |
|
9065 aBuf[0] = 0; |
|
9066 // get the 1st non whitespace char |
|
9067 int c = -1; |
|
9068 for (c = getc(aFile); (c > 0) && IsWhiteSpace(c); c = getc(aFile)) { |
|
9069 } |
|
9070 |
|
9071 if (c > 0) { |
|
9072 haveToken = true; |
|
9073 aBuf[0] = c; |
|
9074 // get everything up to the next whitespace char |
|
9075 size_t cX; |
|
9076 for (cX = 1; cX + 1 < aBufSize ; cX++) { |
|
9077 c = getc(aFile); |
|
9078 if (c < 0) { // EOF |
|
9079 ungetc(' ', aFile); |
|
9080 break; |
|
9081 } |
|
9082 else { |
|
9083 if (IsWhiteSpace(c)) { |
|
9084 break; |
|
9085 } |
|
9086 else { |
|
9087 aBuf[cX] = c; |
|
9088 } |
|
9089 } |
|
9090 } |
|
9091 aBuf[cX] = 0; |
|
9092 } |
|
9093 return haveToken; |
|
9094 } |
|
9095 |
|
9096 DR_Rule* DR_State::ParseRule(FILE* aFile) |
|
9097 { |
|
9098 char buf[128]; |
|
9099 int32_t doDisplay; |
|
9100 DR_Rule* rule = nullptr; |
|
9101 while (GetToken(aFile, buf, sizeof(buf))) { |
|
9102 if (GetNumber(buf, doDisplay)) { |
|
9103 if (rule) { |
|
9104 rule->mDisplay = !!doDisplay; |
|
9105 break; |
|
9106 } |
|
9107 else { |
|
9108 printf("unexpected token - %s \n", buf); |
|
9109 } |
|
9110 } |
|
9111 else { |
|
9112 if (!rule) { |
|
9113 rule = new DR_Rule; |
|
9114 } |
|
9115 if (strcmp(buf, "*") == 0) { |
|
9116 rule->AddPart(nullptr); |
|
9117 } |
|
9118 else { |
|
9119 DR_FrameTypeInfo* info = GetFrameTypeInfo(buf); |
|
9120 if (info) { |
|
9121 rule->AddPart(info->mType); |
|
9122 } |
|
9123 else { |
|
9124 printf("invalid frame type - %s \n", buf); |
|
9125 } |
|
9126 } |
|
9127 } |
|
9128 } |
|
9129 return rule; |
|
9130 } |
|
9131 |
|
9132 void DR_State::AddRule(nsTArray<DR_Rule*>& aRules, |
|
9133 DR_Rule& aRule) |
|
9134 { |
|
9135 int32_t numRules = aRules.Length(); |
|
9136 for (int32_t ruleX = 0; ruleX < numRules; ruleX++) { |
|
9137 DR_Rule* rule = aRules.ElementAt(ruleX); |
|
9138 NS_ASSERTION(rule, "program error"); |
|
9139 if (aRule.mLength > rule->mLength) { |
|
9140 aRules.InsertElementAt(ruleX, &aRule); |
|
9141 return; |
|
9142 } |
|
9143 } |
|
9144 aRules.AppendElement(&aRule); |
|
9145 } |
|
9146 |
|
9147 void DR_State::ParseRulesFile() |
|
9148 { |
|
9149 char* path = PR_GetEnv("GECKO_DISPLAY_REFLOW_RULES_FILE"); |
|
9150 if (path) { |
|
9151 FILE* inFile = fopen(path, "r"); |
|
9152 if (inFile) { |
|
9153 for (DR_Rule* rule = ParseRule(inFile); rule; rule = ParseRule(inFile)) { |
|
9154 if (rule->mTarget) { |
|
9155 nsIAtom* fType = rule->mTarget->mFrameType; |
|
9156 if (fType) { |
|
9157 DR_FrameTypeInfo* info = GetFrameTypeInfo(fType); |
|
9158 if (info) { |
|
9159 AddRule(info->mRules, *rule); |
|
9160 } |
|
9161 } |
|
9162 else { |
|
9163 AddRule(mWildRules, *rule); |
|
9164 } |
|
9165 mActive = true; |
|
9166 } |
|
9167 } |
|
9168 } |
|
9169 } |
|
9170 } |
|
9171 |
|
9172 |
|
9173 void DR_State::AddFrameTypeInfo(nsIAtom* aFrameType, |
|
9174 const char* aFrameNameAbbrev, |
|
9175 const char* aFrameName) |
|
9176 { |
|
9177 mFrameTypeTable.AppendElement(DR_FrameTypeInfo(aFrameType, aFrameNameAbbrev, aFrameName)); |
|
9178 } |
|
9179 |
|
9180 DR_FrameTypeInfo* DR_State::GetFrameTypeInfo(nsIAtom* aFrameType) |
|
9181 { |
|
9182 int32_t numEntries = mFrameTypeTable.Length(); |
|
9183 NS_ASSERTION(numEntries != 0, "empty FrameTypeTable"); |
|
9184 for (int32_t i = 0; i < numEntries; i++) { |
|
9185 DR_FrameTypeInfo& info = mFrameTypeTable.ElementAt(i); |
|
9186 if (info.mType == aFrameType) { |
|
9187 return &info; |
|
9188 } |
|
9189 } |
|
9190 return &mFrameTypeTable.ElementAt(numEntries - 1); // return unknown frame type |
|
9191 } |
|
9192 |
|
9193 DR_FrameTypeInfo* DR_State::GetFrameTypeInfo(char* aFrameName) |
|
9194 { |
|
9195 int32_t numEntries = mFrameTypeTable.Length(); |
|
9196 NS_ASSERTION(numEntries != 0, "empty FrameTypeTable"); |
|
9197 for (int32_t i = 0; i < numEntries; i++) { |
|
9198 DR_FrameTypeInfo& info = mFrameTypeTable.ElementAt(i); |
|
9199 if ((strcmp(aFrameName, info.mName) == 0) || (strcmp(aFrameName, info.mNameAbbrev) == 0)) { |
|
9200 return &info; |
|
9201 } |
|
9202 } |
|
9203 return &mFrameTypeTable.ElementAt(numEntries - 1); // return unknown frame type |
|
9204 } |
|
9205 |
|
9206 void DR_State::InitFrameTypeTable() |
|
9207 { |
|
9208 AddFrameTypeInfo(nsGkAtoms::blockFrame, "block", "block"); |
|
9209 AddFrameTypeInfo(nsGkAtoms::brFrame, "br", "br"); |
|
9210 AddFrameTypeInfo(nsGkAtoms::bulletFrame, "bullet", "bullet"); |
|
9211 AddFrameTypeInfo(nsGkAtoms::colorControlFrame, "color", "colorControl"); |
|
9212 AddFrameTypeInfo(nsGkAtoms::gfxButtonControlFrame, "button", "gfxButtonControl"); |
|
9213 AddFrameTypeInfo(nsGkAtoms::HTMLButtonControlFrame, "HTMLbutton", "HTMLButtonControl"); |
|
9214 AddFrameTypeInfo(nsGkAtoms::HTMLCanvasFrame, "HTMLCanvas","HTMLCanvas"); |
|
9215 AddFrameTypeInfo(nsGkAtoms::subDocumentFrame, "subdoc", "subDocument"); |
|
9216 AddFrameTypeInfo(nsGkAtoms::imageFrame, "img", "image"); |
|
9217 AddFrameTypeInfo(nsGkAtoms::inlineFrame, "inline", "inline"); |
|
9218 AddFrameTypeInfo(nsGkAtoms::letterFrame, "letter", "letter"); |
|
9219 AddFrameTypeInfo(nsGkAtoms::lineFrame, "line", "line"); |
|
9220 AddFrameTypeInfo(nsGkAtoms::listControlFrame, "select", "select"); |
|
9221 AddFrameTypeInfo(nsGkAtoms::objectFrame, "obj", "object"); |
|
9222 AddFrameTypeInfo(nsGkAtoms::pageFrame, "page", "page"); |
|
9223 AddFrameTypeInfo(nsGkAtoms::placeholderFrame, "place", "placeholder"); |
|
9224 AddFrameTypeInfo(nsGkAtoms::canvasFrame, "canvas", "canvas"); |
|
9225 AddFrameTypeInfo(nsGkAtoms::rootFrame, "root", "root"); |
|
9226 AddFrameTypeInfo(nsGkAtoms::scrollFrame, "scroll", "scroll"); |
|
9227 AddFrameTypeInfo(nsGkAtoms::tableCaptionFrame, "caption", "tableCaption"); |
|
9228 AddFrameTypeInfo(nsGkAtoms::tableCellFrame, "cell", "tableCell"); |
|
9229 AddFrameTypeInfo(nsGkAtoms::bcTableCellFrame, "bcCell", "bcTableCell"); |
|
9230 AddFrameTypeInfo(nsGkAtoms::tableColFrame, "col", "tableCol"); |
|
9231 AddFrameTypeInfo(nsGkAtoms::tableColGroupFrame, "colG", "tableColGroup"); |
|
9232 AddFrameTypeInfo(nsGkAtoms::tableFrame, "tbl", "table"); |
|
9233 AddFrameTypeInfo(nsGkAtoms::tableOuterFrame, "tblO", "tableOuter"); |
|
9234 AddFrameTypeInfo(nsGkAtoms::tableRowGroupFrame, "rowG", "tableRowGroup"); |
|
9235 AddFrameTypeInfo(nsGkAtoms::tableRowFrame, "row", "tableRow"); |
|
9236 AddFrameTypeInfo(nsGkAtoms::textInputFrame, "textCtl", "textInput"); |
|
9237 AddFrameTypeInfo(nsGkAtoms::textFrame, "text", "text"); |
|
9238 AddFrameTypeInfo(nsGkAtoms::viewportFrame, "VP", "viewport"); |
|
9239 #ifdef MOZ_XUL |
|
9240 AddFrameTypeInfo(nsGkAtoms::XULLabelFrame, "XULLabel", "XULLabel"); |
|
9241 AddFrameTypeInfo(nsGkAtoms::boxFrame, "Box", "Box"); |
|
9242 AddFrameTypeInfo(nsGkAtoms::sliderFrame, "Slider", "Slider"); |
|
9243 AddFrameTypeInfo(nsGkAtoms::popupSetFrame, "PopupSet", "PopupSet"); |
|
9244 #endif |
|
9245 AddFrameTypeInfo(nullptr, "unknown", "unknown"); |
|
9246 } |
|
9247 |
|
9248 |
|
9249 void DR_State::DisplayFrameTypeInfo(nsIFrame* aFrame, |
|
9250 int32_t aIndent) |
|
9251 { |
|
9252 DR_FrameTypeInfo* frameTypeInfo = GetFrameTypeInfo(aFrame->GetType()); |
|
9253 if (frameTypeInfo) { |
|
9254 for (int32_t i = 0; i < aIndent; i++) { |
|
9255 printf(" "); |
|
9256 } |
|
9257 if(!strcmp(frameTypeInfo->mNameAbbrev, "unknown")) { |
|
9258 if (aFrame) { |
|
9259 nsAutoString name; |
|
9260 aFrame->GetFrameName(name); |
|
9261 printf("%s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)aFrame); |
|
9262 } |
|
9263 else { |
|
9264 printf("%s %p ", frameTypeInfo->mNameAbbrev, (void*)aFrame); |
|
9265 } |
|
9266 } |
|
9267 else { |
|
9268 printf("%s %p ", frameTypeInfo->mNameAbbrev, (void*)aFrame); |
|
9269 } |
|
9270 } |
|
9271 } |
|
9272 |
|
9273 bool DR_State::RuleMatches(DR_Rule& aRule, |
|
9274 DR_FrameTreeNode& aNode) |
|
9275 { |
|
9276 NS_ASSERTION(aRule.mTarget, "program error"); |
|
9277 |
|
9278 DR_RulePart* rulePart; |
|
9279 DR_FrameTreeNode* parentNode; |
|
9280 for (rulePart = aRule.mTarget->mNext, parentNode = aNode.mParent; |
|
9281 rulePart && parentNode; |
|
9282 rulePart = rulePart->mNext, parentNode = parentNode->mParent) { |
|
9283 if (rulePart->mFrameType) { |
|
9284 if (parentNode->mFrame) { |
|
9285 if (rulePart->mFrameType != parentNode->mFrame->GetType()) { |
|
9286 return false; |
|
9287 } |
|
9288 } |
|
9289 else NS_ASSERTION(false, "program error"); |
|
9290 } |
|
9291 // else wild card match |
|
9292 } |
|
9293 return true; |
|
9294 } |
|
9295 |
|
9296 void DR_State::FindMatchingRule(DR_FrameTreeNode& aNode) |
|
9297 { |
|
9298 if (!aNode.mFrame) { |
|
9299 NS_ASSERTION(false, "invalid DR_FrameTreeNode \n"); |
|
9300 return; |
|
9301 } |
|
9302 |
|
9303 bool matchingRule = false; |
|
9304 |
|
9305 DR_FrameTypeInfo* info = GetFrameTypeInfo(aNode.mFrame->GetType()); |
|
9306 NS_ASSERTION(info, "program error"); |
|
9307 int32_t numRules = info->mRules.Length(); |
|
9308 for (int32_t ruleX = 0; ruleX < numRules; ruleX++) { |
|
9309 DR_Rule* rule = info->mRules.ElementAt(ruleX); |
|
9310 if (rule && RuleMatches(*rule, aNode)) { |
|
9311 aNode.mDisplay = rule->mDisplay; |
|
9312 matchingRule = true; |
|
9313 break; |
|
9314 } |
|
9315 } |
|
9316 if (!matchingRule) { |
|
9317 int32_t numWildRules = mWildRules.Length(); |
|
9318 for (int32_t ruleX = 0; ruleX < numWildRules; ruleX++) { |
|
9319 DR_Rule* rule = mWildRules.ElementAt(ruleX); |
|
9320 if (rule && RuleMatches(*rule, aNode)) { |
|
9321 aNode.mDisplay = rule->mDisplay; |
|
9322 break; |
|
9323 } |
|
9324 } |
|
9325 } |
|
9326 } |
|
9327 |
|
9328 DR_FrameTreeNode* DR_State::CreateTreeNode(nsIFrame* aFrame, |
|
9329 const nsHTMLReflowState* aReflowState) |
|
9330 { |
|
9331 // find the frame of the parent reflow state (usually just the parent of aFrame) |
|
9332 nsIFrame* parentFrame; |
|
9333 if (aReflowState) { |
|
9334 const nsHTMLReflowState* parentRS = aReflowState->parentReflowState; |
|
9335 parentFrame = (parentRS) ? parentRS->frame : nullptr; |
|
9336 } else { |
|
9337 parentFrame = aFrame->GetParent(); |
|
9338 } |
|
9339 |
|
9340 // find the parent tree node leaf |
|
9341 DR_FrameTreeNode* parentNode = nullptr; |
|
9342 |
|
9343 DR_FrameTreeNode* lastLeaf = nullptr; |
|
9344 if(mFrameTreeLeaves.Length()) |
|
9345 lastLeaf = mFrameTreeLeaves.ElementAt(mFrameTreeLeaves.Length() - 1); |
|
9346 if (lastLeaf) { |
|
9347 for (parentNode = lastLeaf; parentNode && (parentNode->mFrame != parentFrame); parentNode = parentNode->mParent) { |
|
9348 } |
|
9349 } |
|
9350 DR_FrameTreeNode* newNode = new DR_FrameTreeNode(aFrame, parentNode); |
|
9351 FindMatchingRule(*newNode); |
|
9352 |
|
9353 newNode->mIndent = mIndent; |
|
9354 if (newNode->mDisplay || mIndentUndisplayedFrames) { |
|
9355 ++mIndent; |
|
9356 } |
|
9357 |
|
9358 if (lastLeaf && (lastLeaf == parentNode)) { |
|
9359 mFrameTreeLeaves.RemoveElementAt(mFrameTreeLeaves.Length() - 1); |
|
9360 } |
|
9361 mFrameTreeLeaves.AppendElement(newNode); |
|
9362 mCount++; |
|
9363 |
|
9364 return newNode; |
|
9365 } |
|
9366 |
|
9367 void DR_State::PrettyUC(nscoord aSize, |
|
9368 char* aBuf) |
|
9369 { |
|
9370 if (NS_UNCONSTRAINEDSIZE == aSize) { |
|
9371 strcpy(aBuf, "UC"); |
|
9372 } |
|
9373 else { |
|
9374 if ((nscoord)0xdeadbeefU == aSize) |
|
9375 { |
|
9376 strcpy(aBuf, "deadbeef"); |
|
9377 } |
|
9378 else { |
|
9379 sprintf(aBuf, "%d", aSize); |
|
9380 } |
|
9381 } |
|
9382 } |
|
9383 |
|
9384 void DR_State::PrintMargin(const char *tag, const nsMargin* aMargin) |
|
9385 { |
|
9386 if (aMargin) { |
|
9387 char t[16], r[16], b[16], l[16]; |
|
9388 PrettyUC(aMargin->top, t); |
|
9389 PrettyUC(aMargin->right, r); |
|
9390 PrettyUC(aMargin->bottom, b); |
|
9391 PrettyUC(aMargin->left, l); |
|
9392 printf(" %s=%s,%s,%s,%s", tag, t, r, b, l); |
|
9393 } else { |
|
9394 // use %p here for consistency with other null-pointer printouts |
|
9395 printf(" %s=%p", tag, (void*)aMargin); |
|
9396 } |
|
9397 } |
|
9398 |
|
9399 void DR_State::DeleteTreeNode(DR_FrameTreeNode& aNode) |
|
9400 { |
|
9401 mFrameTreeLeaves.RemoveElement(&aNode); |
|
9402 int32_t numLeaves = mFrameTreeLeaves.Length(); |
|
9403 if ((0 == numLeaves) || (aNode.mParent != mFrameTreeLeaves.ElementAt(numLeaves - 1))) { |
|
9404 mFrameTreeLeaves.AppendElement(aNode.mParent); |
|
9405 } |
|
9406 |
|
9407 if (aNode.mDisplay || mIndentUndisplayedFrames) { |
|
9408 --mIndent; |
|
9409 } |
|
9410 // delete the tree node |
|
9411 delete &aNode; |
|
9412 } |
|
9413 |
|
9414 static void |
|
9415 CheckPixelError(nscoord aSize, |
|
9416 int32_t aPixelToTwips) |
|
9417 { |
|
9418 if (NS_UNCONSTRAINEDSIZE != aSize) { |
|
9419 if ((aSize % aPixelToTwips) > 0) { |
|
9420 printf("VALUE %d is not a whole pixel \n", aSize); |
|
9421 } |
|
9422 } |
|
9423 } |
|
9424 |
|
9425 static void DisplayReflowEnterPrint(nsPresContext* aPresContext, |
|
9426 nsIFrame* aFrame, |
|
9427 const nsHTMLReflowState& aReflowState, |
|
9428 DR_FrameTreeNode& aTreeNode, |
|
9429 bool aChanged) |
|
9430 { |
|
9431 if (aTreeNode.mDisplay) { |
|
9432 DR_state->DisplayFrameTypeInfo(aFrame, aTreeNode.mIndent); |
|
9433 |
|
9434 char width[16]; |
|
9435 char height[16]; |
|
9436 |
|
9437 DR_state->PrettyUC(aReflowState.AvailableWidth(), width); |
|
9438 DR_state->PrettyUC(aReflowState.AvailableHeight(), height); |
|
9439 printf("Reflow a=%s,%s ", width, height); |
|
9440 |
|
9441 DR_state->PrettyUC(aReflowState.ComputedWidth(), width); |
|
9442 DR_state->PrettyUC(aReflowState.ComputedHeight(), height); |
|
9443 printf("c=%s,%s ", width, height); |
|
9444 |
|
9445 if (aFrame->GetStateBits() & NS_FRAME_IS_DIRTY) |
|
9446 printf("dirty "); |
|
9447 |
|
9448 if (aFrame->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN) |
|
9449 printf("dirty-children "); |
|
9450 |
|
9451 if (aReflowState.mFlags.mSpecialHeightReflow) |
|
9452 printf("special-height "); |
|
9453 |
|
9454 if (aReflowState.mFlags.mHResize) |
|
9455 printf("h-resize "); |
|
9456 |
|
9457 if (aReflowState.mFlags.mVResize) |
|
9458 printf("v-resize "); |
|
9459 |
|
9460 nsIFrame* inFlow = aFrame->GetPrevInFlow(); |
|
9461 if (inFlow) { |
|
9462 printf("pif=%p ", (void*)inFlow); |
|
9463 } |
|
9464 inFlow = aFrame->GetNextInFlow(); |
|
9465 if (inFlow) { |
|
9466 printf("nif=%p ", (void*)inFlow); |
|
9467 } |
|
9468 if (aChanged) |
|
9469 printf("CHANGED \n"); |
|
9470 else |
|
9471 printf("cnt=%d \n", DR_state->mCount); |
|
9472 if (DR_state->mDisplayPixelErrors) { |
|
9473 int32_t p2t = aPresContext->AppUnitsPerDevPixel(); |
|
9474 CheckPixelError(aReflowState.AvailableWidth(), p2t); |
|
9475 CheckPixelError(aReflowState.AvailableHeight(), p2t); |
|
9476 CheckPixelError(aReflowState.ComputedWidth(), p2t); |
|
9477 CheckPixelError(aReflowState.ComputedHeight(), p2t); |
|
9478 } |
|
9479 } |
|
9480 } |
|
9481 |
|
9482 void* nsFrame::DisplayReflowEnter(nsPresContext* aPresContext, |
|
9483 nsIFrame* aFrame, |
|
9484 const nsHTMLReflowState& aReflowState) |
|
9485 { |
|
9486 if (!DR_state->mInited) DR_state->Init(); |
|
9487 if (!DR_state->mActive) return nullptr; |
|
9488 |
|
9489 NS_ASSERTION(aFrame, "invalid call"); |
|
9490 |
|
9491 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, &aReflowState); |
|
9492 if (treeNode) { |
|
9493 DisplayReflowEnterPrint(aPresContext, aFrame, aReflowState, *treeNode, false); |
|
9494 } |
|
9495 return treeNode; |
|
9496 } |
|
9497 |
|
9498 void* nsFrame::DisplayLayoutEnter(nsIFrame* aFrame) |
|
9499 { |
|
9500 if (!DR_state->mInited) DR_state->Init(); |
|
9501 if (!DR_state->mActive) return nullptr; |
|
9502 |
|
9503 NS_ASSERTION(aFrame, "invalid call"); |
|
9504 |
|
9505 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr); |
|
9506 if (treeNode && treeNode->mDisplay) { |
|
9507 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
|
9508 printf("Layout\n"); |
|
9509 } |
|
9510 return treeNode; |
|
9511 } |
|
9512 |
|
9513 void* nsFrame::DisplayIntrinsicWidthEnter(nsIFrame* aFrame, |
|
9514 const char* aType) |
|
9515 { |
|
9516 if (!DR_state->mInited) DR_state->Init(); |
|
9517 if (!DR_state->mActive) return nullptr; |
|
9518 |
|
9519 NS_ASSERTION(aFrame, "invalid call"); |
|
9520 |
|
9521 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr); |
|
9522 if (treeNode && treeNode->mDisplay) { |
|
9523 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
|
9524 printf("Get%sWidth\n", aType); |
|
9525 } |
|
9526 return treeNode; |
|
9527 } |
|
9528 |
|
9529 void* nsFrame::DisplayIntrinsicSizeEnter(nsIFrame* aFrame, |
|
9530 const char* aType) |
|
9531 { |
|
9532 if (!DR_state->mInited) DR_state->Init(); |
|
9533 if (!DR_state->mActive) return nullptr; |
|
9534 |
|
9535 NS_ASSERTION(aFrame, "invalid call"); |
|
9536 |
|
9537 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr); |
|
9538 if (treeNode && treeNode->mDisplay) { |
|
9539 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
|
9540 printf("Get%sSize\n", aType); |
|
9541 } |
|
9542 return treeNode; |
|
9543 } |
|
9544 |
|
9545 void nsFrame::DisplayReflowExit(nsPresContext* aPresContext, |
|
9546 nsIFrame* aFrame, |
|
9547 nsHTMLReflowMetrics& aMetrics, |
|
9548 nsReflowStatus aStatus, |
|
9549 void* aFrameTreeNode) |
|
9550 { |
|
9551 if (!DR_state->mActive) return; |
|
9552 |
|
9553 NS_ASSERTION(aFrame, "DisplayReflowExit - invalid call"); |
|
9554 if (!aFrameTreeNode) return; |
|
9555 |
|
9556 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode; |
|
9557 if (treeNode->mDisplay) { |
|
9558 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
|
9559 |
|
9560 char width[16]; |
|
9561 char height[16]; |
|
9562 char x[16]; |
|
9563 char y[16]; |
|
9564 DR_state->PrettyUC(aMetrics.Width(), width); |
|
9565 DR_state->PrettyUC(aMetrics.Height(), height); |
|
9566 printf("Reflow d=%s,%s", width, height); |
|
9567 |
|
9568 if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) { |
|
9569 printf(" status=0x%x", aStatus); |
|
9570 } |
|
9571 if (aFrame->HasOverflowAreas()) { |
|
9572 DR_state->PrettyUC(aMetrics.VisualOverflow().x, x); |
|
9573 DR_state->PrettyUC(aMetrics.VisualOverflow().y, y); |
|
9574 DR_state->PrettyUC(aMetrics.VisualOverflow().width, width); |
|
9575 DR_state->PrettyUC(aMetrics.VisualOverflow().height, height); |
|
9576 printf(" vis-o=(%s,%s) %s x %s", x, y, width, height); |
|
9577 |
|
9578 nsRect storedOverflow = aFrame->GetVisualOverflowRect(); |
|
9579 DR_state->PrettyUC(storedOverflow.x, x); |
|
9580 DR_state->PrettyUC(storedOverflow.y, y); |
|
9581 DR_state->PrettyUC(storedOverflow.width, width); |
|
9582 DR_state->PrettyUC(storedOverflow.height, height); |
|
9583 printf(" vis-sto=(%s,%s) %s x %s", x, y, width, height); |
|
9584 |
|
9585 DR_state->PrettyUC(aMetrics.ScrollableOverflow().x, x); |
|
9586 DR_state->PrettyUC(aMetrics.ScrollableOverflow().y, y); |
|
9587 DR_state->PrettyUC(aMetrics.ScrollableOverflow().width, width); |
|
9588 DR_state->PrettyUC(aMetrics.ScrollableOverflow().height, height); |
|
9589 printf(" scr-o=(%s,%s) %s x %s", x, y, width, height); |
|
9590 |
|
9591 storedOverflow = aFrame->GetScrollableOverflowRect(); |
|
9592 DR_state->PrettyUC(storedOverflow.x, x); |
|
9593 DR_state->PrettyUC(storedOverflow.y, y); |
|
9594 DR_state->PrettyUC(storedOverflow.width, width); |
|
9595 DR_state->PrettyUC(storedOverflow.height, height); |
|
9596 printf(" scr-sto=(%s,%s) %s x %s", x, y, width, height); |
|
9597 } |
|
9598 printf("\n"); |
|
9599 if (DR_state->mDisplayPixelErrors) { |
|
9600 int32_t p2t = aPresContext->AppUnitsPerDevPixel(); |
|
9601 CheckPixelError(aMetrics.Width(), p2t); |
|
9602 CheckPixelError(aMetrics.Height(), p2t); |
|
9603 } |
|
9604 } |
|
9605 DR_state->DeleteTreeNode(*treeNode); |
|
9606 } |
|
9607 |
|
9608 void nsFrame::DisplayLayoutExit(nsIFrame* aFrame, |
|
9609 void* aFrameTreeNode) |
|
9610 { |
|
9611 if (!DR_state->mActive) return; |
|
9612 |
|
9613 NS_ASSERTION(aFrame, "non-null frame required"); |
|
9614 if (!aFrameTreeNode) return; |
|
9615 |
|
9616 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode; |
|
9617 if (treeNode->mDisplay) { |
|
9618 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
|
9619 nsRect rect = aFrame->GetRect(); |
|
9620 printf("Layout=%d,%d,%d,%d\n", rect.x, rect.y, rect.width, rect.height); |
|
9621 } |
|
9622 DR_state->DeleteTreeNode(*treeNode); |
|
9623 } |
|
9624 |
|
9625 void nsFrame::DisplayIntrinsicWidthExit(nsIFrame* aFrame, |
|
9626 const char* aType, |
|
9627 nscoord aResult, |
|
9628 void* aFrameTreeNode) |
|
9629 { |
|
9630 if (!DR_state->mActive) return; |
|
9631 |
|
9632 NS_ASSERTION(aFrame, "non-null frame required"); |
|
9633 if (!aFrameTreeNode) return; |
|
9634 |
|
9635 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode; |
|
9636 if (treeNode->mDisplay) { |
|
9637 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
|
9638 char width[16]; |
|
9639 DR_state->PrettyUC(aResult, width); |
|
9640 printf("Get%sWidth=%s\n", aType, width); |
|
9641 } |
|
9642 DR_state->DeleteTreeNode(*treeNode); |
|
9643 } |
|
9644 |
|
9645 void nsFrame::DisplayIntrinsicSizeExit(nsIFrame* aFrame, |
|
9646 const char* aType, |
|
9647 nsSize aResult, |
|
9648 void* aFrameTreeNode) |
|
9649 { |
|
9650 if (!DR_state->mActive) return; |
|
9651 |
|
9652 NS_ASSERTION(aFrame, "non-null frame required"); |
|
9653 if (!aFrameTreeNode) return; |
|
9654 |
|
9655 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode; |
|
9656 if (treeNode->mDisplay) { |
|
9657 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
|
9658 |
|
9659 char width[16]; |
|
9660 char height[16]; |
|
9661 DR_state->PrettyUC(aResult.width, width); |
|
9662 DR_state->PrettyUC(aResult.height, height); |
|
9663 printf("Get%sSize=%s,%s\n", aType, width, height); |
|
9664 } |
|
9665 DR_state->DeleteTreeNode(*treeNode); |
|
9666 } |
|
9667 |
|
9668 /* static */ void |
|
9669 nsFrame::DisplayReflowStartup() |
|
9670 { |
|
9671 DR_state = new DR_State(); |
|
9672 } |
|
9673 |
|
9674 /* static */ void |
|
9675 nsFrame::DisplayReflowShutdown() |
|
9676 { |
|
9677 delete DR_state; |
|
9678 DR_state = nullptr; |
|
9679 } |
|
9680 |
|
9681 void DR_cookie::Change() const |
|
9682 { |
|
9683 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)mValue; |
|
9684 if (treeNode && treeNode->mDisplay) { |
|
9685 DisplayReflowEnterPrint(mPresContext, mFrame, mReflowState, *treeNode, true); |
|
9686 } |
|
9687 } |
|
9688 |
|
9689 /* static */ void* |
|
9690 nsHTMLReflowState::DisplayInitConstraintsEnter(nsIFrame* aFrame, |
|
9691 nsHTMLReflowState* aState, |
|
9692 nscoord aContainingBlockWidth, |
|
9693 nscoord aContainingBlockHeight, |
|
9694 const nsMargin* aBorder, |
|
9695 const nsMargin* aPadding) |
|
9696 { |
|
9697 NS_PRECONDITION(aFrame, "non-null frame required"); |
|
9698 NS_PRECONDITION(aState, "non-null state required"); |
|
9699 |
|
9700 if (!DR_state->mInited) DR_state->Init(); |
|
9701 if (!DR_state->mActive) return nullptr; |
|
9702 |
|
9703 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, aState); |
|
9704 if (treeNode && treeNode->mDisplay) { |
|
9705 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
|
9706 |
|
9707 printf("InitConstraints parent=%p", |
|
9708 (void*)aState->parentReflowState); |
|
9709 |
|
9710 char width[16]; |
|
9711 char height[16]; |
|
9712 |
|
9713 DR_state->PrettyUC(aContainingBlockWidth, width); |
|
9714 DR_state->PrettyUC(aContainingBlockHeight, height); |
|
9715 printf(" cb=%s,%s", width, height); |
|
9716 |
|
9717 DR_state->PrettyUC(aState->AvailableWidth(), width); |
|
9718 DR_state->PrettyUC(aState->AvailableHeight(), height); |
|
9719 printf(" as=%s,%s", width, height); |
|
9720 |
|
9721 DR_state->PrintMargin("b", aBorder); |
|
9722 DR_state->PrintMargin("p", aPadding); |
|
9723 putchar('\n'); |
|
9724 } |
|
9725 return treeNode; |
|
9726 } |
|
9727 |
|
9728 /* static */ void |
|
9729 nsHTMLReflowState::DisplayInitConstraintsExit(nsIFrame* aFrame, |
|
9730 nsHTMLReflowState* aState, |
|
9731 void* aValue) |
|
9732 { |
|
9733 NS_PRECONDITION(aFrame, "non-null frame required"); |
|
9734 NS_PRECONDITION(aState, "non-null state required"); |
|
9735 |
|
9736 if (!DR_state->mActive) return; |
|
9737 if (!aValue) return; |
|
9738 |
|
9739 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue; |
|
9740 if (treeNode->mDisplay) { |
|
9741 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
|
9742 char cmiw[16], cw[16], cmxw[16], cmih[16], ch[16], cmxh[16]; |
|
9743 DR_state->PrettyUC(aState->ComputedMinWidth(), cmiw); |
|
9744 DR_state->PrettyUC(aState->ComputedWidth(), cw); |
|
9745 DR_state->PrettyUC(aState->ComputedMaxWidth(), cmxw); |
|
9746 DR_state->PrettyUC(aState->ComputedMinHeight(), cmih); |
|
9747 DR_state->PrettyUC(aState->ComputedHeight(), ch); |
|
9748 DR_state->PrettyUC(aState->ComputedMaxHeight(), cmxh); |
|
9749 printf("InitConstraints= cw=(%s <= %s <= %s) ch=(%s <= %s <= %s)", |
|
9750 cmiw, cw, cmxw, cmih, ch, cmxh); |
|
9751 DR_state->PrintMargin("co", &aState->ComputedPhysicalOffsets()); |
|
9752 putchar('\n'); |
|
9753 } |
|
9754 DR_state->DeleteTreeNode(*treeNode); |
|
9755 } |
|
9756 |
|
9757 |
|
9758 /* static */ void* |
|
9759 nsCSSOffsetState::DisplayInitOffsetsEnter(nsIFrame* aFrame, |
|
9760 nsCSSOffsetState* aState, |
|
9761 nscoord aHorizontalPercentBasis, |
|
9762 nscoord aVerticalPercentBasis, |
|
9763 const nsMargin* aBorder, |
|
9764 const nsMargin* aPadding) |
|
9765 { |
|
9766 NS_PRECONDITION(aFrame, "non-null frame required"); |
|
9767 NS_PRECONDITION(aState, "non-null state required"); |
|
9768 |
|
9769 if (!DR_state->mInited) DR_state->Init(); |
|
9770 if (!DR_state->mActive) return nullptr; |
|
9771 |
|
9772 // aState is not necessarily a nsHTMLReflowState |
|
9773 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr); |
|
9774 if (treeNode && treeNode->mDisplay) { |
|
9775 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
|
9776 |
|
9777 char horizPctBasisStr[16]; |
|
9778 char vertPctBasisStr[16]; |
|
9779 DR_state->PrettyUC(aHorizontalPercentBasis, horizPctBasisStr); |
|
9780 DR_state->PrettyUC(aVerticalPercentBasis, vertPctBasisStr); |
|
9781 printf("InitOffsets pct_basis=%s,%s", horizPctBasisStr, vertPctBasisStr); |
|
9782 |
|
9783 DR_state->PrintMargin("b", aBorder); |
|
9784 DR_state->PrintMargin("p", aPadding); |
|
9785 putchar('\n'); |
|
9786 } |
|
9787 return treeNode; |
|
9788 } |
|
9789 |
|
9790 /* static */ void |
|
9791 nsCSSOffsetState::DisplayInitOffsetsExit(nsIFrame* aFrame, |
|
9792 nsCSSOffsetState* aState, |
|
9793 void* aValue) |
|
9794 { |
|
9795 NS_PRECONDITION(aFrame, "non-null frame required"); |
|
9796 NS_PRECONDITION(aState, "non-null state required"); |
|
9797 |
|
9798 if (!DR_state->mActive) return; |
|
9799 if (!aValue) return; |
|
9800 |
|
9801 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue; |
|
9802 if (treeNode->mDisplay) { |
|
9803 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
|
9804 printf("InitOffsets="); |
|
9805 DR_state->PrintMargin("m", &aState->ComputedPhysicalMargin()); |
|
9806 DR_state->PrintMargin("p", &aState->ComputedPhysicalPadding()); |
|
9807 DR_state->PrintMargin("p+b", &aState->ComputedPhysicalBorderPadding()); |
|
9808 putchar('\n'); |
|
9809 } |
|
9810 DR_state->DeleteTreeNode(*treeNode); |
|
9811 } |
|
9812 |
|
9813 /* static */ void* |
|
9814 nsHTMLReflowState::DisplayInitFrameTypeEnter(nsIFrame* aFrame, |
|
9815 nsHTMLReflowState* aState) |
|
9816 { |
|
9817 NS_PRECONDITION(aFrame, "non-null frame required"); |
|
9818 NS_PRECONDITION(aState, "non-null state required"); |
|
9819 |
|
9820 if (!DR_state->mInited) DR_state->Init(); |
|
9821 if (!DR_state->mActive) return nullptr; |
|
9822 |
|
9823 // we don't print anything here |
|
9824 return DR_state->CreateTreeNode(aFrame, aState); |
|
9825 } |
|
9826 |
|
9827 /* static */ void |
|
9828 nsHTMLReflowState::DisplayInitFrameTypeExit(nsIFrame* aFrame, |
|
9829 nsHTMLReflowState* aState, |
|
9830 void* aValue) |
|
9831 { |
|
9832 NS_PRECONDITION(aFrame, "non-null frame required"); |
|
9833 NS_PRECONDITION(aState, "non-null state required"); |
|
9834 |
|
9835 if (!DR_state->mActive) return; |
|
9836 if (!aValue) return; |
|
9837 |
|
9838 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue; |
|
9839 if (treeNode->mDisplay) { |
|
9840 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
|
9841 printf("InitFrameType"); |
|
9842 |
|
9843 const nsStyleDisplay *disp = aState->mStyleDisplay; |
|
9844 |
|
9845 if (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) |
|
9846 printf(" out-of-flow"); |
|
9847 if (aFrame->GetPrevInFlow()) |
|
9848 printf(" prev-in-flow"); |
|
9849 if (aFrame->IsAbsolutelyPositioned()) |
|
9850 printf(" abspos"); |
|
9851 if (aFrame->IsFloating()) |
|
9852 printf(" float"); |
|
9853 |
|
9854 // This array must exactly match the NS_STYLE_DISPLAY constants. |
|
9855 const char *const displayTypes[] = { |
|
9856 "none", "block", "inline", "inline-block", "list-item", "marker", |
|
9857 "run-in", "compact", "table", "inline-table", "table-row-group", |
|
9858 "table-column", "table-column-group", "table-header-group", |
|
9859 "table-footer-group", "table-row", "table-cell", "table-caption", |
|
9860 "box", "inline-box", |
|
9861 #ifdef MOZ_XUL |
|
9862 "grid", "inline-grid", "grid-group", "grid-line", "stack", |
|
9863 "inline-stack", "deck", "popup", "groupbox", |
|
9864 #endif |
|
9865 }; |
|
9866 if (disp->mDisplay >= ArrayLength(displayTypes)) |
|
9867 printf(" display=%u", disp->mDisplay); |
|
9868 else |
|
9869 printf(" display=%s", displayTypes[disp->mDisplay]); |
|
9870 |
|
9871 // This array must exactly match the NS_CSS_FRAME_TYPE constants. |
|
9872 const char *const cssFrameTypes[] = { |
|
9873 "unknown", "inline", "block", "floating", "absolute", "internal-table" |
|
9874 }; |
|
9875 nsCSSFrameType bareType = NS_FRAME_GET_TYPE(aState->mFrameType); |
|
9876 bool repNoBlock = NS_FRAME_IS_REPLACED_NOBLOCK(aState->mFrameType); |
|
9877 bool repBlock = NS_FRAME_IS_REPLACED_CONTAINS_BLOCK(aState->mFrameType); |
|
9878 |
|
9879 if (bareType >= ArrayLength(cssFrameTypes)) { |
|
9880 printf(" result=type %u", bareType); |
|
9881 } else { |
|
9882 printf(" result=%s", cssFrameTypes[bareType]); |
|
9883 } |
|
9884 printf("%s%s\n", repNoBlock ? " +rep" : "", repBlock ? " +repBlk" : ""); |
|
9885 } |
|
9886 DR_state->DeleteTreeNode(*treeNode); |
|
9887 } |
|
9888 |
|
9889 #endif |
|
9890 // End Display Reflow |
|
9891 |
|
9892 #endif |